go|go gRPC初体验(win10+普通网络)

在公司实习的时候,发现代码里面有grpc,当时啥也不懂,也不知道咋用的,好在实习期间并没有需要新增rpc调用的地方,但还是觉得趁早弄明白比较好,以后总会用到。
【go|go gRPC初体验(win10+普通网络)】既然是初体验,肯定是从啥都没有开始的,网上很多文章,安装各种包、命令讲的不是很系统,所以本篇就记录一下从安装开始,到运行一个小case的全过程。
首先声明一下,我是在win10上操作的,我觉得吧,什么东西,如果能在windows上搞通了,在linux和mac上自然也就没问题了。
另外再声明一下,我电脑没有,以下所有操作都是普通网络环境就可以搞定的。
安装protoc grpc使用protocol buffers作为IDL,这很好理解,都是谷歌出的,而使用grpc,第一步其实就是自己写好proto文件,然后使用protoc命令生成go语言包,所以,我们首先要装protoc命令,这个东西我一开始一看还挺恐慌的,想着是不是得编译安装啥的,后来发现并不用,直接下载现成的exe文件就完事儿了,这里直接给出网址 https://github.com/protocolbuffers/protobuf/releases
这个网址打开是这样的:
go|go gRPC初体验(win10+普通网络)
文章图片

可以看到最新版本已经是3.11了,那么问题来了,一大堆版本,有各种语言的,还有各种系统的,我们要下哪个呢?说实话我也不懂。。。不过既然是要在windows上体验,就下个win64的吧,最后的成功证明,我的直觉是对的,哈哈。
go|go gRPC初体验(win10+普通网络)
文章图片

下下来之后,解压,然后,直接将bin里面的exe文件,就是下面这个,拷贝到PATH里的任一一个路径下面就行,既然我是为了用它做go语言的grpc,所以我选择了把它放在我的GOROOT/bin里面。
go|go gRPC初体验(win10+普通网络)
文章图片

然后,打开cmd验证一下,输入

protoc --version

看到版本号,就说明protoc安装成功了。
go|go gRPC初体验(win10+普通网络)
文章图片

下载grpc包 这块呢,因为是普通网络环境,go get是用不了的,所以我们用git下载grpc的go语言包。
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc

这里注意,我们clone下来之后把包放到了$GOPATH/src/google.golang.org/grpc,这是因为,谷歌只是把程序放在了github一份,但是其他包引用grpc包的时候用的还是google.golang.org/grpc这个路径。
一般情况下,装个一个比较大的包会依赖很多其他包,grpc也是如此,但是正常情况下,到这里我们也不知道它缺啥依赖,所以先不管,最后运行程序的时候根据错误提示,缺啥补啥就好了。
体验1 老实说,第一次自己体验,到这里我已经认为可以了。。。所以直接开始尝试了。结果是失败了,但是我觉得没啥,按网上写的一通乱装,都不知道装的那些东西是干啥的,倒不如遇到问题了,再看到底是缺了啥,然后再去装。
言归正传,初次体验,我的目标很简单,就是跑通grpc包里提供的helloworld,grpc的官方文档教的其实也是这个。
首先,在$GOPATH/src/下新建一个工程,就叫goGrpc-test吧。
然后,新建一个helloworld文件夹,在里面新建一个helloworld.proto文件,把grpc包里提供的helloworld.proto文件内容拷贝进去,proto文件在这里:
go|go gRPC初体验(win10+普通网络)
文章图片

proto文件内容是这些:
syntax = "proto3"; option java_multiple_files = true; option java_package = "io.grpc.examples.helloworld"; option java_outer_classname = "HelloWorldProto"; package helloworld; // The greeting service definition. service Greeter { // Sends a greeting rpc SayHello (HelloRequest) returns (HelloReply) {} }// The request message containing the user's name. message HelloRequest { string name = 1; }// The response message containing the greetings message HelloReply { string message = 1; }

这个proto文件吧,我以前也没接触过,要自己写肯定是完全不会,不过看这个写好的其实也能懂大概意思。
  • syntax = "proto3" 这个就是指定proto版本,我们一开始是装的就是3.11,所以这里也是proto3
  • option 这几行我是真的毫无头绪,这不是go么,跟java有啥关系。。。
  • package helloworld 这行好理解,就是用protoc命令生成的go代码的包名,因为后面写客户端服务器程序都要引用这个包,所以就叫helloworld了,也就是跟所在路径同名
  • service 这块呢,就是定义了这个远程调用的服务,服务请求者调用SayHello方法,请求中携带HelloRequest格式的信息,服务提供者则返回HelloReply格式的回复
  • message 这个就是定义请求和回复的格式,这个例子里请求和格式都是简单的string
欧克,这个proto大概理解了,接下来就是生成对应的go语言代码,这里终于要用到我们最开始安装的protoc了,命令如下:
protoc --go_out=plugins=grpc:. helloworld.proto

然鹅,失败了。。。
go|go gRPC初体验(win10+普通网络)
文章图片

这报错信息说找不到,protoc-gen-go,看这个命令,有一个--go_out,这个应该就是生成go代码的意思,看来,生成go代码还需要装这个protoc-gen-go才行,好吧,退出体验,继续装命令。
安装protoc-gen-go 看网上的文章,这个又是go get装的,既然是go-get,都可以通过git clone+go install取代,所以我们首先拉下代码。
git clone https://github.com/golang/protobuf.git$GOPATH/src/github.com/golang/protobuf

注意,这里我直接把protobuf的go语言包拉了下来,因为里面有protoc-gen-go。
go|go gRPC初体验(win10+普通网络)
文章图片

接下来就是安装,安装其实就是编译包里面的文件,生成一个exe文件,然后放到$GOPATH/bin下面,安装命令如下:
go install github.com/golang/protobuf/protoc-gen-go

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
2020.4.18补充:今天,一个同学按照我的blog装protoc-gen-go的时候,报了这个错:
go|go gRPC初体验(win10+普通网络)
文章图片

按照这个错误,我打开我的github.com\golang\protobuf\protoc-gen-go\main.go瞅了一眼,发现28行并没有import它说找不到的这个包,后来发现,golang/protobuf这个仓库在2020.4.13提交过一次:
go|go gRPC初体验(win10+普通网络)
文章图片

这次提交修改了main.go的内容,修改之后该怎么安装protoc-gen-go我还没有研究,不过经过试验,把版本回退到我clone的时候那个版本之后再go install是欧克的,所以如果你想先凑合用的话,就先把protobuf回退到这个版本,再go install。
git reset --hard d23c5127

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
go install执行成功不会有什么安装成功之类的信息,我们可以直接去$GOPATH/bin下看看有没有生成对应的exe文件。
go|go gRPC初体验(win10+普通网络)
文章图片

看到有这个,那就是装成功了。
使用protoc生成go语言代码 刚才失败了,说是找不到这个proto-gen-go,现在我们装上了,可以再试一把了。
go|go gRPC初体验(win10+普通网络)
文章图片

这回,没有报错,而且成功生成了一个go文件。
go|go gRPC初体验(win10+普通网络)
文章图片

打开生成的代码一看,妈呀,刚才20行的proto文件,竟然生成了一个两百多行的go程序,好厉害,不得不说,这些rpc框架确实是很方便,如果没有这个,这些代码就要我们手动去写。
体验2 行了,go代码也生成了,接下来我们就可以使用这些生成好的代码,写一个客户端,一个服务器,体验一下rpc了。
客户端和服务器的程序呢,我也是用的grpc的helloworld包里面提供的,为了模拟这是我自己写的一个rpc,我又把里面的内容拷出来放到我自己新建的文件里了。
首先,自己新建一个client.go,一个server.go。
go|go gRPC初体验(win10+普通网络)
文章图片

然后,把grpc包helloword里的客户端和服务器程序相应的贴进去。
go|go gRPC初体验(win10+普通网络)
文章图片

贴进去之后,改一下,import,改成我们刚刚自己生成的helloworld包。
最终的server.go如下:
package mainimport ( "context" "log" "net" "google.golang.org/grpc" pb "goGrpc-test/helloworld" )const ( port = ":50051" )// server is used to implement helloworld.GreeterServer. type server struct { pb.UnimplementedGreeterServer }// SayHello implements helloworld.GreeterServer func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { log.Printf("Received: %v", in.GetName()) return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil }func main() { lis, err := net.Listen("tcp", port) if err != nil { log.Fatalf("failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterGreeterServer(s, &server{}) if err := s.Serve(lis); err != nil { log.Fatalf("failed to serve: %v", err) } }

client.go代码如下:
package mainimport ( "context" "log" "os" "time" "google.golang.org/grpc" pb "goGrpc-test/helloworld" )const ( address= "localhost:50051" defaultName = "world" )func main() { // Set up a connection to the server. conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock()) if err != nil { log.Fatalf("did not connect: %v", err) } defer conn.Close() c := pb.NewGreeterClient(conn) // Contact the server and print out its response. name := defaultName if len(os.Args) > 1 { name = os.Args[1] } ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) if err != nil { log.Fatalf("could not greet: %v", err) } log.Printf("Greeting: %s", r.GetMessage()) }

到这里已经忍不住开始激动了,马上要见证成果了!先把server跑起!
go|go gRPC初体验(win10+普通网络)
文章图片

这一大堆错吓了我一跳。。。好在定睛一看,只是缺少依赖包而已。。。
安装grpc依赖包 根据上面的报错,先把这个golang.org/x/net装上。
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net

然后再跑一下。
go|go gRPC初体验(win10+普通网络)
文章图片

还缺个text和genproto,一口气装上:
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto

体验3 依赖装好了,再来!
先服务器跑起。
go|go gRPC初体验(win10+普通网络)
文章图片

这回没报错,还弹出个windows防火墙,说明开始监听端口了,嘿嘿。
接下来,客户端走起,注意这里客户端和服务器分别跑在两个terminal里。
go|go gRPC初体验(win10+普通网络)
文章图片

哈哈,成功了,客户端收到了来自服务器的回复,正是我们熟悉的Hello world!
再瞅一眼服务器。
go|go gRPC初体验(win10+普通网络)
文章图片

也打印到了接收到的信息,完美!
到这里,go语言grpc初体验就算是成功收尾了。
总结 这一趟做下来,也算是对grpc有个基本认识了,总结一下,当我们需要增加一个grpc接口的时候,其实就是以下几步
  • 修改proto文件,新增一个service,以及相应的请求和回复的message
  • 使用protoc生成go代码
  • 在服务器调用者和服务提供者的代码里分别调用生成的go包里的方法
这么一看,还蛮简单的,哈哈。
一个小补充 最后还想简单记录一个点,之前实习的时候在一次会上听到的,这个点就是,为什么程序不全使用thrift,而是同时使用thrift和grpc?
首先,thrift是Facebook的开源rpc框架,也是一个使用非常广泛的rpc框架(这个我也体验了一把,感兴趣的看这里go Thrift初体验(win10+普通网络))。
那么grpc和thrift相比优势在哪里呢,主要在于,grpc是基于http-2设计的,http-2的一个重要特点是在单个TCP连接上可以复用多个请求,这一点thrift是做不到的,也就是说,thrift的rpc,一次调用在收到回复之前,是会占用一个TCP连接的,当并发量很高,而一次调用耗时又很长的时候,thrift会出现连接被占满的情况,导致调用堆积,延时增大,而grpc,多个调用可以复用一个连接,这样显然就比thrift支持的并发调用数量更高。
如果是同机房调用,调用一次的延时是很低的,这个时候grpc的优势并不能体现,但如果服务请求者和服务提供者在不同机房,一次通信的延时可能很大,这个时候用grpc就是一个更好的选择。
grpc的官方文档有一句是,grpc在移动设备上表现更好,其实也就是上面说的这个道理。

    推荐阅读