gRPC入坑记

Posted 飞鸿影的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了gRPC入坑记相关的知识,希望对你有一定的参考价值。

概要

由于gRPC主要是谷歌开发的,由于一些已知的原因,gRPC跑demo还是不那么顺利的。单独写这一篇,主要是gRPC安装过程中的坑太多了,记录下来让大家少走弯路。

主要的坑:

  • 如果使用php、Python开发gRPC的客户端,需要编译gRPC命令行工具,生成proto的代码生成插件,否则proto里定义的service无法编译出来。编译需要使用GCC4.8级以上版本,否则报不支持C++11。然后需要龟速下周grpc源码,并下载一大堆第三方依赖。这个过程非常痛苦。使用golang、java的可以忽略。

  • PHP还需要按照grpc的c扩展。编译需要使用GCC4.8级以上版本。

  • 如果使用golang开发服务,依赖的第三方服务基本是下载不下来的,需要使用 go mod增加映射规则到github仓库,github下载也是龟速。

升级GCC

gRPC命令行工具编译需要使用 GCC4.8及以上版本。CentOS6系列的内置版本是GCC4.7。

使用 gcc--version可以查看版本。

如果你的系统GCC版本>=4.8,可以忽略本节。如果仅使用golang、java,请忽略本节。

注:不建议大家下载GCC源码包或者使用yum下载GCC4.8及以上版本,原因:1) 源码包安装真的是非常非常的慢 2) yum 源下载速度慢的像蜗牛。下面的SCL安装方法是推荐大家用的,安装好后原来的版本还能用。

如果需要升级gcc至4.8或更高版本,建议直接采用安装 SCL源之后安装 devtoolset-6(devtoolset-6目前gcc版本为6.3),因为 devtoolset-4及之前的版本都已经结束支持,只能通过其他方法安装。

升级到gcc 6.3:

 
   
   
 
  1. yum -y install centos-release-scl

  2. yum -y install devtoolset-6-gcc devtoolset-6-gcc-c++ devtoolset-6-binutils

  3. scl enable devtoolset-6 bash

需要注意的是scl命令启用只是临时的,退出shell或重启就会恢复原系统gcc版本。如果要长期使用gcc 6.3的话:

 
   
   
 
  1. echo "source /opt/rh/devtoolset-6/enable" >>/etc/profile

这样退出shell重新打开就是新版的gcc了。其它版本同理。

升级到gcc 7.3:

 
   
   
 
  1. yum -y install centos-release-scl

  2. yum -y install devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-binutils

  3. scl enable devtoolset-7 bash

已经停止支持的devtoolset4(gcc 5.2)及之前版本的安装方法,可能比较慢,大家感兴趣的话可以尝试。

升级到gcc 4.8:

 
   
   
 
  1. wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtoolset-2.repo

  2. yum -y install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils

  3. scl enable devtoolset-2 bash

升级到gcc4.9:

 
   
   
 
  1. wget https://copr.fedoraproject.org/coprs/rhscl/devtoolset-3/repo/epel-6/rhscl-devtoolset-3-epel-6.repo -O /etc/yum.repos.d/devtoolset-3.repo

  2. yum -y install devtoolset-3-gcc devtoolset-3-gcc-c++ devtoolset-3-binutils

  3. scl enable devtoolset-3 bash

升级到gcc 5.2:

 
   
   
 
  1. wget https://copr.fedoraproject.org/coprs/hhorak/devtoolset-4-rebuild-bootstrap/repo/epel-6/hhorak-devtoolset-4-rebuild-bootstrap-epel-6.repo -O /etc/yum.repos.d/devtoolset-4.repo

  2. yum install devtoolset-4-gcc devtoolset-4-gcc-c++ devtoolset-4-binutils -y

  3. scl enable devtoolset-4 bash

编译gRPC命令行工具

如果仅使用golang、java,请忽略本节。

gRPC分C、JAVA、GO、NodeJS版本,C版本包括C++, Python, Ruby, Objective-C, PHP, C#,这些语言都是基于C版本开发的,共用代码库一个代码库。

  • C版本:https://github.com/grpc/grpc

  • JAVA版本:https://github.com/grpc/grpc-java

  • GO版本:https://github.com/grpc/grpc-go

  • Node版本:https://github.com/grpc/grpc-node

如果使用C版本的gRPC,最终要从源码里编译出下列工具:

 
   
   
 
  1. grpc_cpp_plugin

  2. grpc_csharp_plugin

  3. grpc_node_plugin

  4. grpc_objective_c_plugin

  5. grpc_php_plugin

  6. grpc_python_plugin

  7. grpc_ruby_plugin

这些工具作为插件供proto编译器使用。需要先下载 grpc/grpc github上的源码。

 
   
   
 
  1. git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc

  2. cd grpc

  3. git submodule update --init

  4. make && sudo make install


  5. # 生成的插件路径

  6. ll ./bins/opt/


  7. # 复制到bin目录

  8. cp -r ./bins/opt/* /usr/local/bin/

 
   
   
 
  1. git clone https://gitee.com/mirrors/grpc-framework grpc

这样下载速度提高了不少。
2、 git submodule update这个命令实际就是在下载 .gitmodules文件里定义的第三方依赖项到third_party目录,这个依赖项有很多,大家可以打开 .gitmodules文件查看下详情。依赖的仓库都在github上,下载没几个小时是下载不下来的,就等着慢慢下载吧。

回头想想,我们花费了很多时间,结果只是为了得到grpc的proto编译插件。

福利:Mac下已编译完成的二进制包:https://files.cnblogs.com/files/52fhy/bins.tar.gz 。下载以上文件解压,将 bins/opt/里的所有文件复制到 /usr/local/bin/

PHP相关支持

如果仅使用golang、java,请忽略本节。

PHP暂时不支持作为grpc的服务端。作为客户端是可以的,需要机器安装:

  • protoc编译工具

  • protobuf c扩展

  • gRPC命令行工具(grpcphpplugin)

  • grpc c扩展

  • grpc php库

其中 protocprotobuf c扩展已经在 Protobuf 小试牛刀 介绍过了,这里不再赘述。上一小节里如果安装成功,那么 grpc_php_plugin也是有了的。下面介绍如何安装PHP版的gRPC库。

安装grpc c扩展:要求:GCC编译器需要4.8及以上版本。可以使用pecl安装:

 
   
   
 
  1. pecl install grpc

也可以指定版本:

 
   
   
 
  1. pecl install grpc-1.12.0

或者下载源码(http://pecl.php.net/package/grpc)安装:

 
   
   
 
  1. wget http://pecl.php.net/get/grpc-1.21.3.tgz

  2. tar zxvf grpc-1.21.3.tgz && cd grpc-1.21.3

  3. phpize

  4. ./configure

  5. make

  6. make install

grpc/grpc代码库里也有PHP扩展的C源码,在 grpc/src/php/ext/grpc目录,进去也可以直接编译。

编译完成后在php.ini里添加,使用 php--ri grpc可以查看信息。

安装完C扩展后,还需要使用composer安装grpc的库:

 
   
   
 
  1. composer require grpc/grpc

gRPC示例

编写gRPC proto

一共定义了三个文件:

 
   
   
 
  1. └── proto

  2. ├── GreeterService.proto

  3. ├── Response.proto

  4. └── User.proto

其中 User 作为 Model定义, Response 用于 RPC统一返回定义, GreeterService 则是服务接口定义。

限于篇幅,proto文件详见 https://github.com/52fhy/grpc-sample 仓库的 proto目录。

GreeterService.proto文件内容如下:

 
   
   
 
  1. syntax = "proto3";

  2. package Sample.Model; //namesapce


  3. import "User.proto";

  4. import "Response.proto";


  5. service Greeter {

  6. // Sends a greeting

  7. rpc SayHello (User) returns (Response) {}

  8. }

这里面定义了一个 service,相当于定义了一个服务接口,我们把方法名、参数定义好了,后面需要去实现它。由于gRPC不支持PHP作为服务端,这里我们使用Golang作为服务端。

首先需要使用proto工具编译出golang的代码:

 
   
   
 
  1. mkdir -p Pb_Go


  2. #编译

  3. cd proto

  4. protoc --go_out=plugins=grpc:../Pb_Go/ *.proto

  5. cd -

如果提示 protoc-gen-go找不到,请根据文章介绍(https://www.cnblogs.com/52fhy/p/11106670.html#autoid-2-0-0)进行安装。

执行成功,会在 Pb_Go目录里生成Go代码:

 
   
   
 
  1. Pb_Go

  2. ├── GreeterService.pb.go

  3. ├── Response.pb.go

  4. └── User.pb.go

如果需要生成PHP客户端的代码,则需要使用grpc php的命令行工具 grpc_php_plugin,前面小结如果执行成功,这个工具已经有了。然后:

 
   
   
 
  1. out=output/php

  2. mkdir -p $out


  3. #编译

  4. cd proto

  5. protoc --php_out=../$out --grpc_out=../$out --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin *.proto

  6. cd -


  7. # 修改命名空间

  8. cd $out

  9. mv GPBMetadata Sample/Model/

  10. find . -name '*.php' ! -name example.php -exec sed -i "" -e 's#GPBMetadata#Sample\\Model\\GPBMetadata#g' -e 's#\\Sample\\Model\\GPBMetadata\\Google#\\GPBMetadata\\Google#g' {} \;

上面是在Mac下操作的,命令和Linux有些不同。CentOS下gRPC编译工具未编译。

最终生成的文件:

 
   
   
 
  1. ├── output

  2.    └── php

  3.    └── Sample

  4.    └── Model

  5.    ├── GPBMetadata

  6.       ├── GreeterService.php

  7.       ├── Response.php

  8.       └── User.php

  9.    ├── GreeterClient.php

  10.    ├── Response.php

  11.    ├── User.php

  12.    └── UserList.php

注意:编译那里如果我们不加 --grpc_out=../$out--plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin,生成的PHP类是没有 GreeterClient的。这个文件是gRPC编译工具自动生成的,用于连接gRPC服务端。

go编写服务

我们用Golang写服务端。上面虽然生成了Golang的部分代码,但真正的服务还没有写呢。

main.go

首先我们新建个main.go,代码不多,我直接贴出来:

 
   
   
 
  1. package main


  2. import (

  3. "fmt"

  4. "log"

  5. "net"

  6. "time"


  7. pb "grpc-sample/Pb_Go"

  8. "golang.org/x/net/context"

  9. "google.golang.org/grpc"

  10. )


  11. const (

  12. addr = ":50051"

  13. )


  14. // server is used to implement helloworld.GreeterServer.

  15. type server struct{}


  16. // SayHello implements helloworld.GreeterServer

  17. func (s *server) SayHello(ctx context.Context, u *pb.User) (*pb.Response, error) {

  18. return &pb.Response{ErrCode: 0, ErrMsg: "success", Data: map[string]string{"name": "Hello " + u.Name}}, nil

  19. }


  20. func main() {

  21. lis, err := net.Listen("tcp", addr)

  22. if err != nil {

  23. log.Fatalf("failed to listen: %v", err)

  24. }


  25. fmt.Printf("%s server start at %s\n", time.Now(), addr)


  26. s := grpc.NewServer()

  27. pb.RegisterGreeterServer(s, &server{})

  28. s.Serve(lis)

  29. }

然后就可以编译了。

有个大坑:go build main.go的时候会先下载 go.mod里定义的依赖(依赖比较多,详情查看:https://github.com/52fhy/grpc-sample/blob/master/go.mod),其中下面这条非常慢,仓库太大了,虽然重定向到github:

 
   
   
 
  1. replace google.golang.org/api => github.com/googleapis/google-api-go-client v0.6.1-0.20190616000641-99157d28da34

编译成功后,生成了二进制文件 main。我们可以直接运行:

 
   
   
 
  1. $ ./main


  2. 2019-06-30 17:16:07.752508 +0800 CST m=+0.028838467 server start at :50051

go test

为了测试我们写的服务是否正常,可以写测试用例:
test_client.go

 
   
   
 
  1. package main


  2. import (

  3. "context"

  4. "google.golang.org/grpc"

  5. pb "grpc-sample/Pb_Go"

  6. "testing"

  7. )


  8. func TestExec(t *testing.T) {

  9. conn, err := grpc.Dial(":50051", grpc.WithInsecure())

  10. if err != nil {

  11. t.Errorf("dial error: %v\n", err)

  12. }

  13. defer conn.Close()


  14. // 实例化客户端

  15. client := pb.NewGreeterClient(conn)


  16. // 调用服务


  17. user := pb.User{}

  18. user.Id = 1

  19. user.Name = "test"

  20. result, err := client.SayHello(context.Background(), &user)

  21. if err != nil {

  22. t.Errorf("grpc error: %v\n", err)

  23. }

  24. t.Logf("Recevied: %v\n", result)

  25. }

运行:

 
   
   
 
  1. $ go test -v client_test.go


  2. === RUN TestExec

  3. --- PASS: TestExec (0.01s)

  4. client_test.go:29: Recevied: errMsg:"success" data:<key:"name" value:"Hello test" >

  5. PASS

  6. ok command-line-arguments 0.021s

运行有点慢,感觉依赖的库多了。

php客户端

使用gRPC PHP客户端,确保你已经安装了:

  • protobuf c扩展

  • grpc c扩展

  • grpc php库

示例:
client_test.php

 
   
   
 
  1. <?php


  2. use Grpc\ChannelCredentials;

  3. use Sample\Model\User;

  4. use Sample\Model\UserList;

  5. use Sample\Model\GreeterClient;


  6. ini_set("display_errors", true);

  7. error_reporting(E_ALL);

  8. require_once "autoload.php";

  9. $user = new User();

  10. $user->setId(1)->setName("test");


  11. $client = new GreeterClient("192.168.99.1:50051", [

  12. 'credentials' => ChannelCredentials::createInsecure(), //不加密

  13. // 'timeout' => 3000000,

  14. ]);


  15. //分别是响应、状态对象

  16. list($reply, $status) = $client->SayHello($user)->wait();


  17. if (!$reply) {

  18. echo json_encode($status);

  19. return;

  20. }


  21. //序列化为string

  22. echo $reply->serializeToJsonString(true) . PHP_EOL;

  23. echo $reply->getErrCode() . PHP_EOL; //errCode

  24. echo $reply->getErrMsg() . PHP_EOL; //errMsg


  25. //data

  26. foreach ($reply->getData() as $key => $value) {

  27. echo $key . "-" . $value . PHP_EOL;

  28. }

运行后输出:

 
   
   
 
  1. $ php tests/client_test.php


  2. {"errMsg":"success","data":{"name":"Hello test"}}

  3. 0

  4. success

  5. name-Hello test

常见问题

1、CentOS6使用 go mod获取第三方依赖包unknown revision xxx错误
解决:其实go mod调用链中会用到一些git指令,当git版本比较旧时,调用失败产生错误,并给出歧义的提示信息。方法就是升级git版本,CentOS6自带的git是1.7版本。升级完毕后,再尝试go mod。

快速升级方法:
centos6:

 
   
   
 
  1. # 安装yum源

  2. wget http://opensource.wandisco.com/centos/6/git/x86_64/wandisco-git-release-6-1.noarch.rpm && rpm -ivh wandisco-git-release-6-1.noarch.rpm


  3. ## 安装git 2.x

  4. yum install git -y


  5. ## 验证

  6. git --version

  7. git version 2.14.1

2、PHP报错:Fatal error: Class 'Google\Protobuf\Internal\Message' not found
解决:请安装PHP的protobuf c扩展。

3、PHP报错:Fatal error: Class '\Grpc\BaseStub' not found
解决:使用 composerrequiregrpc/grpc安装grpc。另外对应的grpc C扩展也要安装。

4、下载 github release包很慢怎么办?
解决:下载Mac版 FreeDownloadManager 下载工具可以解决Github 下载缓慢或失败问题。速度嗖嗖的

参考

1、为CentOS 6、7升级gcc至4.8、4.9、5.2、6.3、7.3等高版本
http://www.vpser.net/manage/centos-6-upgrade-gcc.html
2、centos 6.x/7.x使用yum升级git版本 - 夜空
https://blog.slogra.com/post-721.html
3、Protobuf 小试牛刀 - 飞鸿影
https://www.cnblogs.com/52fhy/p/11106670.html


以上是关于gRPC入坑记的主要内容,如果未能解决你的问题,请参考以下文章

gRPC入坑记

conda命令入坑记

PuppeteerSharp入坑记

Spring Cloud Gateway入坑记

ionic入坑记记录

[乐意黎原创] Centos下载安装PHP7.4.10(7.4.11)时开启ZIP模块的入坑与填坑记