本文通过一个示例介绍gRPC服务开发过程,示例创建两个项目,一个服务端,一个客户端。

完整代码参见 grpc-testing v0.0.1

初始化结构

$ mkdir -p grpc-testing-{server,client}/api
$ touch grpc-testing-{server,client}/{main.go,generate.go} grpc-testing-server/api/echo.proto 
$ cd grpc-testing-server
$ go mod init app/grpc-testing-server
$ cd ../grpc-testing-client
$ go mod init app/grpc-testing-client

两个项目共同的部分

*/generate.go

//go:generate sh -c "protoc --go_out=. --go-grpc_out=. api/*.proto"
package main

*/api/echo.proto

syntax="proto3";

package echo;

option go_package="api/echo";

message EchoRequest {
    string msg = 1;
}

message EchoResponse {
    string msg = 1;
}

service echo {
    rpc Echo(EchoRequest) returns (EchoResponse) {}
}

这里定义了两个 message 和一个 RPC 服务

生成 gRPC 代码

$ cd grpc-testing-server
$ go generate ./...
$ cd ../grpc-testing-client
$go generate ./...

服务端

代码

grpc-testing-server/main.go

package main

import (
	"app/grpc-testing-server/api/echo"
	"context"
	"log"
	"net"

	"google.golang.org/grpc"
)

type EchoServer struct {
	echo.UnimplementedEchoServer
}

func (e *EchoServer) Echo(ctx context.Context, par *echo.EchoRequest) (*echo.EchoResponse, error) {
	result := &echo.EchoResponse{
		Msg: par.GetMsg(),
	}
	return result, nil
}

func main() {
	log.Println("Go")
	listen, err := net.Listen("tcp", ":8080")
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	echo.RegisterEchoServer(s, &EchoServer{})
	if err := s.Serve(listen); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

EchoServer 实现了 protoc 生成的 EchoServer 接口 ( 接口定义在 echo_grpc.pb.go 中) 。主要的业务方法 Echo 实现了具体业务

echo.RegisterEchoServer(s, &EchoServer{}) 将实现类注册到 grpc Server

编译服务端

$ cd grpc-testing-server
$ go mod tidy
$ go build

客户端

代码

grpc-testing-client/main.go

package main

import (
	"app/grpc-testing-client/api/echo"
	"context"
	"log"

	"google.golang.org/grpc"
)

func main() {
	conn, err := grpc.Dial("127.0.0.1:8080", grpc.WithInsecure(), grpc.WithBlock())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer conn.Close()
	c := echo.NewEchoClient(conn)
	ctx, cancle := context.WithCancel(context.Background())
	defer cancle()
	r, err := c.Echo(ctx, &echo.EchoRequest{Msg: "hello"})
	if err != nil {
		log.Fatalf("echo failed :%#v", err)
	}
	log.Print(r.GetMsg())
}

编译客户端

$ cd grpc-testing-client
$ go mod tidy
$ go build

测试

开启两个终端

终端一

$ cd grpc-testing-server 
$ ./grpc-testing-server

终端二

$ cd grpc-testing-client
$ ./grpc-testing-client

总结

使用Go语言实现 gRPC 服务的过程如下:

服务端

  • 安装环境
  • 编写 proto 文件
  • 生成 Go 文件
  • 实现业务方法
  • 创建gRPC server ( grpc.NewServer()
  • 调用生成的注册方法( RegisterXXXXXX )注册实现
  • 调用 Serve 方法启动服务

客户端

  • 创建连接 grpc.Dial
  • 创建 Client
  • 调用实际的 gRPC 方法