概要

由于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、NodeJS版本,C版本包括C++, Python, Ruby, Objective-C, PHP, C#,这些语言都是基于C版本开发的,共用代码库一个代码库。

如果使用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库

其中protocprotobuf 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里定义的依赖(依赖比较多,详情查看:https://github.com/52fhy/grpc-sample/blob/master/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等高版本

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入坑记的更多相关文章

  1. Kylo 入坑记

    一.概述 Kylo,作为一个基于 Spark 和 NiFi 的开源数据湖编排框架,解决对数据湖获取.治理.感知和技术支持等诸多问题.Kylo 将数据湖的很多功能自动化,包括数据接入.准备.分析发现.P ...

  2. Hadoop入坑记

    Hadoop入坑记 按照官方给的伪分布式配置 etc/hadoop/core-site.xml: <configuration> <property> <name> ...

  3. Spring Cloud Gateway入坑记

    Spring Cloud Gateway入坑记 前提 最近在做老系统的重构,重构完成后新系统中需要引入一个网关服务,作为新系统和老系统接口的适配和代理.之前,很多网关应用使用的是Spring-Clou ...

  4. conda命令入坑记

    conda命令入坑记 本人使用的软件版本: TypeError: LoadLibrary() argument 1 must be str, not None 网上太多的资料,大多都是在讲path的设 ...

  5. ALS部署Spark集群入坑记

    [Stage 236:> (0 + 0) / 400]17/12/04 09:45:55 ERROR yarn.ApplicationMaster: User class threw excep ...

  6. Day25 前端自学日记——入坑记

    一 学习契机 今年是走出校门的第一个年头,进入了一家还算不错的公司,领着一份还算不错的薪水,在外人眼中,似乎这样已经不错了,只要我努力好好做,前程一片光明.可事实真是这样吗?两份实习经历都指向我应该从 ...

  7. Highcharts入坑记

    第一次用Highcharts画一个温度湿度变化的图片,因为不熟悉跳了好多坑,特记录下: 一.JS引用 <script src="~/Scripts/jquery.min.js" ...

  8. 【netcore入坑记】 .Net core UseRowNumberForPaging 分页报错 SQL Server 2008 R2 EntityFrameworkCore

    异常环境: netcore版本:.Net Core 2.1 efcore版本:Microsoft.EntityFrameworkCore.SqlServer 2.1.1 sql sqlserver 版 ...

  9. Jenkins入坑记

    记录一遍Jenkins初级使用教程 一,安装 (操作系统 centerOS7) 1-1.本次使用的是rpm包安装方式,在Jenkins官网下载rpm安装文件 下载地址: https://pkg.jen ...

随机推荐

  1. 第一泰泽(Tizen)智能手机发布在俄罗斯

    请看下图: 这是韩国三星公司在俄罗斯境内公布的第一款泰泽(Tizen)智能手机(今年6月2日).这说明,Tizen操作系统没有死去. 在泰泽官网上将泰泽操作系统定义为:"The OS of ...

  2. Metropolis 采样与蒙特卡洛算法

    Metropolis 算法又叫 Metropolis 抽样,是模拟退火算法的基础,在早期的科学计算中蒙特卡洛方法(Monte Carlo)是对大量原子在给定温度下的平衡态的随机模拟,当蒙特卡洛算法计算 ...

  3. StackLayout

    堆栈式地放置内容可以在xaml中完成视图,也可以在cs代码中完成视图 Xamarin的所有视图和布局都是可以 1.在xaml中完成 2.在cs代码中完成视图 (类比WPF) 示例 在cs代码中完成视图 ...

  4. Rxjava 学习(一)

    Rxjava是什么? RxJava是由Netflix开发的响应式扩展(Reactive Extensions)的Java实现.引用MSDN上对它的定义,Reactive Extensions是这样一个 ...

  5. WPF中的资源(二) - 二进制资源

    原文:WPF中的资源(二) - 二进制资源 WPF中的二进制资源,就是类似于MFC中在对话框程序中添加的图片.字符串等资源,程序在运行时将其转换成二进制,以供程序使用. 下面以将字符串转换成二进制为例 ...

  6. Linux学习之“fork函数”

    n返回值: fork函数调用一次,但是返回两次:在子进程中返回0,在父进程中返回子进程ID,出错返回-1.通过返回值,可以确定是在父进程还是子进程中. n子进程和父进程继续执行fork调用之后的指令. ...

  7. Win8 Metro(C#)数字图像处理--2.54迭代法图像二值化

    原文:Win8 Metro(C#)数字图像处理--2.54迭代法图像二值化  [函数名称]   迭代法图像二值化      int IterativeThSegment(WriteableBitm ...

  8. socket上http协议应用(使用socket进行http通信的例子,准备好报头以后,简单read/write就可以了)

    前几天看socket本有点晕, 好不容易弄明白了,才发现公司服务器用的是http的. 找了好久也没发现linux下直接用http的api, 不过今日偶然发现了使用socket进行http通信的例子, ...

  9. QML中实现setTimeout和setInterval

    Qt的QML中,js未提供setTimeout和setInterval,可以通过下面的代码实现. Timer {id: timer} function setTimeout(cb,delayTime) ...

  10. SQL Server上唯一的数据库集群:负载均衡、读写分离、容灾(数据零丢失、服务高可用)

    SQL Server上唯一的数据库集群:负载均衡.读写分离.容灾(数据零丢失.服务高可用).审计.优化,全面解决数据库用户问题.一键安装,易用稳定,性价比高,下载链接:http://www.zheti ...