GO语言 gRPC入门 | 青训营

164 阅读3分钟

需求:开发健身房服务

定义了一个健身房(Gym),提供一个叫健身(Bodybuilding)的远程方法
使用该服务,需要指定人(Person),人有名字(name)和训练动作(actions)两个属性。

对应的协议文件gym.proto文件如下

syntax = "proto3";
//命名空间
package lightweight;

//健身房
service Gym {
    rpc BodyBuilding (Person) returns (Reply) {

    }
}
//谁在健身
message Person {
    string name = 1;
    repeated string actions = 2;
}

//结果
message Reply {
    int32 code = 1;
    string msg = 2;
}

Golang

1. 安装protoc

地址:protobuf/releases
我是mac,用的是这个地址:protoc-3.11.4-osx-x86_64.zip
解压后放到了可以访问的bin即可

2. 安装protoc-gen-go

protoc依赖该工具生成代码

go get -u github.com/golang/protobuf/protoc-gen-go

gogoprotobuf的protoc-gen-gofast插件生成的文件更复杂,性能也更高,安装如下
go get github.com/gogo/protobuf/protoc-gen-gofast

3. 安装grpc包

这是要代码里需要使用的,go get直接安装不了,手动克隆

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

4. 生成代码

#!/usr/bin/env bash

protoDir="../protos"
outDir="../languages/golang/gym"
protoc -I ${protoDir}/ ${protoDir}/*proto --go_out=plugins=grpc:${outDir}

protoc工具参数解释:

  • -I: 指定import路径,可以指定多个-I参数,编译时按顺序查找,不指定默认当前目录
  • -go_out:指定og语言的访问类
  • plugins:指定依赖的插件
使用gofast将go_out=plugins=grpc:${outDir}改为gofast_out=plugins=grpc:${outDir}

执行,然后我们会看到在golang目录生成了该代码

5. 定义服务端

package main

import (
 "app/lightweight"
 "context"
 "fmt"
 "google.golang.org/grpc"
 "log"
 "net"
)

const (
 port = ":50051"
)

// server继承自动生成的服务类
type server struct {
 lightweight.UnimplementedGymServer
}

// 服务端必须实现了相应的接口BodyBuilding
func (s *server) BodyBuilding(ctx context.Context, in *lightweight.Person) (*lightweight.Reply, error) {
 fmt.Printf("%s正在健身, 动作: %s\n", in.Name, in.Actions)
 return &lightweight.Reply{Code: 0, Msg: "ok",}, nil
}
func main() {
 lis, err := net.Listen("tcp", port)
 if err != nil {
  log.Fatalf("failed to listen: %v", err)
 }

 s := grpc.NewServer()
 lightweight.RegisterGymServer(s, &server{})

 if err := s.Serve(lis); err != nil {
  log.Fatalf("failed to serve: %v", err)
 }
}

6. 定义客户端

package main

import (
 "app/lightweight"
 "context"
 "fmt"
 "google.golang.org/grpc"
 "log"
 "time"
)

const (
 address = "localhost:50051"
)

func main() {
 // Set up a connection to the server.
 conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
 if err != nil {
  log.Fatalf("did not connect: %v", err)
 }
 defer conn.Close()
 c := lightweight.NewGymClient(conn)

 ctx, cancel := context.WithTimeout(context.Background(), time.Second)
 defer cancel()
 r, err := c.BodyBuilding(ctx, &lightweight.Person{
  Name: "chenqionghe",
  Actions: []string{"深蹲", "卧推", "硬拉"},
 })
 if err != nil {
  log.Fatalf("error: %v", err)
 }
 fmt.Printf("code: %d, msg: %s", r.Code, r.Msg)
}

7. 运行代码

golang目录结果是现在是这样的

.
├── client.go
├── go.mod
├── go.sum
├── lightweight
│ └── gym.pb.go
└── server.go

运行服务端和客户端,效果如下

可以看到,chenqionghe去健身成功

如何调试gRPC

我们都知道rest api是可用curl和postman这样的工具来调试的,grpc也有类似的工具,对应的分别是grpcurl和grpcui

grpcurl-命令行工具

类似curl,可以直接用来发送请求调用远程的grpc服务,官方地址请看grpcurl

安装

brew直接安装

brew install grpcurl

也可以使用go get安装

go get github.com/fullstorydev/grpcurl
go install github.com/fullstorydev/grpcurl/cmd/grpcurl

使用

注意grpcurl是基于反射,需要在启动服务前添加这样一行代码

 reflection.Register(s)

1.查询服务列表

grpcurl -plaintext 127.0.0.1:50051 list

2.查询服务提供的方法

grpcurl -plaintext 127.0.0.1:50051 list lightweight.Gym

3.查看更详细的描述

grpcurl -plaintext 127.0.0.1:50051 describe lightweight.Gym

4.获取类型信息

grpcurl -plaintext 127.0.0.1:50051 describe lightweight.Person

5.调用服务方法

grpcurl -plaintext -d '{"name":"chenqionghe","actions":["深蹲","卧推","硬拉"]}' 127.0.0.1:50051 lightweight.Gym/BodyBuilding

grpcui-界面工具

简单的说,就是gRPC中的postman,能带你飞起来那种,官方地址grpcui

安装

go get github.com/fullstorydev/grpcui
go install github.com/fullstorydev/grpcui/cmd/grpcui

使用

运行web界面,指定grpc的地址

grpcui -plaintext localhost:50051

提示Web UI的地址为http://127.0.0.1:50791
访问出来以下界面

直接把所有服务和方法都列出来了,真的是相当人性化
我们来使用一下,传递参数

返回结果如下

到服务端查看,已经正常的接收到请求