1. 按照惯例,这里也从一个Hello项目开始,本项目定义了一个Hello Service,客户端发送包含字符串名字的请求,服务端返回Hello消息。
    2. 流程:
    3. 编写.proto描述文件
    4. 编译生成.pb.go文件
    5. 服务端实现约定的接口并提供服务
    6. 客户端按照约定调用.pb.go文件中的方法请求服务
    7. 项目结构:
    8. |—— hello/
    9. |—— client/
    10. |—— main.go // 客户端
    11. |—— server/
    12. |—— main.go // 服务端
    13. |—— proto/
    14. |—— hello/
    15. |—— hello.proto // proto描述文件
    16. |—— hello.pb.go // proto编译后文件
    17. Step1:编写描述文件:hello.proto
    18. syntax = "proto3"; // 指定proto版本
    19. package hello; // 指定默认包名
    20. // 指定golang包名
    21. option go_package = "hello";
    22. // 定义Hello服务
    23. service Hello {
    24. // 定义SayHello方法
    25. rpc SayHello(HelloRequest) returns (HelloResponse) {}
    26. }
    27. // HelloRequest 请求结构
    28. message HelloRequest {
    29. string name = 1;
    30. }
    31. // HelloResponse 响应结构
    32. message HelloResponse {
    33. string message = 1;
    34. }
    35. hello.proto文件中定义了一个Hello Service,该服务包含一个SayHello方法,同时声明了HelloRequestHelloResponse消息结构用于请求和响应。客户端使用HelloRequest参数调用SayHello方法请求服务端,服务端响应HelloResponse消息。一个最简单的服务就定义好了。
    36. Step2:编译生成.pb.go文件
    37. $ cd proto/hello
    38. # 编译hello.proto
    39. $ protoc -I . --go_out=plugins=grpc:. ./hello.proto
    40. 在当前目录内生成的hello.pb.go文件,按照.proto文件中的说明,包含服务端接口HelloServer描述,客户端接口及实现HelloClient,及HelloRequestHelloResponse结构体。
    41. 注意:不要手动编辑该文件
    42. Step3:实现服务端接口 server/main.go
    43. package main
    44. import (
    45. "fmt"
    46. "net"
    47. pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入编译生成的包
    48. "golang.org/x/net/context"
    49. "google.golang.org/grpc"
    50. "google.golang.org/grpc/grpclog"
    51. )
    52. const (
    53. // Address gRPC服务地址
    54. Address = "127.0.0.1:50052"
    55. )
    56. // 定义helloService并实现约定的接口
    57. type helloService struct{}
    58. // HelloService Hello服务
    59. var HelloService = helloService{}
    60. // SayHello 实现Hello服务接口
    61. func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
    62. resp := new(pb.HelloResponse)
    63. resp.Message = fmt.Sprintf("Hello %s.", in.Name)
    64. return resp, nil
    65. }
    66. func main() {
    67. listen, err := net.Listen("tcp", Address)
    68. if err != nil {
    69. grpclog.Fatalf("Failed to listen: %v", err)
    70. }
    71. // 实例化grpc Server
    72. s := grpc.NewServer()
    73. // 注册HelloService
    74. pb.RegisterHelloServer(s, HelloService)
    75. grpclog.Println("Listen on " + Address)
    76. s.Serve(listen)
    77. }
    78. 服务端引入编译后的proto包,定义一个空结构用于实现约定的接口,接口描述可以查看hello.pb.go文件中的HelloServer接口描述。实例化grpc Server并注册HelloService,开始提供服务。
    79. 运行:
    80. $ go run main.go
    81. Listen on 127.0.0.1:50052 //服务端已开启并监听50052端口
    82. Step4:实现客户端调用 client/main.go
    83. package main
    84. import (
    85. pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
    86. "golang.org/x/net/context"
    87. "google.golang.org/grpc"
    88. "google.golang.org/grpc/grpclog"
    89. )
    90. const (
    91. // Address gRPC服务地址
    92. Address = "127.0.0.1:50052"
    93. )
    94. func main() {
    95. // 连接
    96. conn, err := grpc.Dial(Address, grpc.WithInsecure())
    97. if err != nil {
    98. grpclog.Fatalln(err)
    99. }
    100. defer conn.Close()
    101. // 初始化客户端
    102. c := pb.NewHelloClient(conn)
    103. // 调用方法
    104. req := &pb.HelloRequest{Name: "gRPC"}
    105. res, err := c.SayHello(context.Background(), req)
    106. if err != nil {
    107. grpclog.Fatalln(err)
    108. }
    109. grpclog.Println(res.Message)
    110. }
    111. 客户端初始化连接后直接调用hello.pb.go中实现的SayHello方法,即可向服务端发起请求,使用姿势就像调用本地方法一样。
    112. 运行:
    113. $ go run main.go
    114. Hello gRPC. // 接收到服务端响应
    115. 如果你收到了"Hello gRPC"的回复,恭喜你已经会使用github.com/jergoo/go-grpc-example/proto/hello了。
    116. 建议到这里仔细看一看hello.pb.go文件中的内容,对比hello.proto文件,理解protobuf中的定义转换为golang后的结构。