引言

Go原生方法实现RPC 文章中,我们通过原生的方法实现了 RPC 调用。但是大多是基于 protobuf 进行 RPC 的实现。

gRPC是Google公司基于Protobuf开发的跨语言的开源RPC框架。gRPC基于HTTP/2协议设计,可以基于一个HTTP/2链接提供多个服务,对于移动设备更加友好。本节将讲述gRPC的简单用法。

gRPC技术栈:

grpc技术栈

最底层为TCP或Unix Socket协议,在此之上是HTTP/2协议的实现,然后在HTTP/2协议之上又构建了针对Go语言的gRPC核心库。应用程序通过gRPC插件生产的Stub代码和gRPC核心库通信,也可以直接和gRPC核心库通信。

参考文献

protobuf入门学习:https://github.com/mailjobblog/dev_go/tree/master/220115_protobuf
Go原生RPC+protobuf代码下载:https://github.com/mailjobblog/dev_go/tree/master/220113_rpc/3.rpc_protobuf
gRPC代码下载:https://github.com/mailjobblog/dev_go/tree/master/220113_rpc/4.grpc

RPC实现

Go原生rpc+proto实现

代码实现

hello.proto

syntax = "proto3";
package pb;
option go_package="./pb;pb";

// 请求结构体
message HelloRequest {
  string res = 1;
}

// 返回结构体
message HelloResponse {
  int64 reply = 1;
}

server.go

func main() {
	rpc.Register(new(HelloService))
	listener, err := net.Listen("tcp", ":8888")
	if err != nil {
		log.Fatal("ListenTCP error:", err)
	}
	for {
		conn, err := listener.Accept()
		if err != nil {
			log.Fatal("Accept error:", err)
		}
		go rpc.ServeConn(conn)
	}
}

type HelloService struct{}

// Length 和原生相比,这里的接收参数和返回参数都用的是proto生成的代码
func (h *HelloService) Length(res pb.HelloRequest, reply *pb.HelloResponse) error {
	reply.Reply = int64(len(res.Res))
	return nil
}

client_test.go

func TestClient(t *testing.T) {
	client, err := rpc.Dial("tcp", "127.0.0.1:8888")
	if err != nil {
		log.Fatal("dialing:", err)
	}

	// 定义请求和接受参数
	// 接收参数和返回参数都用的是proto生成的代码
	res := pb.HelloRequest{Res: "test666"}
	var reply pb.HelloResponse

	err = client.Call("HelloService.Length", res, &reply)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(reply)
}

gRPC实现

代码实现

hello.proto

syntax = "proto3";
package pb;
option go_package="./pb;pb";

// 请求结构体
message HelloRequest {
  string res = 1;
}

// 返回结构体
message HelloResponse {
  int64 reply = 1;
}

// GRPC服务
service HelloService {
  // 计算字符串长度
  rpc Length(HelloRequest) returns (HelloResponse);
}

server.go

func main() {
	// 创建一个 grpc server
	grpcServer := grpc.NewServer()
	// 注册 grpc
	pb.RegisterHelloServiceServer(grpcServer, new(HelloService))

	lis, err := net.Listen("tcp", ":8888")
	if err != nil {
		log.Fatal(err)
	}
	// 监听端口上提供gRPC服务
	grpcServer.Serve(lis)
}

type HelloService struct {
	pb.UnimplementedHelloServiceServer
}

func (h *HelloService) Length(ctx context.Context, res *pb.HelloRequest) (*pb.HelloResponse, error) {
	reply := &pb.HelloResponse{Reply: int64(len(res.Res))}
	return reply, nil
}

client_test.go

func TestClient(t *testing.T) {
	conn, err := grpc.Dial("127.0.0.1:8888", grpc.WithTransportCredentials(insecure.NewCredentials()))
	if err != nil {
		log.Fatal(err)
	}
	defer conn.Close()
	client := pb.NewHelloServiceClient(conn)

	reply, err := client.Length(context.Background(), &pb.HelloRequest{Res: "test123456"})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(reply.Reply)
	fmt.Println(reply.GetReply())
}