使用gRPC的stream向客户端实时传送信息的坑
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用gRPC的stream向客户端实时传送信息的坑相关的知识,希望对你有一定的参考价值。
参考技术A 最近项目使用gRPC来实现PC端和手机的双向通讯, 我要把PC端的状态持续的发送给手机端 但是遇到一个问题在client端不能及时收到server端发送的stream的信息, 往往要client主动write后, client端才能及时收到相关的信息。
后来查了一下, 找到了类似的问题
https://stackoverflow.com/questions/58299740/how-can-i-receive-data-on-client-side-before-calling-end-on-the-server-side-f
但是这个地方并没有给出解决方案,
去查文档
https://nodejs.org/api/stream.html#stream_class_stream_writable
终于找到了解决方案:
当然, 我后来发现, 如果我不调用cork, 只要在nextTick中调用就可以实现flush的功能
以上来纪念我4月15日的半天光影。
gRPC和gRPC-Gateway的使用以及遇到的坑
原创不易,未经允许,请勿转载。
文章目录
系统:windows10
go版本:1.13.15
一、gRPC的使用
1.1 gPRC和Protobuf的安装
go get github.com/golang/protobuf/proto
go get google.golang.org/grpc
go get github.com/golang/protobuf/protoc-gen-go
装好上面三个之后,还需要安装一个protoc
,可以到https://github.com/protocolbuffers/protobuf/releases/tag/v3.9.0下载编译好的可执行文件,然后把压缩包中bin
目录下的protoc.exe
放到GOPATH/bin
目录下。
压缩包里面的include目录也放到GOPATH/bin目录下,之后用得到
现在,可以在GOPATH/bin
目录下,看到protoc-gen-go.exe
和protoc.exe
这两个可执行文件
1.2 编写proto文件
在编写文件之间,先创建一个go项目,命名为grpc_and_grpc-gateway_demo
,以grpc_and_grpc-gateway_demo
为根目录,创建一个文件名为proto/helloworld.proto
protobuf
语法可以参考https://www.jianshu.com/p/4443c28d4bf7
syntax = "proto3";
package helloworld; // 指定生成后go的package名
option go_package = "helloworld/"; // 生成的目录名,末尾的/不能省略,否则会出错。
message request
string name = 1;
message response
string res = 1;
service HelloWorld
rpc Call(request) returns(response)
编写好proto文件后,打开命令行窗口,进入proto目录下
cd proto
使用protoc
编译helloworld.proto
文件,生成对应的go
文件
protoc --go_out=plugins=grpc:. ./helloworld.proto
--go_out
表示生成go文件。grpc:.
冒号后面表示生成文件保存的位置,这个路径要已存在。.
表示当前目录。helloworld.proto
表示要编译的文件名
执行好上面的命令后,如果没出错的话,在proto
目录下会生成一个helloworld/helloworld.pb.go
文件
其中helloworld
为文件夹名,就算go_package
指定的目录名。
helloworld.pb.go
文件包含按照helloworld.proto
文件描述的,服务端接口HelloWorldServer
,客户端接口以及实现HelloWorldClient
,以及Request
、Response
结构体
1.3 编写服务端接口程序
以grpc_and_grpc-gateway_demo
为根目录,创建server/main.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
pb "grpc_and_grpc-gateway_demo/proto/helloworld"
"net"
)
type helloWorldServer struct
func (this helloWorldServer) Call(ctx context.Context, request *pb.Request) (*pb.Response, error)
resp := new(pb.Response)
resp.Res = "Hello " + request.Name
return resp, nil
func main()
listen, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil
fmt.Println(err)
return
defer listen.Close()
s := grpc.NewServer()
pb.RegisterHelloWorldServer(s,helloWorldServer)
fmt.Println("Listen on 127.0.0.1:8080")
s.Serve(listen)
运行该程序
go run main.go
1.4 编写客户端程序
以grpc_and_grpc-gateway_demo
为根目录,创建client/main.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
pb "grpc_and_grpc-gateway_demo/proto/helloworld"
)
func main()
conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure())
if err != nil
fmt.Println(err)
defer conn.Close()
c := pb.NewHelloWorldClient(conn)
req := &pb.RequestName: "jiang"
res, err := c.Call(context.Background(), req)
if err != nil
fmt.Println(err)
fmt.Println(res.Res)
运行该程序
go run main.go
最后打印
Hello jiang
二、gRPC-Gateway的使用
通过protobuf的自定义option实现了一个网关,服务端同时开启gRPC和HTTP服务,HTTP服务接收客户端请求后转换为grpc请求数据,获取响应后转为json数据返回给客户端。
2.1 安装grpc-gateway
安装推荐去grpc-gateway
的GitHub仓库,看最新的文档进行,安装,
go get github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-grpc-gateway
一些博客教程里面写的安装命令是下面这种
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
但是用这个命令安装的话,在后面执行命令的时候,会出现一些错误,可能会打印出一长串东西,结尾大概如下
--grpc-gateway_out: 11:1: expected 'IDENT', found 'import'
2.2 编写proto文件
以grpc_and_grpc-gateway_demo
为根目录,创建一个文件名为proto/helloworld_http.proto
syntax = "proto3";
package helloworld_http; // 指定生成后go的package名
option go_package = "helloworld_http/"; // 生成的目录名,末尾的/不能省略,否则会出错。
import "google/api/annotations.proto";
message request
string name = 1;
message response
string res = 1;
service HelloWorldHTTP
rpc Call(request) returns(response)
option (google.api.http) =
post : "/hello/world/call"
body : "*"
;
在编译前,还需要把google/api/annotations.proto
等文件复制到proto
目录下。
如果你本地下载了github.com\\grpc-ecosystem\\grpc-gateway
的1.0+
版本的话,进入该项目目录,找到third_party\\googleapis
项目中的这个目录,然后把里面的google
文件夹整个复制到proto
目录下即可。
如果没有下载1.0+
版本的话,可以到这个GitHub上去拷贝https://github.com/grpc-ecosystem/grpc-gateway/tree/v1/third_party/googleapis
protoc --go_out=plugins=grpc:. .\\helloworld_http.proto
protoc --grpc-gateway_out=logtostderr=true:. .\\helloworld_http.proto
两个命令执行完后,没出错误的话,会在proto
目录下生成两个文件:helloworld_http/helloworld_http.pb.go
、helloworld_http/helloworld_http.pb.gw.go
2.3 编写服务端代码
以grpc_and_grpc-gateway_demo
为根目录,创建server_http/server.go
package main
import (
"context"
"fmt"
"google.golang.org/grpc"
pb "grpc_and_grpc-gateway_demo/proto/helloworld_http" // 编译生成的包
"net"
)
type helloWorldServer struct
func (this helloWorldServer) Call(ctx context.Context, request *pb.Request) (*pb.Response, error)
resp := new(pb.Response)
resp.Res = "Hello " + request.Name
return resp, nil
func main()
listen, err := net.Listen("tcp", "127.0.0.1:8080")
if err != nil
fmt.Println(err)
return
defer listen.Close()
s := grpc.NewServer()
pb.RegisterHelloWorldHTTPServer(s,helloWorldServer)
fmt.Println("Listen on 127.0.0.1:8080")
s.Serve(listen)
运行代码
go run server.go
2.4 编写http转发代码
以grpc_and_grpc-gateway_demo
为根目录,创建server_http/main.go
package main
import (
"context"
"fmt"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"google.golang.org/grpc"
"grpc_and_grpc-gateway_demo/proto/helloworld_http"
"net/http"
)
func main()
ctx := context.Background()
ctx, cancel := context.WithCancel(ctx)
defer cancel()
// grpc服务地址
endpoint := "127.0.0.1:8080"
mux := runtime.NewServeMux()
opts := []grpc.DialOptiongrpc.WithInsecure()
// HTTP转grpc
err := helloworld_http.RegisterHelloWorldHTTPHandlerFromEndpoint(ctx, mux, endpoint, opts)
if err != nil
fmt.Println("Register handler err:%v\\n", err)
// http监听的端口
fmt.Println("HTTP Listen on 8088")
http.ListenAndServe(":8088", mux)
运行代码
go run main.go
2.5 测试
打开postmain或者其他接口测试工具,往以下地址发送请求,请求方法为POST
http://localhost:8088/hello/world/call
请求体为一个json数据
"name":"jiang"
请求方法为上面proto文件中option定义的,请求地址/hello/world/call
也一样是上面定义好的。
json数据name为request的内容。
三、可能遇到的坑
这个坑在上面提到过一次了,在这里再说一下,因为这个坑, 浪费博主一两个小时的时间。
一开始安装protoc-gen-grpc-gateway
的时候,我也是用下面的命令安装的
go get -u github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway
结果在编译这个命令的时候,不能正常生成文件,反而打印了一堆奇奇怪怪的东西。
protoc --grpc-gateway_out=logtostderr=true:. .\\helloworld_http.proto
生成如下乱码。
E1207 15:44:17.874478 15920 generator.go:108] 11:1: expected 'IDENT', found 'import':
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: hello.proto
/*
Package is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
.....
.....
--grpc-gateway_out: 11:1: expected 'IDENT', found 'import'
后来看了github上的文档才解决的。
拒绝白嫖从一键三连开始!
原创不易,未经允许,请勿转载。
以上是关于使用gRPC的stream向客户端实时传送信息的坑的主要内容,如果未能解决你的问题,请参考以下文章