1.什么是gRPC
gRPC 是一个高性能、开源、通用的RPC
框架,由Google
推出,基于HTTP2协议标准设计开发,默认采用Protocol Buffers数据序列化协议,支持多种开发语言。gRPC
提供了一种简单的方法来精确的定义服务,并且为客户端和服务端自动生成可靠的功能库。
2.gRPC技术栈
最底层为TCP
或Unix
套接字协议,在此之上是HTTP/2
协议的实现,然后在HTTP/2
协议之上又构建了针对Go
语言的gRPC
核心库(gRPC
内核+解释器)。应用程序通过gRPC
插件生成的Stub
代码和gRPC
核心库通信,也可以直接和gRPC
核心库通信。
3. 前置准备
3.1 安装protoc
下面示例是在mac环境
中安装。
➜ brew install protobuf
➜ protoc --version libprotoc 3.17.3
|
3.2 安装插件
安装插件的目的是为了将protobuf
文件,生成Go
代码
$ go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.26 $ go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.1
|
3.3 设置插件环境变量
$ export PATH="$PATH:$(go env GOPATH)/bin"
|
3.4 验证插件是否成功
$ protoc-gen-go --version protoc-gen-go v1.26.0
$ protoc-gen-go-grpc --version protoc-gen-go-grpc 1.1.0
|
4. 快速使用
4.1 定义protobuf
文件
文件名: hello.proto
syntax = "proto3"; package hello;
option go_package = "server/hello";
message Request { string name =1; }
message Response { string result = 1; }
service UserService { rpc Say(Request) returns (Response); }
|
4.2 生成GO
代码
$ protoc --go-grpc_out=. --go_out=. hello.proto
|
4.3 查看生成代码
1. hello.pb.go
部分代码
package hello
import ( protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" )
const ( _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) )
type Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` }
func (x *Request) GetName() string { if x != nil { return x.Name } return "" }
type Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields
Result string `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` } func (x *Response) GetResult() string { if x != nil { return x.Result } return "" }
|
2. hello_grpc.pb.go
部分代码
package hello
import ( context "context" "fmt" grpc "google.golang.org/grpc" )
type UserServiceClient interface { Say(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) }
type userServiceClient struct { cc grpc.ClientConnInterface }
func NewUserServiceClient(cc grpc.ClientConnInterface) UserServiceClient { return &userServiceClient{cc} }
func (c *userServiceClient) Say(ctx context.Context, in *Request, opts ...grpc.CallOption) (*Response, error) { out := new(Response) err := c.cc.Invoke(ctx, "/hello.UserService/Say", in, out, opts...) if err != nil { return nil, err } return out, nil }
type UserServiceServer interface { Say(context.Context, *Request) (*Response, error) mustEmbedUnimplementedUserServiceServer() }
type UnimplementedUserServiceServer struct { }
func (UnimplementedUserServiceServer) Say(ctx context.Context, r *Request) (*Response, error) { reply := &Response{ Result: fmt.Sprintf("%s say hello word!", r.Name), } return reply, nil }
func RegisterUserServiceServer(s grpc.ServiceRegistrar, srv UserServiceServer) { s.RegisterService(&UserService_ServiceDesc, srv) }
|
4.4 下载grpc
包
$ go get -u google.golang.org/grpc
|
4.5 编写服务端代码
server.go
package main
import ( "52lu/go-study-example/grpc/server/hello" "fmt" "google.golang.org/grpc" "net" )
func main() { grpcServer := grpc.NewServer() hello.RegisterUserServiceServer(grpcServer, new(hello.UnimplementedUserServiceServer)) listen, err := net.Listen("tcp", ":1234") if err != nil { fmt.Println("服务启动失败", err) return } grpcServer.Serve(listen) }
|
4.6 编写客户端代码
client.go
package main
import ( "52lu/go-study-example/grpc/server/hello" "fmt" "golang.org/x/net/context" "google.golang.org/grpc" )
func main() { conn, err := grpc.Dial("127.0.0.1:1234", grpc.WithInsecure()) if err != nil { fmt.Println("Dial Error ", err) return } defer conn.Close() client := hello.NewUserServiceClient(conn) reply, err := client.Say(context.TODO(), &hello.Request{Name: "张三"}) if err != nil { return } fmt.Println("返回:", reply.Result) }
|
4.7 启动 & 请求
$ go run server.go
$ go run client.go 返回: 张三 say hello word!
|