• go实现简单gRPC demo


    环境配置

    1. 1.# 下载最新版本23.2的protoc,这个是protobuf代码生成工具,通过proto文件生成对应的代码,根据自己操作系统下载相应文件,这里以windows 64位系统为例
    2. wget https://github.com/protocolbuffers/protobuf/releases/download/v23.2/protoc-23.2-win64.zip
    3. # 解压并放在windows本地目录,并配置在Path路径下如D:\Program Files\protoc-23.2-win64\bin,在windows下命令行执行protoc --version检查是否安装配置正确
    4. 2.# 创建go项目grpc-demo,在GoLand IDE编写,并通过下面命令安装grpc核心库protoc,可以GoLand IDE安装protoc插件,实现语法高亮
    5. go get google.golang.org/grpc
    6. 3.# 在实际开发中最好指定具体的版本,这里是演示使用就直接用latest,在命令行中执行下面两条命令
    7. go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
    8. go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest

    文件结构

    1.创建user-service模块

    service/users.proto内容:

    1. syntax = "proto3";
    2. option go_package = "../service";
    3. // 服务和方法
    4. service Users {
    5. rpc GetUser (UserGetRequest) returns (UserGetReply) {}
    6. }
    7. // 请求消息
    8. message UserGetRequest {
    9. string email = 1;
    10. int32 id = 2;
    11. }
    12. // 响应消息
    13. message User {
    14. string id = 1;
    15. string first_name = 2;
    16. string last_name = 3;
    17. int32 age = 4;
    18. }
    19. message UserGetReply {
    20. User user = 1;
    21. }

    2.生成客户端和服务端代码

    在service目录下执行以下命令,自动生成pb.go文件

    protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative users.proto

    3.编写服务器

    server/server.go内容:

    1. package main
    2. import (
    3. "context"
    4. "log"
    5. "net"
    6. "os"
    7. users "testgo/service" // 导入之前生成的包
    8. "google.golang.org/grpc"
    9. )
    10. // userService类型是Users服务的服务处理程序
    11. type userService struct {
    12. users.UnimplementedUsersServer // 这个字段对于gRPC中的任何服务实现都是强制性的
    13. }
    14. func (s *userService) GetUser(ctx context.Context, in *users.UserGetRequest) (*users.UserGetReply, error) {
    15. // 打印客户端传过来的数据
    16. log.Printf("已接收到邮件地址: %s, 还有ID: %d", in.Email, in.Id)
    17. // 自定义数据响应给客户端
    18. u := users.User{
    19. Id: "user-782911",
    20. FirstName: "mike",
    21. LastName: "li",
    22. Age: 22,
    23. }
    24. return &users.UserGetReply{User: &u}, nil
    25. }
    26. // 向gRPC服务器注册Users服务
    27. func registerServices(s *grpc.Server) {
    28. users.RegisterUsersServer(s, &userService{})
    29. }
    30. // 启动gRPC服务器
    31. func startServer(s *grpc.Server, l net.Listener) error {
    32. return s.Serve(l)
    33. }
    34. func main() {
    35. listenAddr := os.Getenv("LISTEN_ADDR")
    36. if len(listenAddr) == 0 {
    37. listenAddr = ":50051"
    38. }
    39. lis, err := net.Listen("tcp", listenAddr)
    40. if err != nil {
    41. log.Fatal(err)
    42. }
    43. s := grpc.NewServer()
    44. registerServices(s)
    45. log.Fatal(startServer(s, lis))
    46. }
    •  在gRPC服务实现中,都需要嵌入一个users.UnimplementedUsersServer字段(在grpc.pb.go中存在该字段),将该字段嵌入到userService的struct中。
    • 实现proto中定义的GetUser接口,接收器为userService的指针类型,在方法内可以直接访问结构体的字段并修改。GetUser接口入参为context和proto定义的request指针,返回值是proto定义的response指针以及error。注意返回的response要按照proto定义的response格式实现。
    • 新建注册user服务方法,调用grpc.pb.go的RegisterUsersServer方法,该方法需要两个参数:一个grpc.Server类型的指针(grpc的服务对象)以及一个实现了UsersServer接口的对象(在这里userService结构体实现了UsersServer接口,它以指针方式传给RegisterUsersServer),RegisterUsersServer会将这个对象注册到gRPC服务中,这样客户端就可以通过这个对象调用定义在接口中的方法了。
    • 新建启动gRPC服务方法,其中入参有两个:s是已经初始化好的grpc.Server对象,l是通过net.Listen获取的监听套接字。Serve方法将grpc服务绑定到监听套接字l上,开始接收连接(阻塞)。
    • main的步骤十分清晰,先获取监听套接字,新建gRPC服务对象,注册服务,启动服务监听请求。

    4.编写客户端

    1. package main
    2. import (
    3. "context"
    4. "log"
    5. "os"
    6. users "testgo/service" // 导入之前生成的包
    7. "google.golang.org/grpc"
    8. )
    9. // 建立与服务器的连接(通道)
    10. func setupGrpcConnection(addr string) (*grpc.ClientConn, error) {
    11. return grpc.DialContext(
    12. context.Background(),
    13. addr,
    14. grpc.WithInsecure(),
    15. grpc.WithBlock(),
    16. )
    17. }
    18. // 创建客户端与Users服务通信
    19. func getUserServiceClient(conn *grpc.ClientConn) users.UsersClient {
    20. return users.NewUsersClient(conn)
    21. }
    22. // 调用Users服务中的GetUser()方法
    23. func getUser(client users.UsersClient, u *users.UserGetRequest) (*users.UserGetReply, error) {
    24. return client.GetUser(context.Background(), u)
    25. }
    26. func main() {
    27. if len(os.Args) != 2 {
    28. log.Fatal("缺少gRPC服务器地址")
    29. }
    30. conn, err := setupGrpcConnection(os.Args[1])
    31. if err != nil {
    32. log.Fatal(err)
    33. }
    34. defer conn.Close()
    35. c := getUserServiceClient(conn)
    36. result, err := getUser(c, &users.UserGetRequest{
    37. Email: "mike_li@163.com",
    38. Id: 8082731,
    39. })
    40. if err != nil {
    41. log.Fatal(err)
    42. }
    43. // 打印响应
    44. log.Printf("收到响应: %s %s %s %d\n", result.User.Id, result.User.FirstName, result.User.LastName, result.User.Age)
    45. }
    •  新建 建立与服务器连接(通道)的方法,这里的入参就是监听的端口号,返回一个*grpc.ClientConn对象。后续Client可以使用这个ClientConn对象来发送RPC请求。这里内部使用grpc.DialContext来创建连接,grpc.WithInsecure() 表示使用不安全连接,即不验证 TLS 证书,grpc.WithBlock() 设置连接是阻塞的。该方法的作用是封装了gRPC客户端连接的创建,提供了一个简单的接口给调用者使用。
    • 在getUserServiceClient方法中,使用返回的ClientConn对象来创建具体的服务客户端,NewUsersClient方法是pb.go文件自动生成的。
    • 在getUser方法中,使用创建好的具体的服务客户端和request调用Users服务中的GetUser()方法。注意此处的GetUser()是服务端实现的remote方法,该方法的定义在grpc.pb.go中。
    • main中步骤较清晰,先获取一个Client连接对象(注意用defer在最后return后关闭该连接),使用该连接对象创建具体的客户端,调用本地getUser()方法(实际调用remote服务端的GetUser()方法),传入client对象以及request信息,获取响应。

    5.效果验证

    1. # 1.服务端执行(在server文件夹下):
    2. go run .\server.go
    3. # 2.客户端执行(开启另外一个终端,在client文件夹下):
    4. go run main.go localhost:50051

  • 相关阅读:
    Git明白人
    「Python」身份运算符 —— is 与 is not
    git--工程备份git仓的使用
    ActiveMQ 反序列化漏洞(CVE-2015-5254)特征分析
    AISummit全球人工智能技术大会顺利开幕:首日精彩回顾
    建模杂谈系列157 关于“如何降低工作成本“的思考
    如何使用Vcluster实现Kubernetes中的多租户
    java 自带命令
    C++ 01
    windows系统cmake生成动态库无lib文件解决方法
  • 原文地址:https://blog.csdn.net/weixin_43459883/article/details/132618876