gRPC简介和实践

简介

  gRPC是Google主导的一个高性能,跨语言的RPC(远程过程调用)框架。官网: https://grpc.io/
  支持多种常见的流行编程语言,如C++ Java Node.js PHP Python等等。 其实从本质上来看到gRPC其实是基于使用protobuf做为数据传输格式,借助HTTP2协议通信做的一个RPC框架,只要任何语言实现了gRPC的规范即可以实现跨平台调用。类比Restful Api使用json作为数据传输格式,over HTTP1.1进行通信,大致也就这么回事,没想象中那么复杂。

  跨语言和跨平台很大功劳在于Google推出的这个protobuf数据传输格式。我们可以把它看做和json xml一类作用的东西。
protobuf数据格式被编译后形成二进制数据,相对json等传输格式,解析效率高,压缩体积小,并且能够明确传递数据的数据类型等等。

1.什么是protobuf?

  详细信息可以参考官方文档与介绍: https://developers.google.com/protocol-buffers/

2.实践

  采用PHP作为client, Golang作为Server端模拟一个用户登录过程。(PHP目前只支持作为客户端的角色实现。我们知道由于PHP语言特性本身不支持内置提供强大的HTTP服务,借助于fasgic与nginx或者apache等工作,所以这个也是不支持作为gRPC server服务端的主要原因。)

  其实工作顺序大致分为下面几个步骤:

1.编写 protobuf文件,定义好rpc接口 参数 以及返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
syntax = "proto3";

package Lib.Grpc; // grpc包 在php中也是namespace=> \Lib\Grpc\

option go_package = "protobuf"; // golang中包名

// 成功响应数据
message ApiResponse {
uint32 Code = 1;
string msg = 2;
}

// 附加信息
message LoginLogData {
string OpName = 1;
string OpAddress = 2;
string OpIp = 3;
}

// 登录表单
message LoginFormData {
string UserName = 1;
string PassWord = 2;
LoginLogData log = 3;
}

// welcome接口
service WelcomeApi {
// 用户登录rpc接口
rpc DoLogin(LoginFormData) returns (ApiResponse);
}

2.使用protoc工具将上面的protobuf文件转化为符合gRPC的PHP代码以及Go代码

1
2
3
4
5
6
7
8
9
10
11
#预备知识:   protobuf知识得了解 以及 protoc生成各种语言的插件集成
#生成go服务端的protobuf代码
protoc --go_out=plugins=grpc:../backend-go/lib/protobuf/ *.proto

#生成php端的protobuf代码
protoc --php_out=../frontend-php/application/ --grpc_out=../frontend-php/application/ --plugin=protoc-gen-grpc=./bin/macos/grpc_php_plugin .

#每次修改protobuf文件内容或者新增protobuf文件的时候
#需要执行以下上面的2个命令这样同步protobuf转化代码
#接下来的事情不用我们管了,直接在代码中new使用我们定义好的对象就行,protoc已经帮我们转化代码了
#对于Go还好 对于PHP记得自己增加psr-4的目录声明 否则autoload不起作用,导入不了正确的文件

3.PHP客户端要做的事情

1
2
1. pecl install grpc  #安装grpc扩展
2. composer require grpc/grpc google/protobuf #安装这2个composer包 一个是处理基于grpc扩展的composer包 一个是处理protobuf的composer包

4.启动Go服务端代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

// WelcomeApi服务的实现
type WelcomeApi struct{
protobuf.UnimplementedWelcomeApiServer
}

// 登录
func (*WelcomeApi) DoLogin(ctx context.Context, req *protobuf.LoginFormData) (*protobuf.ApiResponse, error) {

user := models.Users{}

models.DB.First(&user, "user_name = ?", req.UserName)

response := protobuf.ApiResponse{}

if user.Id == 0 {
response.Code = 3001
response.Msg = "用户名或密码错误"
return &response,nil
}else{
if user.PassWord == Md5(req.PassWord) {
response.Code = 0
response.Msg = "登录成功"

go func() { // 日志直接异步 go 不阻塞
// 记录登录日志
loginLog := models.UserLogs{}
loginLog.OpName = req.Log.OpName
loginLog.OpAddress = req.Log.OpAddress
loginLog.OpIp = req.Log.OpIp
loginLog.Uid = user.Id
models.DB.Create(&loginLog)
}()

}else{
response.Code = 3001;
response.Msg = "用户名或密码错误"
}
return &response,nil
}
}

func main() {
lis, err := net.Listen("tcp", ":30081")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
protobuf.RegisterWelcomeApiServer(s, &WelcomeApi{}) //注册service路由

reflection.Register(s) // 反射
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

5.PHP客户端调用情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

//开始执行登录
$loginFrom = new \Lib\Grpc\LoginFormData();
$loginFrom->setUserName($user_name);
$loginFrom->setPassWord($pass_word);

// 操作日志
$address = getAddressByIp($_SERVER['REMOTE_ADDR']);
$loginLog = $this->saveOpLog("登录");
$loginFrom->setLog($loginLog);

// grpc客户端
$client = new \Lib\Grpc\WelcomeApiClient('192.168.2.136:30081',[
'credentials' => \Grpc\ChannelCredentials::createInsecure()
]);

// 设置超时时间
$client->waitForReady(3000000); //微秒
list($resp,$status) = $client->DoLogin($loginFrom)->wait(); // 像本地一样调用

if ($status->code == 0) { // 代表接口执行成功
if ($resp instanceof \Lib\Grpc\ApiResponse) {
$success = ['code'=>$resp->getCode(),'msg'=>$resp->getMsg()];
echo json_encode($success);
}
}else{ // 接口执行失败
throw new Exception($status->details,$status->code);
}

3.调试工具推荐

  我们在普通开发Restful Api的时候常常借助于Postman等工具来调试接口,以及无界面的工具
curl等。 那么我们开发gRPC项目的时候,是不是也有类似的产品或者工具呢?肯定是有的。

  GUI工具,类似Postman=>bloomrpc: https://github.com/uw-labs/bloomrpc

  UI效果还不错,值得一试。导入protobuf,填写好调用的server端信息即可调试了。

  命令行工具,类似curl=>grpcurl: https://github.com/fullstorydev/grpcurl