美文网首页
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