Go语言框架之RPC | 青训营

72 阅读4分钟

Go语言框架之RPC使用入门

RPC介绍

RPCRemote Procedure Call的简称,中文名为远程过程调用。通过远程过程调用,客户端可调用另一个应用所自定义的函数,并拿到数据的返回的内容,并且不需要关系底层的实现细节。简单的说,就是通过使用RPC,能做到调用远程方法和调用本地方法一样。 go语言自带有原生的rpc,但它的缺点十分明显,不太好用:

  • 编写相对复杂,需要自己去关注实现过程
  • 缺少代码提示

而gRPC是由 google开发的一个高性能、通用的开源RPC框架。它基于ProtoBuf序列化协议和HTTP2协议,支持多种语言。

利用gRPC,我们可以将以往的程序的单体架构改造成微服务架构,这样就可以按照服务进行单独的扩容和升级,各个服务直接也可以独立开发和部署,项目的开发者也不再需要了解整个系统的实现细节和流程。但是微服务架构也会存在代码冗余等问题。

安装准备

首先需要在Windows上安装protoc转换工具。 可以通过下面链接进行下载。github.com/protocolbuf… 下载解压完毕之后,还需要将protoc的bin目录添加到环境变量中。如何添加不再赘述。 还需要安装go的依赖 go install github.com/golang/protobuf/protoc-gen-go 同时也要将protoc-gen-go添加到环境变量当中。

protobuf文件

syntax = "proto3"; // 指定proto版本
package hello;     // 指定默认包名

// 指定golang包名
option go_package = "/hello";

service HelloService {
  rpc SayHello (HelloRequest) returns (HelloResponse) {}
}

message HelloRequest {
  string name = 1;
  string message = 2;
}

message HelloResponse{
  string name = 1;
  string message = 2;
}

然后在当前目录下执行:

protoc -I . --go_out=plugins=grpc:. .\hello.proto

之后它就会自动生成一个hello文件夹和一个hello.pb.go文件。

proto中,service就等于go中的接口,rpc就是结构体中的方法,而message对应的便是结构体。

proto有许多种基本数据类型,它们和go语言中的数据类型对应如下

.proto Type解释Go Type
doublefloat64
floatfloat32
int32使用变长编码,对于负值的效率很低int32
uint32使用变长编码uint32
uint64使用变长编码uint64
sint32使用变长编码,在负值时比int32高效的多int32
sint64使用变长编码,比通常的int64高效int64
fixed32总是4个字节uint32
fixed64总是8个字节uint64
sfixed32总是4个字节int32
sfixed64总是8个字节int64
boolbool
string一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本string
bytes可能包含任意顺序的字节数据[]byte

复合类型

  • []int64 --> repeated int64
  • []string --> repeated string
  • RequestList []*Request --> repeated Request request_list
  • A map[int64]string --> map<int64, string> a

服务端编写

首先编写一个结构体,需要实现protobuf中的定义的所有方法。之后再监听端口,注册服务。

type HelloServer struct {}

func (HelloServer) SayHello(ctx context.Context, request *hello.HelloRequest) (res *hello.HelloResponse, err error) {
  return &hello.HelloResponse{
      Name: "xx",
      Message: "OK",
  }, nil
}

SayHello便是之前proto文件中定义的方法。这个方法传入参数一般都为context上下文环境和在proto文件中的请求。而返回一般为响应结构体的指针和错误。

然后需要在main函数中启动RPC服务


func main() {
  listen, err := net.Listen("tcp", ":8080")
  if err != nil {
    return
  }
  
  s := grpc.NewServer()
  server := HelloServer{}
  grpc.RegisterHelloServiceServer(s, &server)
  err = s.Serve(listen)
}
  • net.Listen()的作用是启动监听端口

  • grpc.NewServer()创建一个gRPC服务器的实例

  • grpc.RegisterHelloServiceServer(s, &server),这个函数将server结构体注册为gRPC服务。

  • s.Serve(listen),开始来处理来自客户端的请求。

客户端编写

客户端的编写方法和服务端相对应,首先需要和客户端建立连接,之后就可以调用服务端的方法了。

func main() {
  addr := ":8080"
  conn, err := grpc.Dial(":8080", grpc.WithTransportCredentials(insecure.NewCredentials()))
  
  if err != nil {
    return
  }
  defer conn.Close()
  client := hello.NewHelloServiceClient(conn)
  result, err := client.SayHello(context.Background(), &hello.HelloRequest{
    Name:    "xx",
    Message: "OK",
  })
}

使用 grpc.Dial创建一个到指定地址的 gRPC 连接,并且使用不安全的证书来实现 SSL/TLC连接。 通过hello.NewHelloServiceClient(conn)来初始化客户端,之后就可以直接调用服务端实现的函数了。最后不要忘记使用defer conn.Close()确保关闭了连接。