概要
由于 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 下载也是龟速.
本文讲解 gRPC demo 的同时, 会介绍如何解决这些坑. 本文对应的 GitHub 地址: https://github.com/52fhy/grpc-sample . 该仓库存储了 demo 示例, 以及部分系统编译好的二进制包, 大家觉得有些步骤里耗时实在太长了, 可以直接 clone 该仓库, 复制二进制包到对应目录(仅限测试开发, 生产环境还是老老实实自己编译吧).
升级 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:
- yum -y install CentOS-release-scl
- yum -y install devtoolset-6-gcc devtoolset-6-gcc-c++ devtoolset-6-binutils
- scl enable devtoolset-6 bash
需要注意的是 scl 命令启用只是临时的, 退出 shell 或重启就会恢复原系统 gcc 版本. 如果要长期使用 gcc 6.3 的话:
echo "source /opt/rh/devtoolset-6/enable">>/etc/profile
这样退出 shell 重新打开就是新版的 gcc 了. 其它版本同理.
升级到 gcc 7.3:
- yum -y install CentOS-release-scl
- yum -y install devtoolset-7-gcc devtoolset-7-gcc-c++ devtoolset-7-binutils
- scl enable devtoolset-7 bash
已经停止支持的 devtoolset4(gcc 5.2)及之前版本的安装方法, 可能比较慢, 大家感兴趣的话可以尝试.
升级到 gcc 4.8:
- wget http://people.centos.org/tru/devtools-2/devtools-2.repo -O /etc/yum.repos.d/devtoolset-2.repo
- yum -y install devtoolset-2-gcc devtoolset-2-gcc-c++ devtoolset-2-binutils
- scl enable devtoolset-2 bash
升级到 gcc4.9:
- 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
- yum -y install devtoolset-3-gcc devtoolset-3-gcc-c++ devtoolset-3-binutils
- scl enable devtoolset-3 bash
升级到 gcc 5.2:
- 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
- yum install devtoolset-4-gcc devtoolset-4-gcc-c++ devtoolset-4-binutils -y
- scl enable devtoolset-4 bash
编译 gRPC 命令行工具
如果仅使用 golang,java, 请忽略本节.
gRPC 分 C,JAVA,GO,Node.JS 版本, 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, 最终要从源码里编译出下列工具:
- grpc_cpp_plugin
- grpc_csharp_plugin
- grpc_node_plugin
- grpc_objective_c_plugin
- grpc_php_plugin
- grpc_python_plugin
- grpc_ruby_plugin
这些工具作为插件供 proto 编译器使用. 需要先下载 grpc/grpc GitHub 上的源码.
- Git clone -b $(curl -L https://grpc.io/release) https://github.com/grpc/grpc
- cd grpc
- Git submodule update --init
- make && sudo make install
- # 生成的插件路径
- ll ./bins/opt/
- # 复制到 bin 目录
- cp -r ./bins/opt/*/usr/local/bin/
这里有 2 个坑:
1,grpc/grpc 仓库比较大, 鉴于国内访问的网速, 建议使用国内镜像. 码云 (https://gitee.com) 提供了同步更新的镜像地址:
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 命令行工具(grpc_php_plugin)
grpc c 扩展
grpc PHP 库
其中 protoc 和 protobuf c 扩展已经在 Protobuf 小试牛刀 介绍过了, 这里不再赘述. 上一小节里如果安装成功, 那么 grpc_php_plugin 也是有了的. 下面介绍如何安装 PHP 版的 gRPC 库.
安装 grpc c 扩展:
要求: GCC 编译器需要 4.8 及以上版本. 可以使用 pecl 安装:
pecl install grpc
也可以指定版本:
pecl install grpc-1.12.0
或者下载源码 (http://pecl.php.net/package/grpc) 安装:
- wget http://pecl.php.net/get/grpc-1.21.3.tgz
- tar zxvf grpc-1.21.3.tgz && cd grpc-1.21.3
- phpize
- ./configure
- make
- make install
grpc/grpc 代码库里也有 PHP 扩展的 C 源码, 在 grpc/src/PHP/ext/grpc 目录, 进去也可以直接编译.
编译完成后在 PHP.INI 里添加, 使用 PHP --ri grpc 可以查看信息.
安装完 C 扩展后, 还需要使用 Composer 安装 grpc 的库:
Composer require grpc/grpc
gRPC 示例
编写 gRPC proto
一共定义了三个文件:
└── proto
├── GreeterService.proto
├── Response.proto
└── User.proto
其中 User 作为 Model 定义, Response 用于 RPC 统一返回定义, GreeterService 则是服务接口定义.
限于篇幅, proto 文件详见 https://github.com/52fhy/grpc-sample 仓库的 proto 目录.
GreeterService.proto 文件内容如下:
- syntax = "proto3";
- package Sample.Model; //namesapce
- import "User.proto";
- import "Response.proto";
- service Greeter {
- // Sends a greeting
- rpc SayHello (User) returns (Response) {}
- }
这里面定义了一个 service, 相当于定义了一个服务接口, 我们把方法名, 参数定义好了, 后面需要去实现它. 由于 gRPC 不支持 PHP 作为服务端, 这里我们使用 Golang 作为服务端.
首先需要使用 proto 工具编译出 golang 的代码:
- mkdir -p Pb_Go
- # 编译
- cd proto
- protoc --go_out=plugins=grpc:../Pb_Go/*.proto
- cd -
如果提示 protoc-gen-go 找不到, 请根据文章介绍 (https://www.cnblogs.com/52fhy/p/11106670.html#autoid-2-0-0) 进行安装.
执行成功, 会在 Pb_Go 目录里生成 Go 代码:
Pb_Go
├── GreeterService.pb.go
├── Response.pb.go
└── User.pb.go
如果需要生成 PHP 客户端的代码, 则需要使用 grpc PHP 的命令行工具 grpc_php_plugin, 前面小结如果执行成功, 这个工具已经有了. 然后:
- out=output/PHP
- mkdir -p $out
- # 编译
- cd proto
- protoc --php_out=../$out --grpc_out=../$out --plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin *.proto
- cd -
- # 修改命名空间
- cd $out
- mv GPBMetadata Sample/Model/
- 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 编译工具未编译.
最终生成的文件:
├── output
│ └── PHP
│ └── Sample
│ └── Model
│ ├── GPBMetadata
│ │ ├── GreeterService.PHP
│ │ ├── Response.PHP
│ │ └── User.PHP
│ ├── GreeterClient.PHP
│ ├── Response.PHP
│ ├── User.PHP
│ └── 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, 代码不多, 我直接贴出来:
- package main
- import (
- "fmt"
- "log"
- "net"
- "time"
- pb "grpc-sample/Pb_Go"
- "golang.org/x/net/context"
- "google.golang.org/grpc"
- )
- const (
- addr = ":50051"
- )
- // server is used to implement helloworld.GreeterServer.
- type server struct{}
- // SayHello implements helloworld.GreeterServer
- func (s *server) SayHello(ctx context.Context, u *pb.User) (*pb.Response, error) {
- return &pb.Response{ErrCode: 0, ErrMsg: "success", Data: map[string]string{"name": "Hello" + u.Name}}, nil
- }
- func main() {
- lis, err := net.Listen("tcp", addr)
- if err != nil {
- log.Fatalf("failed to listen: %v", err)
- }
- fmt.Printf("%s server start at %s\n", time.Now(), addr)
- s := grpc.NewServer()
- pb.RegisterGreeterServer(s, &server{})
- s.Serve(lis)
- }
然后就可以编译了.
有个大坑: go build main.go 的时候会先下载 go.mod 里定义的依赖(依赖比较多, 详情查看:), 其中下面这条非常慢, 仓库太大了, 虽然重定向到 GitHub:
replace google.golang.org/API => GitHub.com/googleapis/google-API-go-client v0.6.1-0.20190616000641-99157d28da34
为了快速下载, 我在码云上做了镜像, 地址: gitee.com/52fhy/google-API-go-client . 改了之后下载快多了.
编译成功后, 生成了二进制文件 main. 我们可以直接运行:
- $ ./main
- 2019-06-30 17:16:07.752508 +0800 CST m=+0.028838467 server start at :50051
- go test
为了测试我们写的服务是否正常, 可以写测试用例:
- test_client.go
- package main
- import (
- "context"
- "google.golang.org/grpc"
- pb "grpc-sample/Pb_Go"
- "testing"
- )
- func TestExec(t *testing.T) {
- conn, err := grpc.Dial(":50051", grpc.WithInsecure())
- if err != nil {
- t.Errorf("dial error: %v\n", err)
- }
- defer conn.Close()
- // 实例化客户端
- client := pb.NewGreeterClient(conn)
- // 调用服务
- user := pb.User{}
- user.Id = 1
- user.Name = "test"
- result, err := client.SayHello(context.Background(), &user)
- if err != nil {
- t.Errorf("grpc error: %v\n", err)
- }
- t.Logf("Recevied: %v\n", result)
- }
运行:
- $ go test -v client_test.go
- === RUN TestExec
- --- PASS: TestExec (0.01s)
- client_test.go:29: Recevied: errMsg:"success" data:<key:"name" value:"Hello test">
- PASS
- ok command-line-arguments 0.021s
运行有点慢, 感觉依赖的库多了.
PHP 客户端
使用 gRPC PHP 客户端, 确保你已经安装了:
protobuf c 扩展
grpc c 扩展
grpc PHP 库
示例:
client_test.PHP
- <?PHP
- use Grpc\ChannelCredentials;
- use Sample\Model\User;
- use Sample\Model\UserList;
- use Sample\Model\GreeterClient;
- ini_set("display_errors", true);
- error_reporting(E_ALL);
- require_once "autoload.php";
- $user = new User();
- $user->setId(1)->setName("test");
- $client = new GreeterClient("192.168.99.1:50051", [
- 'credentials' => ChannelCredentials::createInsecure(), // 不加密
- // 'timeout' => 3000000,
- ]);
- // 分别是响应, 状态对象
- list($reply, $status) = $client->SayHello($user)->wait();
- if (!$reply) {
- echo json_encode($status);
- return;
- }
- // 序列化为 string
- echo $reply->serializeToJsonString(true) . PHP_EOL;
- echo $reply->getErrCode() . PHP_EOL; //errCode
- echo $reply->getErrMsg() . PHP_EOL; //errMsg
- //data
- foreach ($reply->getData() as $key => $value) {
- echo $key . "-" . $value . PHP_EOL;
- }
运行后输出:
- $ PHP tests/client_test.PHP
- {
- "errMsg":"success","data":{
- "name":"Hello test"
- }
- }
- 0
- success
- name-Hello test
常见问题
1,CentOS6 使用 go mod 获取第三方依赖包 unknown revision xxx 错误
解决: 其实 go mod 调用链中会用到一些 Git 指令, 当 Git 版本比较旧时, 调用失败产生错误, 并给出歧义的提示信息. 方法就是升级 Git 版本, CentOS6 自带的 Git 是 1.7 版本. 升级完毕后, 再尝试 go mod.
快速升级方法:
- centos6:
- # 安装 yum 源
- 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
- ## 安装 Git 2.x
- yum install Git -y
- ## 验证
- Git --version
- 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
解决: 使用 Composer require grpc/grpc 安装 grpc. 另外对应的 grpc C 扩展也要安装.
4, 下载 GitHub release 包很慢怎么办?
解决: 下载 Mac 版
Free Download Manager
下载工具可以解决 GitHub 下载缓慢或失败问题. 速度嗖嗖的.
参考
1, 为 CentOS 6,7 升级 gcc 至 4.8,4.9,5.2,6.3,7.3 等高版本
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
来源: https://www.cnblogs.com/52fhy/p/11110704.html