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

参考:https://www.cnblogs.com/hongjijun/p/13724738.html