RPC

RPC(Remote Procedure Call: 远程过程调用)是一个计算机通信协议,该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。

gRPC

gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。()

在 gRPC 里客户端应用可以像调用本地对象一样直接调用另一台不同的机器上服务端应用的方法,使得您能够更容易地创建分布式应用和服务。与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根能够像服务端一样的方法。

gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。

使用gRPC分为三步

  • 编写.proto文件
  • 利用工具将.proto文件生成对应语言的代码
  • 根据生成的代码编写服务端和客户端的代码

开始之前

首先我们需要安装将.proto文件生成对应代码的工具,下载地址(),下载你对应操作系统的压缩包即可。下载完成后解压将其bin目录下的可执行文件放入环境变量中的文件夹即可。

此外,我们还需要安装对应语言的插件,比如

go语言插件安装方式

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

python插件安装方式

pip install grpcio
pip install protobuf
pip install grpcio_tools

接下来我将以一个例子来做演示介绍如何在go中使用gRPC,注意我将采用go module的方式来编写这个demo,

hello_grpchello_grpc
module "hello_grpc"

然后用你喜欢的IDE打开这个文件夹,进行之后的操作

1. 编写.proto文件

pbhello_grpc.proto
syntax = "proto3";
​
package service;
​
option go_package = ".;hello_grpc";
​
service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}
​
message HelloRequest {
  string name = 1;
}
​
message HelloReply {
  string message = 1;
}

其中第一行指定了我们使用 protocol buffers的版本

下面我们定义了包的名称,这将成为后面我们生成的go语言的代码的包名

然后我们定义了一个服务名为Greeter,其中定义了一个函数SayHello它的参数定义在HelloRequest,返回值定义在HelloReply

关于proto的服务,一共有4种类型,此例子中是最简单的一种

streamstreamstream


2. 生成对应语言的代码

service
protoc -I pb/ pb/hello_grpc.proto --go_out=plugins=grpc:service
-I
--go_out=plugins=grpc:
hello_grpc.pb.go

打开这个文件发现抱错,原因是我们的项目中还没有安装相应的包,输入以下命令安装

go mod tidy

3.编写服务端和客户端的代码

服务端

serverserver.go
package main
​
import (
    "context"
    "fmt"
    "hello_grpc/service"
    "google.golang.org/grpc"
    "google.golang.org/grpc/reflection"
    "net"
)
​
type server struct {}
​
func (s *server) SayHello(ctx context.Context, in *service.HelloRequest) (*service.HelloReply, error) {
    return &service.HelloReply{Message: "hello " + in.Name}, nil
}
​
func main() {
    // 监听本地端口
    lis, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Printf("监听端口失败: %s", err)
        return
    }
​
    // 创建gRPC服务器
    s := grpc.NewServer()
    // 注册服务
    service.RegisterGreeterServer(s, &server{})
​
    reflection.Register(s)
​
    err = s.Serve(lis)
    if err != nil {
        fmt.Printf("开启服务失败: %s", err)
        return
    }
}
GreeterServer
type GreeterServer interface {
    SayHello(context.Context, *HelloRequest) (*HelloReply, error)
}

服务端

client/client.go
package main
​
import (
    "context"
    "fmt"
    "hello_grpc/service"
    "google.golang.org/grpc"
)
​
func main(){
    // 连接服务器
    conn, err := grpc.Dial(":8080", grpc.WithInsecure())
    if err != nil {
        fmt.Printf("连接服务端失败: %s", err)
        return
    }
    defer conn.Close()
​
    // 新建一个客户端
    c := service.NewGreeterClient(conn)
​
    // 调用服务端函数
    r, err := c.SayHello(context.Background(), &service.HelloRequest{Name: "horika"})
    if err != nil {
        fmt.Printf("调用服务端代码失败: %s", err)
        return
    }
​
    fmt.Printf("调用成功: %s", r.Message)
}

4. 运行

  • 先运行服务端
    go run server/server.go
  • 开新的终端运行客户端
    go run client/client.go
  • 观察客户端输出

5. 番外篇:编写跨语言调用

以python为例,这里就不编写python的服务端了,直接编写python的客户端调用go的服务端

注意:你是否安装python的插件?

pip install grpcio
pip install protobuf
pip install grpcio_tools


生成python代码

python
python -m grpc_tools.protoc -I pb/ --python_out=python/ --grpc_python_out=python/ pb/hello_grpc.proto
hello_grpc_pb2.pyhello_grpc_pb2_grpc.py

编写python客户端代码

在python文件夹中新建client.py写入

import logging

import grpc

import hello_grpc_pb2
import hello_grpc_pb2_grpc

def run():
    with grpc.insecure_channel('localhost:8080') as channel:
        stub = hello_grpc_pb2_grpc.GreeterStub(channel)
        response = stub.SayHello(hello_grpc_pb2.HelloRequest(name='horika'))
    print("调用成功: {}!".format(response.message))

if __name__ == '__main__':
    logging.basicConfig()
    run()

运行go的服务端,然后运行python客户端查看输出