关于微服务 - 你需要知道GRPC

159 阅读4分钟

前言

gRPC是Google公布的开源项目,基于HTTP2.0协议,并支持常见的众多编程语言。HTTP2.0协议是基于二进制的HTTP协议的升级版本,gRPC底层使用了Netty框架

(1):RPC接口和HTTP接口有什么区别呢?

RPC(Remote Procedure Call,远程过程调用)和HTTP(HyperText Transfer Protocol,超文本传输协议),前者是一种方法后者是协议,两者都常用于实现服务,在这个层面最本质的区别是RPC服务主要工作在TCP协议之上(也可以在HTTP协议),而HTTP服务工作在HTTP协议之上。由于HTTP协议基于TCP协议,所以RPC服务天然比HTTP更轻量,效率更胜一筹

两者都是基于网络实现的,从这一点上,都是基于Client/Server架构

(2):RPC接口和HTTP接口怎么选择?

传输效率:RPC比HTTP更快,虽然两者底层都是TCP,但是HTTP信息更加臃肿(不过可以采用gzip压缩),而RPC可以基于thrift实现高效的二进制传输
灵活性:HTTP比RPC更简单,开发效率更快。RPC每次增删改一个传输/返回参数服务端和客户端都要重新生成pb文件

所以综上所述,我的建议是内部接口走RPC,开放接口走HTTP

首先是前期准备(以CentOS-7为例)

(1):完成Golang运行环境安装,版本根据需求自定义,建议1.17以上

(2):安装 protobuf 编译器

      (2-1):wget https://github.com/protocolbuffers/protobuf/releases/download/v3.11.2/protobuf-all-3.11.2.tar.gz
      (2-2):tar zxf protobuf-all-3.11.2.tar.gz
      (2-3):cd protobuf-3.11.2
      (2-4):./configure -prefix=/usr/local/
      (2-5):make && make install
      root@localhost> protoc --version
      # 能正常输出版本即可,安装过程中出现失败按照错误提示安装前置扩展即可

(3):安装Go protobuf插件

3-1):go get -u github.com/golang/protobuf/proto
     (3-2):go get -u github.com/golang/protobuf/protoc-gen-go3-3):go get -u google.golang.org/grpc

然后是创建服务端(以golang1.7版本为例)

(1):创建proto文件

     // 指定 proto 版本
     syntax = "proto3";

     option go_package="./;proto";
     // 指定包名
     package proto;

     // 说明接口用途,养成良好注释习惯
     service CloseExpireOrder {

         rpc CloseExpireOrder(CloseExpireOrderRequest) returns (CloseExpireOrderResponse) {}
     }

     // 接口请求参数
     message CloseExpireOrderRequest {
            string 		oid 	= 1;
     }

     // 接口返回参数
     message CloseExpireOrderResponse {
          int32 		result 	= 1;
            string 		msg 		= 2;
            string 		data 		= 3;
     }

(2):生成pb.go文件

    进入上述proto所在目录运行 "protoc -I . --go_out=plugins=grpc:. ./xxx.proto"

(3):创建业务控制器

    package order

import (
 "context"
 "encoding/json"
 "net/http"
 CloseExpireOrderPB "xxx/services/grpc/proto/closeexpireorder"
)

type OrderControllers struct{}

 func (h *OrderControllers) CloseExpireOrder(ctx context.Context, c *CloseExpireOrderPB.CloseExpireOrderRequest) (*CloseExpireOrderPB.CloseExpireOrderResponse, error) {
 
 	if c.Oid == "" {
 
 		return &CloseExpireOrderPB.CloseExpireOrderResponse{Result: http.StatusBadRequest, Msg: "校验参数缺失", Data: "{}"}, nil
 	}
     
     // 这里写你的业务。。。。。
 	return &CloseExpireOrderPB.CloseExpireOrderResponse{Result: http.StatusOK, Msg: "订单成功已成功关闭", Data: "{}"}, nil
 }

(4):创建入口路由文件【只用创建一次】

```
    /*
     * @Author: psq
     * @Date: 2022-01-30 21:20:08
     * @LastEditors: psq
     * @LastEditTime: 2022-09-02 17:22:23
     */
    package service
    
    import (
        "fmt"
        "net"
        "os"

        "xxx/services/grpc/controllers/order"
        CloseExpireOrderPB "xxx/services/grpc/proto/closeexpireorder"

        GoogleGPRC "google.golang.org/grpc"
        "google.golang.org/grpc/reflection"
    )

    /**
     * @description: 启动GRPC服务
     * @param {int16} port 运行端口
     * @return {*}
     */
    func GrpcServiceStart(port int16) {

                lis, err := net.Listen("tcp", fmt.Sprintf(":%d", port))

                if err != nil {
                        fmt.Println(fmt.Scanln("GRPC启动失败, 服务已自动, 终止错误内容: [%s]", err))
                        os.Exit(0)
                }

                fmt.Println("GRPC启动成功, 占用端口", port)

                GrpcServer := GoogleGPRC.NewServer()
                reflection.Register(GrpcServer)

                CloseExpireOrderPB.RegisterCloseExpireOrderServer(GrpcServer, &order.OrderControllers{})

        GrpcServer.Serve(lis)
    }
    
```

(5):GRPC接口测试

  // 安装过程如果遇到报错,按照提示安装所需的前置扩展即可
  (1) : go get github.com/fullstorydev/grpcui
  (2) : go install github.com/fullstorydev/grpcui/cmd/grpcui
  (3) : root@localhost> grpcui -plaintext 127.0.0.1:[GRPC服务端口]

上述步骤完成后,恭喜你,GRPC服务已经创建好了,很简单吧!!!

最后就是客户端调用了

python调用示例(版本3.6)

1):pip install grpcio
    (2):pip install protobuf
    (3):pip install grpcio_tools
     # 将服务端的proto文件原样复制过来4):python -m  grpc_tools.protoc  --python_out=../pb2/ --grpc_python_out=../pb2grpc -I. ./xxx.proto
    
    # 调用示例
     '''
     Author: psq
     Date: 2022-09-02 17:31:42
     LastEditors: psq
     LastEditTime: 2022-09-04 14:45:33
     '''

     #!/usr/bin/python3
     # -*- coding: UTF-8 -*-
     import grpc
     import [XXX]_pb2, 
     import [XXX]_pb2_grpc
 
     if __name__ == '__main__': 
     
         with grpc.insecure_channel("[GRPC主机地址]:[GRPC主机端口]") as channel:
             
             res = closeexpireorder_pb2_grpc.CloseExpireOrderStub(channel = channel).CloseExpireOrder(closeexpireorder_pb2.CloseExpireOrderRequest(oid = "hello world!"))
         
         print(res.msg) 

golang调用示例(版本1.17)

     package main

     import (
         "fmt"
         CloseExpireOrderPB "xxx/services/grpc/proto/closeexpireorder"
         "golang.org/x/net/context"
         "google.golang.org/grpc"
     )

     func main() {

         //得到 gRPC 链接客户端句柄
         conn, err := grpc.Dial("[GRPC主机地址]:[GRPC主机端口]", grpc.WithInsecure())
         if err != nil {
             fmt.Println("did not connetc : ", err)
             return
         }
         
         defer conn.Close()

         //将 proto 里面的服务句柄 和 gRPC句柄绑定
         c := pb.NewCloseExpireOrder(conn)

         //远程调用 CloseExpireOrder接口
         r1, err := c.CloseExpireOrder(context.Background(), &pb.CloseExpireOrderRequest{Oid: "hello world"})
         if err != nil {
             fmt.Println("cloud not get Hello server ..", err)
             return
         }

         fmt.Println(r1.Result, r1.Msg, r1.Data)
     }

php调用示例(版本7.2)

1):使用编译或pecl方式安装grpc和protobuf扩展
   (2):使用composer安装grpc和protobuf依赖包
       composer require grpc/grpc -vvv
       composer require google/protobuf -vvv
   (3):将服务端的proto文件原样复制到你要存放的目录后运行命令生成连接文件
       protoc --php_out=. ./xxx.proto
    
   (4):创建连接客户端
   
       <?php
       /*
        * @Author: psq
        * @Date: 2022-09-07 16:14:35
        * @LastEditors: psq
        * @LastEditTime: 2022-09-07 17:15:25
        */
       
       
       class GrpcClient extends \Grpc\BaseStub
       {   
           /**
            * @description: GRPC连接初始化
            * @param {*} $channel
            * @return {*}
            */    
           public function __construct($channel = null)
           {
               parent::__construct('[GRPC主机地址]:[GRPC主机端口]', [
                   'credentials' => \Grpc\ChannelCredentials::createInsecure(),
               ], $channel);
           }
       
       
           /**
            * @description: 获取用户平台VIP等级
            * @param {UserGradeRequest} $argument
            * @param {*} $metadata
            * @param {*} $options
            * @return {*}
            */    
           public function CloseExpireOrder(\Proto\CloseExpireOrderRequest $argument, $metadata = [], $options = [])
           {   
               // 传递参数说明
                   // 0: 找到golang那边生成的pd.go文件里面的FullMethod的值原模原样填写过来即可!
                   // 1: [接收返回内容的控制器, 解析]
               return $this->_simpleRequest('/proto.CloseExpireOrder/CloseExpireOrder', $argument, ['Proto\CloseExpireOrderResponse', 'decode'], $metadata, $options);
           }
       
           // 后续有新的接口在后面加function即可
   }
   
   (5): 调用测试
   
       $request = new \Proto\CloseExpireOrderRequest();
       $request->setOid("hello world");

       $requestSend = (new GrpcClient())->CloseExpireOrder($request)->wait();
       
       list($grpcResponse, $grpcSendStatus) = $requestSend;

       dump($grpcSendStatus);
       // 注意:$grpcSendStatus->code如果等于0表示请求成功,其他表示失败
       
       dump($grpcResponse->getResult());
       dump($grpcResponse->getMsg());
       dump($grpcResponse->getData());
  
  

其他开发语言我这边暂时没用到,需要自行补充啦~~