grpc - yaokun123/php-wiki GitHub Wiki
grpc
RPC的全称是Remote Procedure Call,远程过程调用。这是一种协议,是用来屏蔽分布式计算中的各种调用细节,使得你可以像是本地调用一样直接调用一个远程的函数。
而gRPC又是什么呢?用官方的话来说:
A high-performance, open-source universal RPC framework
gRPC是一个高性能的、开源的通用的RPC框架。
在gRPC中,我们称调用方为client,被调用方为server。
跟其他的RPC框架一样,gRPC也是基于”服务定义“的思想。简单的来讲,就是我们通过某种方式来描述一个服务,这种描述方式是语言无关的。在这个”服务定义“的过程中,我们描述了我们提供的服务服务名是什么,有哪些方法可以被调用,这些方法有什么样的入参,有什么样的回参。
也就是说,在定义好了这些服务、这些方法之后,gRPC会屏蔽底层的细节,client只需要直接调用定义好的方法,就能拿到预期的返回结果。对于server端来说,还需要实现我们定义的方法。同样的,gRPC也会帮我们屏蔽底层的细节,我们只需要实现所定义的方法的具体逻辑即可。
你可以发现,在上面的描述过程中,所谓的”服务定义“,就跟定义接口的语义是很接近的。我更愿意理解为这是一种”约定“,双方约定好接口,然后server实现这个接口,client调用这个接口的代理对象。至于其他的细节,交给gRPC。
gRPC使用了Protocol Buffers
你可以把他当成一个代码生成工具以及序列化工具。这个工具可以把我们定义的方法,转换成特定语言的代码。比如你定义了一种类型的参数,他会帮你转换成Golang中的struct 结构体,你定义的方法,他会帮你转换成func 函数。此外,在发送请求和接受响应的时候,这个工具还会完成对应的编码和解码工作,将你即将发送的数据编码成gRPC能够传输的形式,又或者将即将接收到的数据解码为编程语言能够理解的数据格式。
一、环境配置
1.1、gRPC
这一步安装的是gRPC的核心库
go get -u google.golang.org/grpc # 安装 gRPC 框架
1.2、protocol buffers
brew install protobuf
1.3、protoc-gen-go
上一步安装的是protocol编译器。而上文中我们提到了可以生成各种不同语言的代码。因此,除了这个编译器,我们还需要配合各个语言的代码生成工具。
对于Golang来说,称为protoc-gen-go
notice:
不过在这儿有个小小的坑,github.com/golang/protobuf/protoc-gen-go和google.golang.org/protobuf/cmd/protoc-gen-go是不同的。 区别在于前者是旧版本,后者是google接管后的新版本,他们之间的API是不同的,也就是说用于生成的命令,以及生成的文件都是不一样的。
因为这些文件在安装grpc的时候,已经下载下来了,因此使用install命令就可以了,而不需要使用get命令
go install google.golang.org/protobuf/cmd/protoc-gen-go
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc
然后你看你的$GOPATH/bin路径,应该有标1和2的两个文件:
protoc-gen-go
protoc-gen-go-grpc
二、proto文件创建
syntax = "proto3"; // 指定proto版本,这是在说明我们使用的是proto3语法
package hello; // 指定默认包名
option go_package = "hello";// 这部分的内容是关于最后生成的go文件是处在哪个目录哪个包中,.代表在当前目录生成,message代表了生成的go文件的包名是message。
// 定义Hello服务
service Hello {
// 定义SayHello方法
rpc SayHello(HelloRequest) returns (HelloResponse) {}
}
// HelloRequest 请求结构
message HelloRequest {
string name = 1;
}
// HelloResponse 响应结构
message HelloResponse {
string message = 1;
}
执行命令
在编写完上面的内容后,在proto文件所在目录下执行如下命令:
protoc xxx.proto --go_out=.
protoc xxx.proto --go-grpc_out=.
这两条命令会生成如下的两个文件:
xxx.pb.go
xxx_grpc.pb.go
在这两个文件中,包含了我们定义方法的go语言实现,也包含了我们定义的请求与相应的go语言实现。
简单来讲,就是protoc-gen-go已经把你定义的语言无关的message.proto转换为了go语言的代码,以便server和client直接使用。
notice:
到了这一部分你可能会有一些疑惑,在网上的一些教程中,有这样的生成方式:
protoc --go_out=plugins=grpc:. helloworld.proto
这种生成方式,使用的就是github版本的protoc-gen-go,而目前这个项目已经由Google接管了。
并且,如果使用这种生成方式的话,并不会生成上图中的xxx_grpc.pb.go与xxx.pb.go两个文件,只会生成xxx.pb.go这种文件。
三、服务端
我们在server目录下面创建一个server.go文件。
package main
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"grpc-demo/hello"
"net"
)
const Address = "127.0.0.1:50052"
// 定义helloService并实现约定的接口
type helloService struct{}
// HelloService Hello服务
var HelloService = helloService{}
// SayHello 实现Hello服务接口
func (h helloService) SayHello(ctx context.Context, in *hello.HelloRequest) (*hello.HelloResponse, error) {
resp := new(hello.HelloResponse)
resp.Message = fmt.Sprintf("Hello %s.", in.Name)
return resp, nil
}
func main() {
listen, err := net.Listen("tcp", Address)
if err != nil {
grpclog.Fatalf("Failed to listen: %v", err)
}
// 实例化grpc Server
s := grpc.NewServer()
// 注册HelloService
hello.RegisterHelloServer(s, HelloService)
grpclog.Println("Listen on " + Address)
s.Serve(listen)
}
//运行
go run main.go
四、客户端
我们在client目录下面创建一个main.go文件。
package main
import (
"fmt"
"golang.org/x/net/context"
"google.golang.org/grpc"
"google.golang.org/grpc/grpclog"
"grpc-demo/hello"
)
const Address = "127.0.0.1:50052"
func main() {
// 连接
conn, err := grpc.Dial(Address, grpc.WithInsecure())
if err != nil {
grpclog.Fatalln(err)
}
defer conn.Close()
// 初始化客户端
c := hello.NewHelloClient(conn)
// 调用方法
req := &hello.HelloRequest{Name: "gRPC"}
res, err := c.SayHello(context.Background(), req)
if err != nil {
grpclog.Fatalln(err)
}
grpclog.Infoln(res.Message)
fmt.Println(res.Message)
}
//运行
go run main.go