美文网首页
envoy+grpc-json transcoder将grpc接

envoy+grpc-json transcoder将grpc接

作者: georgeguo | 来源:发表于2020-08-05 18:01 被阅读0次

项目中服务都是以GRPC的方式提供,但是为了和前台对接,需要以Restful的方式提供接口。于是研究了GPRC到Restful接口的转换方法。常用的转换方法有一下3种。

  • grpc + envoy + grpc-web,grpc-web是一套js库,前台通过grpc-web和envoy实现和grpc服务的交互。该方法的缺点是前台仍然需要拿到proto文件,然后生成对应的js文件才可以调用服务。无法达到服务对调用者透明的效果。
  • grpc + grpc-gateway,grpc-gateway是go实现的一个代理转发库,未实验。
  • grpc + envoy + grpc-json transcoder,本文主要通过这种方式实现grpc服务的Restful接口化。

无论使用grpc-web还是grpc-json transcode都需要envoy的转发。下面先说明如何使用envoy。

1 envoy的使用

envoy是由C++开发的一个代理服务,功能非常强大。可以编译安装,也可以直接使用docker。本文实验直接通过docker的方式使用envoy。具体操作命令如下:

1.1 下载镜像

docker pull envoyproxy/envoy-dev

1.2 启动envoy

docker run --name envoy --rm -d  -p 5858:5858 -p 9901:9901 \
-v /root/service/etc/envoy:/etc/envoy \
envoyproxy/envoy-dev

各个参数的说明:

  • --name,启动的容器名称。
  • 5858为监听器端口, 9901为管理端口,端口号和后面的envoy.yaml文件中的配置相对应。
  • -v /root/service/etc/envoy:/etc/envoy,将envoy.yaml和proto.pb映射到envoy的默认配置路径下。

envoy涉及的概念较多,可以先参考基本概念,理解envoy配置文件中各个参数的含义。

2 创建grpc服务

2.1 编写grpc的proto文件

本实验中使用的proto文件wind.proto的内容如下,

syntax = "proto3";

import "google/api/annotations.proto";

package wind_power;

service WindServer {
    rpc wind_predict(Request) returns (Response) {
        option (google.api.http) = {
          get: "/predict"
        };
    }
    
    rpc send_data(Request) returns (Response) {
        option (google.api.http) = {
          post: "/send",
          body: "*"
        };
    }
}

message Request {
    string content = 1;
}

message Response {
    string msg = 1;
    int32 code = 2;
}

proto中添加了两个接口,分别用于测试get方法和post方法。

2.2 生成proto.pb描述文件以及wind.proto对应的pb文件

python -m grpc_tools.protoc -I../../googleapis-master -I. \
--include_imports --include_source_info --descriptor_set_out=proto.pb \
--python_out=.. --grpc_python_out=.. wind.proto

执行上述命令之后会生成3个文件,proto.pb/wind_pb2.py/wind_pb2_grpc.py。其中proto.pb为对应的协议描述文件,后面envoy.yaml中需要配置该文件所在的路径。

注意:需要先将googleapis这个googleapis项目下载都指定的路径下,并将上述命令中的第一个-I替换成googleapis所在的路径。

2.3 grpc服务端代码

服务文件名称wind_predict_srv.py

# -*- coding: utf-8 -*- 
# --------------------------------
# Name:     wind_predict_srv.py
# Author:   george
# Date:     2020/7/25
# --------------------------------

import grpc
import logging
from concurrent import futures

import proto.wind_pb2 as wind_pb2
import proto.wind_pb2_grpc as wind_pb2_grpc

class WindPredictSrv(wind_pb2_grpc.WindServerServicer):

    def wind_predict(self, request, context):
        print("call wind_predict")
        return wind_pb2.Response(msg='%s!' % request.content)
        
    def send_data(self, request, context):
        print("call send_data")
        return wind_pb2.Response(msg='%s!' % request.content)

def server():
    grpc_server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    wind_pb2_grpc.add_WindServerServicer_to_server(WindPredictSrv(), grpc_server)
    grpc_server.add_insecure_port('[::]:50052')
    grpc_server.start()
    grpc_server.wait_for_termination()

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

2.4 grpc客户端代码

客户端文件名称wind_predict_client.py

import grpc
import logging

import proto.wind_pb2 as wind_pb2
import proto.wind_pb2_grpc as wind_pb2_grpc

def run():
    option = [('grpc.keepalive_timeout_ms', 10000)]
    with grpc.insecure_channel(target='127.0.0.1:50052', options=option) as channel:
        stub = wind_pb2_grpc.WindServerStub(channel)
        request = wind_pb2.Request(content='hello grpc')
        response = stub.wind_predict(request, timeout=10)
    print("Greeter client received: " + response.msg)

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

2.5 测试grpc服务是否正常

启动服务端

python wind_predict_srv.py

新启动一个窗口,使用grpc客户端测试grpc服务

python wind_predict_client.py

若打印Greeter client received: hello grpc!,说明grpc服务构建成功。

3 配置envoy

envoy的使用主要是通过envoy的配置文件实现。本实验中envoy的配置文件如下,配置文件名称envoy.yaml。

admin:
  access_log_path: /tmp/admin_access.log
  address:
    socket_address: { address: 0.0.0.0, port_value: 9901 }

static_resources:
  listeners:
  - name: listener1
    address:
      socket_address: { address: 0.0.0.0, port_value: 5858 }
    filter_chains:
    - filters:
      - name: envoy.filters.network.http_connection_manager
        typed_config:
          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
          stat_prefix: grpc_json
          codec_type: AUTO
          route_config:
            name: local_route
            virtual_hosts:
            - name: local_service
              domains: ["*"]
              routes:
              - match: { prefix: "/wind_power.WindServer" }
                route: { cluster: grpc, timeout: { seconds: 60 } }
          http_filters:
          - name: envoy.filters.http.grpc_json_transcoder
            typed_config:
              "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_json_transcoder.v3.GrpcJsonTranscoder
              proto_descriptor: "/etc/envoy/proto.pb"
              services: ["wind_power.WindServer"]
              print_options:
                add_whitespace: true
                always_print_primitive_fields: true
                always_print_enums_as_ints: false
                preserve_proto_field_names: false
          - name: envoy.filters.http.router

  clusters:
  - name: grpc
    connect_timeout: 1.25s
    type: logical_dns
    lb_policy: round_robin
    dns_lookup_family: V4_ONLY
    http2_protocol_options: {}
    upstream_connection_options:
      tcp_keepalive:
        keepalive_time: 300
    load_assignment:
      cluster_name: grpc
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: 0.0.0.0
                port_value: 50052

4 请求验证

grpc服务正常启动,envoy的yaml文件编辑好且envoy正常启动就可以测试请求了。

4.1 测试get请求

 curl http://localhost:5858/predict?content=george

4.2 测试post方式请求

curl -H "Content-Type:application/json" -X POST -d '{"content": "hello grpc"}'  \
http://localhost:5858/send

注意:这里名的predict和send方法是在proto文件中通过option的方式绑定的,如:

    rpc wind_predict(Request) returns (Response) {
        option (google.api.http) = {
          get: "/predict"
        };
    }

FAQ

问题1:upstream connect error or disconnect/reset before headers. reset reason: connection failure

出现这种情况一般都是因为网络问题。实验中启动envoy容器时没有指定网络,默认使用的是bridge网络,而gprc服务是在宿主机上的,二者不在同一个网段,这种情况下访问就会出现上面的问题。解决方法:

  • ① 启动容器时指定host网络,即添加 --network="host"。
  • ② 将grpc服务和envoy放置在同一个网段的网络上。

参考

anvoy + grpc-json 参考

相关文章

网友评论

      本文标题:envoy+grpc-json transcoder将grpc接

      本文链接:https://www.haomeiwen.com/subject/agiyrktx.html