以前学习的go的微服务之间仍是经过REST API
的方式互相调用的,但既然要学习微服务,gRPC
确定是一个绕不过去的须要学习的技术, 因此就开搞吧java
gRPC
是一款语言中立、平台中立、开源的远程过程调用系统git
即:
gRPC
客户端和服务端能够在多种环境中运行和交互,例如用java
写一个服务端,能够用go语言写客户端调用github
微服务架构中,因为每一个服务对应的代码库是独立运行的,没法直接调用,彼此间的通讯就是个大问题.golang
gRPC能够实现将大的项目拆分为多个小且独立的业务模块,也就是服务。各服务间使用高效的protobuf
协议进行RPC调用,gRPC默认使用protocol buffers
,这是google开源的一套成熟的结构数据序列化机制shell
固然也能够使用其余数据格式如JSONvim
能够用proto files建立gRPC服务,用message类型来定义方法参数和返回类型bash
以下图,解压出来因平台而异会是一个protoc
或者protoc.exe
架构
# 打开存放环境变量的文件 vim ~/.bash_profile # 添加以下,后面是路径 alias protoc="/Users/emm/others/protoc-3.12.4-osx-x86_64/bin/protoc" # 刷新环境变量 source ./.bash_profile
go get github.com/golang/protobuf/protoc-gen-go函数
安装后会在GOPATH
目录下生成可执行文件,protobuf的编译器插件protoc-gen-go
,等下执行protoc
命令会自动调用这个插件
这里新建一个pbfiles文件夹用于存放protoc
文件
// 这个就是protobuf的中间文件 // 指定的当前proto语法的版本,有2和3 syntax = "proto3"; // 指定等会文件生成出来的package package service; // 定义request message ProductRequest{ int32 prod_id = 1; // 1表明顺序 } // 定义response message ProductResponse{ int32 prod_stock = 1; // 1表明顺序 }
而后运行如下的命令来生成.go
结尾的文件
protoc
包以及protoc-gen-go
插件的做用# 编译Product.proto以后输出到service文件夹 protoc --go_out=../service Product.proto
以下就在service文件夹自动生成了一个go文件,而且它提示咱们不要去修改它
这个protoc文件比上面的多出了一个service的定义和里面的一个方法的定义
// 这个就是protobuf的中间文件 // 指定的当前proto语法的版本,有2和3 syntax = "proto3"; // 指定等会文件生成出来的package package service; // 定义request model message ProductRequest{ int32 prod_id = 1; // 1表明顺序 } // 定义response model message ProductResponse{ int32 prod_stock = 1; // 1表明顺序 } // 定义服务主体 service ProdService{ // 定义方法 rpc GetProductStock(ProductRequest) returns(ProductResponse); }
注意
protoc --go_out=plugins=grpc:../service Product.proto
而后仍是会在service文件夹下生成一个.go
的文件
有两个比较须要注意的
后面须要在server中调用这个来注册
咱们须要继承这个接口,即实现它全部的方法
上面咱们在protoc
文件中定义了一个ProdService
中包含了一个GetProductStock
的方法
这里咱们要实现自动生成的go文件中的接口
package service import "context" type ProdService struct { } func (ps *ProdService) GetProductStock(ctx context.Context, request *ProductRequest) (*ProductResponse, error) { return &ProductResponse{ProdStock: request.ProdId}, nil }
前面的都是准备工做,这里是真正把服务端跑起来的操做
下面是服务端代码:
package main import ( "gomicro-quickstart/grpc_demo/service" "google.golang.org/grpc" "log" "net" ) func main() { // 1. new一个grpc的server rpcServer := grpc.NewServer() // 2. 将刚刚咱们新建的ProdService注册进去 service.RegisterProdServiceServer(rpcServer, new(service.ProdService)) // 3. 新建一个listener,以tcp方式监听8082端口 listener, err := net.Listen("tcp", ":8082") if err != nil { log.Fatal("服务监听端口失败", err) } // 4. 运行rpcServer,传入listener _ = rpcServer.Serve(listener) }
排坑:
undefined: grpc.SupportPackageIsVersion6
和undefined: grpc.ClientConnInterface
的错误,能够修改go.mod将grpc版本改到1.27.0grpc_client
文件夹存放客户端相关的grpc_client
文件夹下再新建一个service
文件夹package main import ( "context" "fmt" "gomicro-quickstart/grpc_client/service" "google.golang.org/grpc" "log" ) func main() { // 1. 新建链接,端口是服务端开放的8082端口 // 而且添加grpc.WithInsecure(),否则没有证书会报错 conn, err := grpc.Dial(":8082", grpc.WithInsecure()) if err != nil { log.Fatal(err) } // 退出时关闭连接 defer conn.Close() // 2. 调用Product.pb.go中的NewProdServiceClient方法 productServiceClient := service.NewProdServiceClient(conn) // 3. 直接像调用本地方法同样调用GetProductStock方法 resp, err := productServiceClient.GetProductStock(context.Background(), &service.ProductRequest{ProdId: 233}) if err != nil { log.Fatal("调用gRPC方法错误: ", err) } fmt.Println("调用gRPC方法成功,ProdStock = ", resp.ProdStock) }
而后客户端输出正确的结果,第一个go的gRPC调用运行成功