按照惯例,这里也从一个Hello项目开始,本项目定义了一个Hello Service,客户端发送包含字符串名字的请求,服务端返回Hello消息。
    流程:

    1. 编写.proto描述文件
    2. 编译生成.pb.go文件
    3. 服务端实现约定的接口并提供服务
    4. 客户端按照约定调用.pb.go文件中的方法请求服务

    项目结构:

    1. |—— hello/
    2. |—— client/
    3. |—— main.go // 客户端
    4. |—— server/
    5. |—— main.go // 服务端
    6. |—— proto/
    7. |—— hello/
    8. |—— hello.proto // proto描述文件
    9. |—— hello.pb.go // proto编译后文件

    Step1:编写描述文件:hello.proto

    1. syntax = "proto3"; // 指定proto版本
    2. package hello; // 指定默认包名
    3. // 指定golang包名
    4. option go_package = "hello";
    5. // 定义Hello服务
    6. service Hello {
    7. // 定义SayHello方法
    8. rpc SayHello(HelloRequest) returns (HelloResponse) {}
    9. }
    10. // HelloRequest 请求结构
    11. message HelloRequest {
    12. string name = 1;
    13. }
    14. // HelloResponse 响应结构
    15. message HelloResponse {
    16. string message = 1;
    17. }

    hello.proto文件中定义了一个Hello Service,该服务包含一个SayHello方法,同时声明了HelloRequest和HelloResponse消息结构用于请求和响应。客户端使用HelloRequest参数调用SayHello方法请求服务端,服务端响应HelloResponse消息。一个最简单的服务就定义好了。
    Step2:编译生成.pb.go文件

    1. $ cd proto/hello
    2. # 编译hello.proto
    3. $ protoc -I . --go_out=plugins=grpc:. ./hello.proto

    在当前目录内生成的hello.pb.go文件,按照.proto文件中的说明,包含服务端接口HelloServer描述,客户端接口及实现HelloClient,及HelloRequest、HelloResponse结构体。
    注意:不要手动编辑该文件
    Step3:实现服务端接口 server/main.go

    1. package main
    2. import (
    3. "fmt"
    4. "net"
    5. pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入编译生成的包
    6. "golang.org/x/net/context"
    7. "google.golang.org/grpc"
    8. "google.golang.org/grpc/grpclog"
    9. )
    10. const (
    11. // Address gRPC服务地址
    12. Address = "127.0.0.1:50052"
    13. )
    14. // 定义helloService并实现约定的接口
    15. type helloService struct{}
    16. // HelloService Hello服务
    17. var HelloService = helloService{}
    18. // SayHello 实现Hello服务接口
    19. func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloResponse, error) {
    20. resp := new(pb.HelloResponse)
    21. resp.Message = fmt.Sprintf("Hello %s.", in.Name)
    22. return resp, nil
    23. }
    24. func main() {
    25. listen, err := net.Listen("tcp", Address)
    26. if err != nil {
    27. grpclog.Fatalf("Failed to listen: %v", err)
    28. }
    29. // 实例化grpc Server
    30. s := grpc.NewServer()
    31. // 注册HelloService
    32. pb.RegisterHelloServer(s, HelloService)
    33. grpclog.Println("Listen on " + Address)
    34. s.Serve(listen)
    35. }

    服务端引入编译后的proto包,定义一个空结构用于实现约定的接口,接口描述可以查看hello.pb.go文件中的HelloServer接口描述。实例化grpc Server并注册HelloService,开始提供服务。
    运行:

    1. $ go run main.go
    2. Listen on 127.0.0.1:50052 //服务端已开启并监听50052端口

    Step4:实现客户端调用 client/main.go

    1. package main
    2. import (
    3. pb "github.com/jergoo/go-grpc-example/proto/hello" // 引入proto包
    4. "golang.org/x/net/context"
    5. "google.golang.org/grpc"
    6. "google.golang.org/grpc/grpclog"
    7. )
    8. const (
    9. // Address gRPC服务地址
    10. Address = "127.0.0.1:50052"
    11. )
    12. func main() {
    13. // 连接
    14. conn, err := grpc.Dial(Address, grpc.WithInsecure())
    15. if err != nil {
    16. grpclog.Fatalln(err)
    17. }
    18. defer conn.Close()
    19. // 初始化客户端
    20. c := pb.NewHelloClient(conn)
    21. // 调用方法
    22. req := &pb.HelloRequest{Name: "gRPC"}
    23. res, err := c.SayHello(context.Background(), req)
    24. if err != nil {
    25. grpclog.Fatalln(err)
    26. }
    27. grpclog.Println(res.Message)
    28. }

    客户端初始化连接后直接调用hello.pb.go中实现的SayHello方法,即可向服务端发起请求,使用姿势就像调用本地方法一样。
    运行:

    1. $ go run main.go
    2. Hello gRPC. // 接收到服务端响应

    如果你收到了”Hello gRPC”的回复,恭喜你已经会使用github.com/jergoo/go-grpc-example/proto/hello了。
    建议到这里仔细看一看hello.pb.go文件中的内容,对比hello.proto文件,理解protobuf中的定义转换为golang后的结构。