Golang笔记 6.3 RPC 编程之 gRPC

文章目录

    • 前言
    • 1 它是什么
    • 2 为什么是它!
    • 3 详细介绍
      • 服务的定义
      • 使用 API surface
      • 同步与异步
      • RPC 生命周期
    • 4 golang 中怎么用
      • 4.1 安装
      • 4.2 示例 - helloworld
        • 运行
        • proto 文件分析
        • client 源码操作分析
        • server 源码操作分析
      • 4.3 示例修改 - 增加一个新的方法
    • 5 小结
    • END

前言

我正在学习酷酷的 Golang,可点此查看帖子Golang学习笔记汇总。

1 它是什么

gRPC 是一个高性能、开源、通用的 RPC 框架。

A high performance, open-source universal RPC framework.

2 为什么是它!

gRPC is a modern open source high performance RPC framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services.

  • 可运行在任何环境的RPC框架
  • 可以插件式的增加 load balancing, tracing, health checking and authentication 这些支持
  • 也适用于分布式计算的最后一英里

3 详细介绍

先了解几个基本概念,https://grpc.io/docs/guides/concepts/

服务的定义

gRPC 默认使用 protocol buffers 作为接口描述语言,用它来描述服务端接口及消息格式。

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

message HelloRequest {
  string greeting = 1;
}

message HelloResponse {
  string reply = 1;
}

gRPC 允许你定义四类服务方法:

  • 单次 RPC,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用;

    rpc SayHello(HelloRequest) returns (HelloResponse){
    }
    
  • 服务端流式 RPC,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止;

    rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){
    }
    
  • 客户端流式 RPC,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答;

    rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) {
    }
    
  • 双向流式 RPC,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持;

    rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){
    }
    

使用 API surface

gRPC提供了 protocol buffer 编译器插件,可生成客户端和服务器端代码。 gRPC用户通常在客户端(stub)调用这些API,并在服务器端实现相应的API。

同步与异步

阻塞的同步RPC调用直到服务器收到响应为止是最接近RPC所追求的过程调用抽象的近似方法。 另一方面,网络本质上是异步的,并且在许多情况下能够启动RPC而不阻塞当前线程很有用。

gRPC编程都有同步和异步两种形式。

RPC 生命周期

4 golang 中怎么用

参考 https://grpc.io/docs/quickstart/go/

4.1 安装

安装 gRPC

$ go get -u google.golang.org/grpc

安装 PROTOC 编译器

3.7.1 版本安装示例:

$ PROTOC_ZIP=protoc-3.7.1-linux-x86_64.zip
$ curl -OL https://github.com/google/protobuf/releases/download/v3.7.1/$PROTOC_ZIP
$ sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc
$ rm -f $PROTOC_ZIP

3.10.1 版本安装示例:

PROTOC_ZIP=protoc-3.10.1-linux-x86_64.zip
curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v3.10.1/$PROTOC_ZIP
sudo unzip -o $PROTOC_ZIP -d /usr/local bin/protoc
rm -f $PROTOC_ZIP

安装 protoc 的 go 插件

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

protoc-gen-go 将会安装在 $GOBIN, 默认是 $GOPATH/bin. 需要在 $PATH 找到编译器。

$ export PATH=$PATH:$GOPATH/bin

4.2 示例 - helloworld

运行

$ cd $GOPATH/src/google.golang.org/grpc/examples/helloworld

开两个终端,分别执行 server 和 client:

$ go run greeter_server/main.go
$ go run greeter_client/main.go

这样执行之后,终端上就有日志打印出来了:

# go run greeter_client/main.go
2019/11/04 12:21:47 Greeting: Hello world
# go run greeter_server/main.go
2019/11/04 12:21:47 Received: world

proto 文件分析

// 1 定义服务
service Greeter {
  // 2 定义服务中的方法
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// 3 定义方法中的请求消息格式
message HelloRequest {
  string name = 1;
}

// 4 定义方法中的回复消息格式
message HelloReply {
  string message = 1;
}

client 源码操作分析

  // Step1. grpc 连接
	conn, err := grpc.Dial(address, grpc.WithInsecure())
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
  defer conn.Close()

  // Step2. 新建客户端    c := pb.New(ServiceName)Client(conn)
  c := pb.NewGreeterClient(conn)

	// Step3. 准备 context 用于 grpc 请求
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  defer cancel()
  
  // Step4. 客户端调用预先定义的方法,并传入方法对应的请求  c.(MethodName)(ctx, &pb.(RequestMessageName))
	r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})

server 源码操作分析

  // Step1. 配置监听的协议和端口
	lis, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
  }
  
  // Step2. 新建 grpc 服务端
  s := grpc.NewServer()
  
  // Step3. 将 pb消息处理 的实例进行注册
  pb.RegisterGreeterServer(s, &server{})
  
  // Step4. 启动监听服务
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
  }

除了这4步常规操作之外,更重要的是定义 pb消息处理 的实例:

// server is used to implement helloworld.GreeterServer.
type server struct{}

// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.Name)
	return &pb.HelloReply{Message: "Hello " + in.Name}, nil
}

4.3 示例修改 - 增加一个新的方法

在 pb 中增加新的方法

service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}

使用 protoc 自动更新代码

protoc -I helloworld/ helloworld/helloworld.proto --go_out=plugins=grpc:helloworld

在 greeter_server/main.go 中更新 server 代码

func (s *server) SayHelloAgain(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
        return &pb.HelloReply{Message: "Hello again " + in.GetName()}, nil
}

在 greeter_client/main.go 中更新 client 代码

r, err = c.SayHelloAgain(ctx, &pb.HelloRequest{Name: name})
if err != nil {
        log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", r.GetMessage())

运行结果

$ go run greeter_client/main.go
Greeting: Hello world
Greeting: Hello again world

5 小结

  1. 在 .proto 文件中定义服务
  2. 使用 protocol buffer 编译器产生 服务端和客户端 代码
  3. 使用 Go gRPC API 为你的服务编写客户端和服务端

本篇笔记演示了如何使用 gRPC 框架,包括 proto 文件定义服务,使用 pb 编译器产生代码,最后使用 gRPC API 编写 client 和 server 代码。

END


你可能感兴趣的:(开发,-,Golang,go,golang,gRPC)