Hello gRPC
按照惯例,这里也从一个Hello项目开始,本项目定义了一个Hello Service,客户端发送包含字符串名字的请求,服务端返回Hello消息。
流程
- 编写
.proto
描述文件
- 编译生成
.pb.go
文件
- 服务端实现约定的接口并提供服务
- 客户端按照约定调用
.pb.go
文件中的方法请求服务
环境
下载对应平台的代码生成工具(或者自行编译)
https://github.com/google/protobuf/releases
安装编译插件
1 2 3 4 5 6 7 8 9 10 11 12 13
| # 在gopath的bin目录下生成protoc-gen-go.exe go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
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
cd $GOPATH/src/ go install google.golang.org/grpc
|
实例
1 2 3 4 5 6 7 8 9
| |—— hello/ |—— client/ |—— main.go // 客户端 |—— server/ |—— main.go // 服务端 |—— proto/ |—— hello/ |—— hello.proto // proto描述文件 |—— hello.pb.go // proto编译后文件
|
Step1:编写描述文件:hello.proto
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| syntax = "proto3"; package hello;
option go_package = "hello"; service Hello { rpc SayHello(HelloRequest) returns (HelloResponse) {} }
message HelloRequest { string name = 1; }
message HelloResponse { string message = 1; }
|
hello.proto
文件中定义了一个Hello Service,该服务包含一个SayHello
方法,同时声明了HelloRequest
和HelloResponse
消息结构用于请求和响应。客户端使用HelloRequest
参数调用SayHello
方法请求服务端,服务端响应HelloResponse
消息。一个最简单的服务就定义好了。
Step2:编译生成.pb.go文件
1 2 3
| $ cd proto/hello # 编译hello.proto $ protoc.exe -I F:/goproject/src/hellogrpc/proto/hello/ --go_out=plugins=grpc:F:/goproject/src/hellogrpc/proto/hello/ hello.proto
|
在当前目录内生成的hello.pb.go
文件,按照.proto
文件中的说明,包含服务端接口HelloServer
描述,客户端接口及实现HelloClient
,及HelloRequest
、HelloResponse
结构体。
注意:不要手动编辑该文件
Step3:实现服务端接口 server/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| package main import ( "fmt" "net" pb "hellogrpc/proto/hello" "context" "google.golang.org/grpc" "log" ) const ( Address = "127.0.0.1:50052" )
type helloService struct{}
var HelloService = helloService{}
func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) { resp := new(pb.HelloResponse) resp.Message = fmt.Sprintf("Hello %s.", in.Name) return resp, nil } func main() {
listen, err := net.Listen("tcp", Address) if err != nil { log.Fatalf("Failed to listen: %v", err) } s := grpc.NewServer() pb.RegisterHelloServer(s, HelloService) log.Println("Listen on " + Address) s.Serve(listen) }
|
服务端引入编译后的proto
包,定义一个空结构用于实现约定的接口,接口描述可以查看hello.pb.go
文件中的HelloServer
接口描述。实例化grpc Server并注册HelloService,开始提供服务。
运行:
1 2
| $ go run main.go Listen on 127.0.0.1:50052 # 服务端已开启并监听50052端口
|
Step4:实现客户端调用 client/main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| package main import ( pb "hellogrpc/proto/hello" "golang.org/x/net/context" "google.golang.org/grpc" "log" ) const ( Address = "127.0.0.1:50052" ) func main() { conn, err := grpc.Dial(Address, grpc.WithInsecure()) if err != nil { log.Fatalln(err) } defer conn.Close() c := pb.NewHelloClient(conn) req := &pb.HelloRequest{Name: "gRPC"} res, err := c.SayHello(context.Background(), req) if err != nil { log.Fatalln(err) } log.Println(res.Message) }
|
客户端初始化连接后直接调用hello.pb.go
中实现的SayHello
方法,即可向服务端发起请求,使用姿势就像调用本地方法一样。
运行:
1 2
| $ go run main.go Hello gRPC. # 接收到服务端响应
|
建议到这里仔细看一看hello.pb.go文件中的内容,对比hello.proto文件,理解protobuf中的定义转换为golang后的结构。