zoukankan      html  css  js  c++  java
  • gRPC详细入门教程,Golang/Python/PHP多语言讲解

    一、gRPC是什么?

    gRPC,其实就是RPC框架的一种,前面带了一个g,代表是RPC中的大哥,龙头老大的意思,另外g也有global的意思,意思是全球化比较fashion,是一个高性能、开源和通用的 RPC 框架,面向服务端和移动端,基于 HTTP/2 设计。

    RPC框架是什么?

    RPC 框架说白了就是让你可以像调用本地方法一样调用远程服务提供的方法,而不需要关心底层的通信细节。简单地说就让远程服务调用更加简单、透明。
    RPC包含了客户端(Client)和服务端(Server)

    常见的RPC框架有

    1. gRPC。谷歌出品
    2. Thrift。Apache出品
    3. Dubbo。阿里出品,也是一个微服务框架

    gRPC的特性

    官方文档的介绍,有以下4点特性:

    1. 使用Protocal Buffers这个强大的结构数据序列化工具
    2. grpc可以跨语言使用
    3. 安装简单,扩展方便(用该框架每秒可达到百万个RPC)
    4. 基于HTTP2协议

    gRPC使用流程

    gprc的使用流程一般是这样的:

    1. 定义标准的proto文件
    2. 生成标准代码
    3. 服务端使用生成的代码提供服务
    4. 客户端使用生成的代码调用服务

    二、Protocol Buffers是什么?

    谷歌开源的一种结构数据序列化的工具,比方说JSON、XML也是结构数据序列化的工具,不同的是,

    1. Protocol Buffers序列化后的数据是不可读的,因为是二进制流
    2. 使用Protocol Buffer需要事先定义数据的格式(.proto 协议文件),还原一个序列化之后的数据需要使用到这个数据格式
    3. Protocol Buffer 比 XML、JSON快很多,因为是基于二进制流,比字符串更省带宽,传输速度快
      Protocol Buffer语法:查看官方文档

    下面演示根据需求开发项目,建议自己运行一下,加深印象

    三、需求:开发健身房服务

    定义了一个健身房(Gym),提供一个叫健身(Bodybuilding)的远程方法
    使用该服务,需要指定人(Person),人有名字(name)和训练动作(actions)两个属性。

    对应的协议文件gym.proto文件如下

    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;
    }
    

    四、最佳实践

    下面以Golang、Python、PHP介绍该grpc的使用,代码已经上传到了chenqionghe/grpc-demo
    最终目录结构如下图

    Golang

    1. 安装protoc

    地址:protobuf/releases
    我是mac,用的是这个地址:protoc-3.11.4-osx-x86_64.zip
    解压后放到了可以访问的bin即可

    2. 安装protoc-gen-go

    protoc依赖该工具生成代码

    go get -u github.com/golang/protobuf/protoc-gen-go
    

    gogoprotobuf的protoc-gen-gofast插件生成的文件更复杂,性能也更高,安装如下
    go get github.com/gogo/protobuf/protoc-gen-gofast

    3. 安装grpc包

    这是要代码里需要使用的,go get直接安装不了,手动克隆

    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
    

    4. 生成代码

    #!/usr/bin/env bash
    
    protoDir="../protos"
    outDir="../languages/golang/gym"
    protoc -I ${protoDir}/ ${protoDir}/*proto --go_out=plugins=grpc:${outDir}
    

    protoc工具参数解释:

    • -I: 指定import路径,可以指定多个-I参数,编译时按顺序查找,不指定默认当前目录
    • -go_out:指定og语言的访问类
    • plugins:指定依赖的插件
    使用gofast将go_out=plugins=grpc:${outDir}改为gofast_out=plugins=grpc:${outDir}
    

    执行,然后我们会看到在golang目录生成了该代码

    5. 定义服务端

    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
    ", in.Name, in.Actions)
     return &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, &server{})
    
     if err := s.Serve(lis); err != nil {
      log.Fatalf("failed to serve: %v", err)
     }
    }
    
    

    6. 定义客户端

    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, &lightweight.Person{
      Name: "chenqionghe",
      Actions: []string{"深蹲", "卧推", "硬拉"},
     })
     if err != nil {
      log.Fatalf("error: %v", err)
     }
     fmt.Printf("code: %d, msg: %s", r.Code, r.Msg)
    }
    

    7. 运行代码

    golang目录结果是现在是这样的

    .
    ├── client.go
    ├── go.mod
    ├── go.sum
    ├── lightweight
    │ └── gym.pb.go
    └── server.go
    

    运行服务端和客户端,效果如下

    可以看到,chenqionghe去健身成功

    Python

    1. 安装grpc包

    pip install grpcio
    

    2. 安装protobuf

    pip install protobuf
    

    3. 安装grpc的protobuf编译工具

    包含了protoc编译器和生成代码的插件

    pip install grpcio-tools
    

    4. 生成代码

    #!/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
    

    5. 定义服务端

    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()
    
    

    6. 定义客户端

    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()
    
    

    7. 运行代码

    目录结构如下,分别运行

    ├── client.py
    ├── lightweight
    │ ├── gym_pb2.py
    │ └── gym_pb2_grpc.py
    └── server.py
    


    可以看到,chenqionghe去健身成功

    PHP

    1. 安装protoc

    地址:protobuf/releases
    我是mac,用的是这个地址:protoc-3.11.4-osx-x86_64.zip
    解压后放到了可以访问的bin即可

    2. 安装grpc扩展

    方式一:pecl安装

    pecl install grpc
    

    将扩展加入到php.ini

    extension=grpc.so
    

    3. 安装protobuf扩展

    pecl install protobuf
    

    将扩展加入到php.ini

    extension=protobuf.so
    

    4. 安装生成代码的插件grpc_php_plugin

    该插件用来生成PHP的gRPC代码

    git clone -b v1.27.0 https://github.com/grpc/grpc
    cd grpc && git submodule update --init && make grpc_php_plugin
    

    注意:这个过程耗时比较久,请做好心理准备(可以在.gitmodules文件中看到依赖的仓库比较多)

    画风是这样的

    如果grpc_php_plugin安装不上,mac系统可以直接copy我已经编译好的grpc_php_plugin

    安装完成画风

    最终会在bins/opt下生成grpc_php_plugin文件,我们把它移动到/usr/local/bin下

    5. 生成代码

    #!/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
    

    生成代码如下

    6. 定义客户端

    1.创建composer.json文件并执行

    {
      "name": "gym",
      "require": {
        "grpc/grpc": "^v1.3.0",
        "google/protobuf": "^v3.3.0"
      },
      "autoload": {
        "psr-4": {
          "GPBMetadata\": "lightweight/GPBMetadata/",
          "Lightweight\": "lightweight/Lightweight/"
        }
      }
    }
    

    执行composer install
    2.创建客户端文件client.php

    <?php
    /**
     * 客户端使用示例
     * @author chenqionghe
     */
    
    require dirname(__FILE__) . '/vendor/autoload.php';
    
    //初始化要去的健身房
    $client = new LightweightGymClient('127.0.0.1:50051', [
        'credentials' => GrpcChannelCredentials::createInsecure(),
    ]);
    //带上自己的卡和运动计划
    $request = new LightweightPerson();
    $request->setName("chenqionghe");
    $request->setActions(['深蹲', '卧推', '硬拉']);
    //去健身房健身
    list($response, $status) = $client->BodyBuilding($request)->wait();
    echo sprintf("code: %s, msg: %s 
    ", $response->getCode(), $response->getMsg());
    

    注意:php不支持grpc的服务端,建议服务端起一个上面的golang或者python中的server
    这里我用的是golang的

    7. 运行代码


    可以看到,chenqionghe去健身成功,yes,就是这么easy!

    五、如何调试gRPC

    我们都知道rest api是可用curl和postman这样的工具来调试的,grpc也有类似的工具,对应的分别是grpcurl和grpcui

    grpcurl-命令行工具

    类似curl,可以直接用来发送请求调用远程的grpc服务,官方地址请看grpcurl

    安装

    brew直接安装

    brew install grpcurl
    

    也可以使用go get安装

    go get github.com/fullstorydev/grpcurl
    go install github.com/fullstorydev/grpcurl/cmd/grpcurl
    

    使用

    注意grpcl是基于反射,需要在启动服务前添加这样一行代码

     reflection.Register(s)
    

    1.查询服务列表

    grpcurl -plaintext 127.0.0.1:50051 list
    

    2.查询服务提供的方法

    grpcurl -plaintext 127.0.0.1:50051 list lightweight.Gym
    

    3.查看更详细的描述

    grpcurl -plaintext 127.0.0.1:50051 describe lightweight.Gym
    

    4.获取类型信息

    grpcurl -plaintext 127.0.0.1:50051 describe lightweight.Person
    

    5.调用服务方法

    grpcurl -plaintext -d '{"name":"chenqionghe","actions":["深蹲","卧推","硬拉"]}' 127.0.0.1:50051 lightweight.Gym/BodyBuilding
    

    grpcui-界面工具

    简单的说,就是gRPC中的postman,能带你飞起来那种,官方地址grpcui

    安装

    go get github.com/fullstorydev/grpcui
    go install github.com/fullstorydev/grpcui/cmd/grpcui
    

    使用

    运行web界面,指定grpc的地址

    grpcui -plaintext localhost:50051
    

    提示Web UI的地址为http://127.0.0.1:50791
    访问出来以下界面

    直接把所有服务和方法都列出来了,真的是相当人性化
    我们来使用一下,传递参数

    返回结果如下

    到服务端查看,已经正常的接收到请求

    OK,到这里,分别演示了Golang、Python、PHP使用gRPC的例子,以及gRPC调试工具,就是这么简单粗暴!light weight baby !

  • 相关阅读:
    3种Java从文件路径中获取文件名的方法
    win10系统下点击关机却自动重启的问题解决思路
    Eclipse窗口总是在最前的解决办法
    tomcat端口被占用
    js页面跳转整理(转载未整理)
    java.lang.IllegalArgumentException 不合法的参数异常
    MySQL如何查询两个日期之间的记录
    web -- 前端访问后台跨区问题解决
    Maven -- 发布jar包至远程仓库
    5 -- Hibernate的基本用法 --4 3 JDBC连接属性
  • 原文地址:https://www.cnblogs.com/chenqionghe/p/12394845.html
Copyright © 2011-2022 走看看