若楠哄心 發表於 2020-3-2 12:06:00

gRPC详细入门教程,Golang/Python/PHP多语言讲解

<p></p><div class="toc"><div class="toc-container-header">目录</div><ul><li>一、gRPC是什么?<ul><li>RPC框架是什么?</li><li>gRPC的特性</li><li>gRPC使用流程</li></ul></li><li>二、Protocol Buffers是什么?</li><li>三、需求:开发健身房服务</li><li>四、最佳实践<ul><li>Golang<ul><li>1. 安装protoc</li><li>2. 安装protoc-gen-go</li><li>3. 安装grpc包</li><li>4. 生成代码</li><li>5. 定义服务端</li><li>6. 定义客户端</li><li>7. 运行代码</li></ul></li><li>Python<ul><li>1. 安装grpc包</li><li>2. 安装protobuf</li><li>3. 安装grpc的protobuf编译工具</li><li>4. 生成代码</li><li>5. 定义服务端</li><li>6. 定义客户端</li><li>7. 运行代码</li></ul></li><li>PHP<ul><li>1. 安装protoc</li><li>2. 安装grpc扩展</li><li>3. 安装protobuf扩展</li><li>4. 安装生成代码的插件grpc_php_plugin</li><li>5. 生成代码</li><li>6. 定义客户端</li><li>7. 运行代码</li></ul></li></ul></li><li>五、如何调试gRPC<ul><li>grpcurl-命令行工具<ul><li>安装</li><li>使用</li></ul></li><li>grpcui-界面工具<ul><li>安装</li><li>使用</li></ul></li></ul></li></ul></div><p></p>
<h1 id="一grpc是什么">一、gRPC是什么?</h1>
<p>gRPC,其实就是RPC框架的一种,前面带了一个g,代表是RPC中的大哥,龙头老大的意思,另外g也有global的意思,意思是全球化比较fashion,是一个高性能、开源和通用的 RPC 框架,面向服务端和移动端,基于 HTTP/2 设计。</p>
<h2 id="rpc框架是什么">RPC框架是什么?</h2>
<p>RPC 框架说白了就是让你可以像调用本地方法一样调用远程服务提供的方法,而不需要关心底层的通信细节。简单地说就让远程服务调用更加简单、透明。<br>
RPC包含了客户端(Client)和服务端(Server)</p>
<p>常见的RPC框架有</p>
<ol>
<li>gRPC。谷歌出品</li>
<li>Thrift。Apache出品</li>
<li>Dubbo。阿里出品,也是一个微服务框架</li>
</ol>
<h2 id="grpc的特性">gRPC的特性</h2>
<p>看官方文档的介绍,有以下4点特性:</p>
<ol>
<li>使用Protocal Buffers这个强大的结构数据序列化工具</li>
<li>grpc可以跨语言使用</li>
<li>安装简单,扩展方便(用该框架每秒可达到百万个RPC)</li>
<li>基于HTTP2协议</li>
</ol>
<h2 id="grpc使用流程">gRPC使用流程</h2>
<p>gprc的使用流程一般是这样的:</p>
<ol>
<li>定义标准的proto文件</li>
<li>生成标准代码</li>
<li>服务端使用生成的代码提供服务</li>
<li>客户端使用生成的代码调用服务</li>
</ol>
<h1 id="二protocol-buffers是什么">二、Protocol Buffers是什么?</h1>
<p>谷歌开源的一种结构数据序列化的工具,比方说JSON、XML也是结构数据序列化的工具,不同的是,</p>
<ol>
<li>Protocol Buffers序列化后的数据是不可读的,因为是二进制流</li>
<li>使用Protocol Buffer需要事先定义数据的格式(.proto 协议文件),还原一个序列化之后的数据需要使用到这个数据格式</li>
<li>Protocol Buffer 比 XML、JSON快很多,因为是基于二进制流,比字符串更省带宽,传输速度快<br>
Protocol Buffer语法:查看官方文档</li>
</ol>
<p>下面演示根据需求开发项目,建议自己运行一下,加深印象</p>
<h1 id="三需求开发健身房服务">三、需求:开发健身房服务</h1>
<p>定义了一个健身房(Gym),提供一个叫健身(Bodybuilding)的远程方法<br>
使用该服务,需要指定人(Person),人有名字(name)和训练动作(actions)两个属性。</p>
<p>对应的协议文件gym.proto文件如下</p>
<pre><code>syntax = "proto3";
//命名空间
package lightweight;

//健身房
service Gym {
    rpc BodyBuilding (Person) returns (Reply) {

    }
}
//谁在健身
message Person {
    string name = 1;
    repeated string actions = 2;
}

//结果
message Reply {
    int32 code = 1;
    string msg = 2;
}
</code></pre>
<h1 id="四最佳实践">四、最佳实践</h1>
<p>下面以Golang、Python、PHP介绍该grpc的使用,代码已经上传到了chenqionghe/grpc-demo<br>
最终目录结构如下图<br>
<img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302121444264-1610663325.png" alt="" loading="lazy"></p>
<h2 id="golang">Golang</h2>
<h3 id="1-安装protoc">1. 安装protoc</h3>
<p>地址:protobuf/releases<br>
我是mac,用的是这个地址:protoc-3.11.4-osx-x86_64.zip<br>
解压后放到了可以访问的bin即可</p>
<h3 id="2-安装protoc-gen-go">2. 安装protoc-gen-go</h3>
<p>protoc依赖该工具生成代码</p>
<pre><code>go get -u github.com/golang/protobuf/protoc-gen-go
</code></pre>
<blockquote>
<p>gogoprotobuf的protoc-gen-gofast插件生成的文件更复杂,性能也更高,安装如下<br>
go get github.com/gogo/protobuf/protoc-gen-gofast</p>
</blockquote>
<h3 id="3-安装grpc包">3. 安装grpc包</h3>
<p>这是要代码里需要使用的,go get直接安装不了,手动克隆</p>
<pre><code>git clone https://github.com/grpc/grpc-go.git $GOPATH/src/google.golang.org/grpc
git clone https://github.com/golang/net.git $GOPATH/src/golang.org/x/net
git clone https://github.com/golang/text.git $GOPATH/src/golang.org/x/text
git clone https://github.com/google/go-genproto.git $GOPATH/src/google.golang.org/genproto
cd $GOPATH/src/
go install google.golang.org/grpc
</code></pre>
<h3 id="4-生成代码">4. 生成代码</h3>
<pre><code>#!/usr/bin/env bash

protoDir="../protos"
outDir="../languages/golang/gym"
protoc -I ${protoDir}/ ${protoDir}/*proto --go_out=plugins=grpc:${outDir}
</code></pre>
<p>protoc工具参数解释:</p>
<ul>
<li>-I: 指定import路径,可以指定多个-I参数,编译时按顺序查找,不指定默认当前目录</li>
<li>-go_out:指定og语言的访问类</li>
<li>plugins:指定依赖的插件</li>
</ul>
<pre><code>使用gofast将go_out=plugins=grpc:${outDir}改为gofast_out=plugins=grpc:${outDir}
</code></pre>
<p>执行,然后我们会看到在golang目录生成了该代码<br>
<img src="https://img2018.cnblogs.com/blog/662544/202003/662544-20200302120144409-1594156158.png" alt="" loading="lazy"></p>
<h3 id="5-定义服务端">5. 定义服务端</h3>
<pre><code>package main

import (
"app/lightweight"
"context"
"fmt"
"google.golang.org/grpc"
"log"
"net"
)

const (
port = ":50051"
)

// server继承自动生成的服务类
type server struct {
lightweight.UnimplementedGymServer
}

// 服务端必须实现了相应的接口BodyBuilding
func (s *server) BodyBuilding(ctx context.Context, in *lightweight.Person) (*lightweight.Reply, error) {
fmt.Printf("%s正在健身, 动作: %s\n", in.Name, in.Actions)
return &amp;lightweight.Reply{Code: 0, Msg: "ok",}, nil
}
func main() {
lis, err := net.Listen("tcp", port)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}

s := grpc.NewServer()
lightweight.RegisterGymServer(s, &amp;server{})

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

</code></pre>
<h3 id="6-定义客户端">6. 定义客户端</h3>
<pre><code>package main

import (
"app/lightweight"
"context"
"fmt"
"google.golang.org/grpc"
"log"
"time"
)

const (
address = "localhost:50051"
)

func main() {
// Set up a connection to the server.
conn, err := grpc.Dial(address, grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := lightweight.NewGymClient(conn)

ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
r, err := c.BodyBuilding(ctx, &amp;lightweight.Person{
Name: "chenqionghe",
Actions: []string{"深蹲", "卧推", "硬拉"},
})
if err != nil {
log.Fatalf("error: %v", err)
}
fmt.Printf("code: %d, msg: %s", r.Code, r.Msg)
}
</code></pre>
<h3 id="7-运行代码">7. 运行代码</h3>
<p>golang目录结果是现在是这样的</p>
<pre><code>.
├── client.go
├── go.mod
├── go.sum
├── lightweight
│ └── gym.pb.go
└── server.go
</code></pre>
<p>运行服务端和客户端,效果如下<br>
<img src="https://img2018.cnblogs.com/blog/662544/202003/662544-20200302120158376-520152349.png" alt="" loading="lazy"><br>
可以看到,chenqionghe去健身成功</p>
<h2 id="python">Python</h2>
<h3 id="1-安装grpc包">1. 安装grpc包</h3>
<pre><code>pip install grpcio
</code></pre>
<h3 id="2-安装protobuf">2. 安装protobuf</h3>
<pre><code>pip install protobuf
</code></pre>
<h3 id="3-安装grpc的protobuf编译工具">3. 安装grpc的protobuf编译工具</h3>
<p>包含了protoc编译器和生成代码的插件</p>
<pre><code>pip install grpcio-tools
</code></pre>
<h3 id="4-生成代码-1">4. 生成代码</h3>
<pre><code>#!/usr/bin/env bash

protoDir="../protos"
outDir="../languages/python/gym/"

python3 -m grpc_tools.protoc -I ${protoDir}/ --python_out=${outDir} --grpc_python_out=${outDir} ${protoDir}/*proto
</code></pre>
<p><img src="https://img2018.cnblogs.com/blog/662544/202003/662544-20200302120215136-2075105048.png" alt="" loading="lazy"></p>
<h3 id="5-定义服务端-1">5. 定义服务端</h3>
<pre><code>from concurrent import futures
import logging
import grpc

# 支持新的包
import sys
sys.path.append("lightweight")
import lightweight.gym_pb2_grpc as gym_pb2_grpc
import lightweight.gym_pb2 as gym_pb2


class Gym(gym_pb2_grpc.GymServicer):

    def BodyBuilding(self, request, context):
      print(f"{request.name}在健身, 动作: {list(request.actions)}")
      return gym_pb2.Reply(code=0, msg='ok')


def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    gym_pb2_grpc.add_GymServicer_to_server(Gym(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    server.wait_for_termination()


if __name__ == '__main__':
    logging.basicConfig()
    serve()

</code></pre>
<h3 id="6-定义客户端-1">6. 定义客户端</h3>
<pre><code>from __future__ import print_function
import logging
import grpc

# 支持导入新的包
import sys
sys.path.append("lightweight")
import lightweight.gym_pb2_grpc as gym_pb2_grpc
import lightweight.gym_pb2 as gym_pb2

def run():
    with grpc.insecure_channel('localhost:50051') as channel:
      stub = gym_pb2_grpc.GymStub(channel)
      response = stub.BodyBuilding(gym_pb2.Person(
            name='chenqionghe', actions=['深蹲', '卧推', '硬拉']
      ))
    print(f'code: {response.code}, msg:{response.msg}')


if __name__ == '__main__':
    logging.basicConfig()
    run()

</code></pre>
<h3 id="7-运行代码-1">7. 运行代码</h3>
<p>目录结构如下,分别运行</p>
<pre><code>├── client.py
├── lightweight
│ ├── gym_pb2.py
│ └── gym_pb2_grpc.py
└── server.py
</code></pre>
<p><img src="https://img2018.cnblogs.com/blog/662544/202003/662544-20200302120225540-626624207.png" alt="" loading="lazy"><br>
可以看到,chenqionghe去健身成功</p>
<h2 id="php">PHP</h2>
<h3 id="1-安装protoc-1">1. 安装protoc</h3>
<p>地址:protobuf/releases<br>
我是mac,用的是这个地址:protoc-3.11.4-osx-x86_64.zip<br>
解压后放到了可以访问的bin即可</p>
<h3 id="2-安装grpc扩展">2. 安装grpc扩展</h3>
<p>方式一:pecl安装</p>
<pre><code>pecl install grpc
</code></pre>
<p>将扩展加入到php.ini</p>
<pre><code>extension=grpc.so
</code></pre>
<h3 id="3-安装protobuf扩展">3. 安装protobuf扩展</h3>
<pre><code>pecl install protobuf
</code></pre>
<p>将扩展加入到php.ini</p>
<pre><code>extension=protobuf.so
</code></pre>
<h3 id="4-安装生成代码的插件grpc_php_plugin">4. 安装生成代码的插件grpc_php_plugin</h3>
<p>该插件用来生成PHP的gRPC代码</p>
<pre><code>git clone -b v1.27.0 https://github.com/grpc/grpc
cd grpc &amp;&amp; git submodule update --init &amp;&amp; make grpc_php_plugin
</code></pre>
<blockquote>
<p>注意:这个过程耗时比较久,请做好心理准备(可以在.gitmodules文件中看到依赖的仓库比较多)</p>
</blockquote>
<p>画风是这样的<br>
<img src="https://img2018.cnblogs.com/blog/662544/202003/662544-20200302120245347-1021912900.png" alt="" loading="lazy"></p>
<p>如果grpc_php_plugin安装不上,mac系统可以直接copy我已经编译好的grpc_php_plugin</p>
<p>安装完成画风<br>
<img src="https://img2018.cnblogs.com/blog/662544/202003/662544-20200302120256013-619603378.png" alt="" loading="lazy"></p>
<p>最终会在bins/opt下生成grpc_php_plugin文件,我们把它移动到/usr/local/bin下</p>
<h3 id="5-生成代码">5. 生成代码</h3>
<pre><code>#!/usr/bin/env bash

protoDir="../protos"
outDir="../languages/php/lightweight"
protoc -I ${protoDir}/ ${protoDir}/*proto --go_out=plugins=grpc:${outDir}

protoc --proto_path=${protoDir} \
--php_out=${outDir} \
--grpc_out=${outDir} \
--plugin=protoc-gen-grpc=/usr/local/bin/grpc_php_plugin \
${protoDir}/*.proto
</code></pre>
<p>生成代码如下<br>
<img src="https://img2018.cnblogs.com/blog/662544/202003/662544-20200302120322391-164535321.png" alt="" loading="lazy"></p>
<h3 id="6-定义客户端-2">6. 定义客户端</h3>
<p>1.创建composer.json文件并执行</p>
<pre><code>{
"name": "gym",
"require": {
    "grpc/grpc": "^v1.3.0",
    "google/protobuf": "^v3.3.0"
},
"autoload": {
    "psr-4": {
      "GPBMetadata\\": "lightweight/GPBMetadata/",
      "Lightweight\\": "lightweight/Lightweight/"
    }
}
}
</code></pre>
<p>执行composer install<br>
2.创建客户端文件client.php</p>
<pre><code>&lt;?php
/**
* 客户端使用示例
* @author chenqionghe
*/

require dirname(__FILE__) . '/vendor/autoload.php';

//初始化要去的健身房
$client = new \Lightweight\GymClient('127.0.0.1:50051', [
    'credentials' =&gt; Grpc\ChannelCredentials::createInsecure(),
]);
//带上自己的卡和运动计划
$request = new \Lightweight\Person();
$request-&gt;setName("chenqionghe");
$request-&gt;setActions(['深蹲', '卧推', '硬拉']);
//去健身房健身
list($response, $status) = $client-&gt;BodyBuilding($request)-&gt;wait();
echo sprintf("code: %s, msg: %s \n", $response-&gt;getCode(), $response-&gt;getMsg());
</code></pre>
<p>注意:php不支持grpc的服务端,建议服务端起一个上面的golang或者python中的server<br>
这里我用的是golang的</p>
<h3 id="7-运行代码-2">7. 运行代码</h3>
<p><img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302120706553-25725592.png" alt="" loading="lazy"><br>
可以看到,chenqionghe去健身成功,yes,就是这么easy!</p>
<h1 id="五如何调试grpc">五、如何调试gRPC</h1>
<p>我们都知道rest api是可用curl和postman这样的工具来调试的,grpc也有类似的工具,对应的分别是grpcurl和grpcui</p>
<h2 id="grpcurl-命令行工具">grpcurl-命令行工具</h2>
<p>类似curl,可以直接用来发送请求调用远程的grpc服务,官方地址请看grpcurl</p>
<h3 id="安装">安装</h3>
<p>brew直接安装</p>
<pre><code>brew install grpcurl
</code></pre>
<p>也可以使用go get安装</p>
<pre><code>go get github.com/fullstorydev/grpcurl
go install github.com/fullstorydev/grpcurl/cmd/grpcurl
</code></pre>
<h3 id="使用">使用</h3>
<p>注意grpcurl是基于反射,需要在启动服务前添加这样一行代码</p>
<pre><code> reflection.Register(s)
</code></pre>
<p>1.查询服务列表</p>
<pre><code>grpcurl -plaintext 127.0.0.1:50051 list
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302160402156-1496387912.png" alt="" loading="lazy"></p>
<p>2.查询服务提供的方法</p>
<pre><code>grpcurl -plaintext 127.0.0.1:50051 list lightweight.Gym
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302160420211-1597044059.png" alt="" loading="lazy"></p>
<p>3.查看更详细的描述</p>
<pre><code>grpcurl -plaintext 127.0.0.1:50051 describe lightweight.Gym
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302160425466-1127826923.png" alt="" loading="lazy"></p>
<p>4.获取类型信息</p>
<pre><code>grpcurl -plaintext 127.0.0.1:50051 describe lightweight.Person
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302160433932-1715423616.png" alt="" loading="lazy"></p>
<p>5.调用服务方法</p>
<pre><code>grpcurl -plaintext -d '{"name":"chenqionghe","actions":["深蹲","卧推","硬拉"]}' 127.0.0.1:50051 lightweight.Gym/BodyBuilding
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302160441454-1030181910.png" alt="" loading="lazy"></p>
<h2 id="grpcui-界面工具">grpcui-界面工具</h2>
<p>简单的说,就是gRPC中的postman,能带你飞起来那种,官方地址grpcui</p>
<h3 id="安装-1">安装</h3>
<pre><code>go get github.com/fullstorydev/grpcui
go install github.com/fullstorydev/grpcui/cmd/grpcui
</code></pre>
<h3 id="使用-1">使用</h3>
<p>运行web界面,指定grpc的地址</p>
<pre><code>grpcui -plaintext localhost:50051
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302163156787-1329102902.png" alt="" loading="lazy"></p>
<p>提示Web UI的地址为http://127.0.0.1:50791<br>
访问出来以下界面<br>
<img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302163244401-1821065395.png" alt="" loading="lazy"></p>
<p>直接把所有服务和方法都列出来了,真的是相当人性化<br>
我们来使用一下,传递参数<br>
<img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302163250711-1567366848.png" alt="" loading="lazy"><br>
返回结果如下<br>
<img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302163259779-239729299.png" alt="" loading="lazy"></p>
<p>到服务端查看,已经正常的接收到请求<br>
<img src="https://img2020.cnblogs.com/blog/662544/202003/662544-20200302163305412-732376208.png" alt="" loading="lazy"></p>
<p>OK,到这里,分别演示了Golang、Python、PHP使用gRPC的例子,以及gRPC调试工具,就是这么简单粗暴!light weight baby !</p><br><br>
来源:https://www.cnblogs.com/chenqionghe/p/12394845.html
頁: [1]
查看完整版本: gRPC详细入门教程,Golang/Python/PHP多语言讲解