成功最有效的方法就是向有经验的人学习!

grpc专题分析

什么是grpc

gRpc http://www.grpc.io/ 是来自 Google 的 RPC 框架。它使用协议缓冲区作为底层 序列化 /IDL(接口描述语言的缩写) 格式。在传输层,它使用 HTTP/2 进行请求/响应复用。Envoy 在传输层和应用层都提供对 gRPC 的一流支持:

  • gRPC 使用 HTTP/2 trailers 特性(可以在 HTTP 请求和响应报文后追加 HTTP Header)来传送请求状态。Envoy 是能够正确支持 HTTP/2 trailers 的少数几个 HTTP 代理之一,因此也是可以传输 gRPC 请求和响应的代理之一。
  • 某些语言的 gRPC 运行时相对不成熟。Envoy 支持 gRPC 桥接过滤器(https://www.servicemesher.com/envoy/configuration/http_filters/grpc_http1_bridge_filter.html#config-http-filters-grpc-bridge),允许 gRPC 请求通过 HTTP/1.1 发送给 Envoy。然后,Envoy 将请求转换为 HTTP/2 以传输到目标服务器。该响应被转换回 HTTP/1.1。
  • 安装后,除了标准的全局 HTTP 统计数据之外,桥接过滤器还会根据每个 RPC 统计数据进行收集。
  • gRPC-Web 由一个指定的过滤器(https://www.servicemesher.com/envoy/configuration/http_filters/grpc_web_filter.html#config-http-filters-grpc-web)支持,该过滤器允许 gRPC-Web 客户端通过 HTTP/1.1 向 Envoy 发送请求并代理到 gRPC 服务器。
  • gRPC-JSON 转码器由一个指定的过滤器(https://www.servicemesher.com/envoy/configuration/http_filters/grpc_json_transcoder_filter.html#config-http-filters-grpc-json-transcoder)支持,该过滤器允许 RESTful JSON API 客户端通过 HTTP 向 Envoy 发送请求并获取代理到 gRPC 服务。

grpc相关过滤器有哪些

gRPC HTTP/1.1 Bridge

允许grpc请求通过http/1/1方式发送到envoy,然后envoy转换成http/2或http/3传输到目标服务器。响应会转换回http/1.1

gRPC HTTP/1.1 Reverse Bridge

允许grpc请求发送到envoy,然后转换成http/1.1,到上游。响应转换回grpc到下游。上游可以不感知grpc的存在。

gRPC statistics

这个过滤器启用grpc调用遥测。他会计算成功和失败的调用,把他们通过方法名称分组。

gRPC Web

grpc-web是一个js客户端库,他支持相同的api如grpc-node方式访问grpc服务。

这个过滤器启用grpc-web客户端和grpc服务器桥接。

gRPC-JSON transcoder

这个过滤器允许restful json api客户端请求envoy用http的方式,代理到grpc服务端。

实战

gRPC HTTP/1.1 Bridge

例子来自:https://github.com/envoyproxy/envoy/tree/main/examples/grpc-bridge

bridge-deploy.yaml

kubectl apply -f bridge-deploy.yaml -n istio
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grpc-server
  labels:
    app: grpc-server
spec:
  selector:
    matchLabels:
      app: grpc-server
  replicas: 1
  template:
    metadata:
      labels:
        app: grpc-server
        version: v1
    spec:
      containers:
        - name: csrf
          image: registry.cn-beijing.aliyuncs.com/hxpdocker/grpc-bridge-server:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 8081
---
apiVersion: v1
kind: Service
metadata:
  name: grpc-server
spec:
  selector:
    app: grpc-server
  ports:
    - name: grpc-server
      protocol: TCP
      port: 8081
      targetPort: 8081
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grpc-client
  labels:
    app: grpc-client
spec:
  selector:
    matchLabels:
      app: grpc-client
  replicas: 1
  template:
    metadata:
      labels:
        app: grpc-client
        version: v1
    spec:
      containers:
        - name: csrf
          image: registry.cn-beijing.aliyuncs.com/hxpdocker/grpc-bridge-client:latest
          imagePullPolicy: Always
          env:
          - name: CLIENT_PROXY
            value: http://grpc-server:8081

ef-bridge-client.yaml

kubectl apply -f ef-bridge-client.yaml -n istio
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: grpc-client
spec:
  workloadSelector:
    labels:
      app: grpc-client
  configPatches:
  - applyTo: HTTP_FILTER
    match:
        context: SIDECAR_OUTBOUND
        listener:
          name: 0.0.0.0_8081
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
    patch:
        operation: INSERT_BEFORE
        value:           
          name: envoy.filters.http.grpc_http1_bridge
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config

测试:

 kubectl exec -it -n istio grpc-client-5f86876755-564t6 -- /bin/bash
 /client/grpc-kv-client.py set foo bar2

 [root@node01 ~]# kubectl logs -f -n istio grpc-server-59ff9799c4-7pcqn 
2021/10/15 03:14:36 starting grpc on :8081
2021/10/15 03:33:10 set: foo = bar
2021/10/15 03:33:20 get: foo
2021/10/15 03:33:40 set: foo = bar
2021/10/15 03:33:44 get: foo
2021/10/15 03:33:50 set: foo = bar2
2021/10/15 03:33:53 get: foo
2021/10/15 03:34:37 get: foo
2021/10/15 03:41:41 set: foo = bar2
2021/10/15 03:42:01 set: foo = bar2

gRPC HTTP/1.1 Reverse Bridge

例子来自:https://github.com/sp-manuel-jurado/istio-grpc-http1-reverse-bridge

reverse-bridge-deploy.yaml

kubectl apply -f reverse-bridge-deploy.yaml -n istio
---
apiVersion: v1
kind: Service
metadata:
  name: ping-http
  labels:
    app: ping-http
spec:
  ports:
  - port: 8888
    name: http
  selector:
    app: ping-http
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ping-http
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ping-http
  template:
    metadata:
      labels:
        app: ping-http
    spec:
      containers:
      - name: ping-http
        image: registry.cn-beijing.aliyuncs.com/hxpdocker/ping-http:latest
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
        imagePullPolicy: Always
        ports:
        - containerPort: 8888
---
apiVersion: v1
kind: Service
metadata:
  name: ping-grpc
  labels:
    app: ping-grpc
spec:
  ports:
  - port: 10005
    name: grpc
  selector:
    app: ping-grpc
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ping-grpc
spec:
  replicas: 1
  selector:
    matchLabels:
      app: ping-grpc
  template:
    metadata:
      labels:
        app: ping-grpc
    spec:
      containers:
      - name: ping-grpc
        image: registry.cn-beijing.aliyuncs.com/hxpdocker/ping-grpc:latest
        resources:
          requests:
            cpu: "100m"
            memory: "128Mi"
        imagePullPolicy: Always
        ports:
        - containerPort: 10005

ef-reverse-bridge.yaml

kubectl  apply -f ef-reverse-bridge.yaml -n istio
---
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: lab-test
spec:
  workloadSelector:
    labels:
      app: ping-http
  configPatches:
    - applyTo: HTTP_FILTER
      match:
        context: SIDECAR_INBOUND
        listener:
          portNumber: 8888 
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
      patch:
        operation: INSERT_BEFORE
        value:
          name: envoy.filters.http.grpc_http1_reverse_bridge
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_reverse_bridge.v3.FilterConfig
            content_type: application/grpc+proto
            withhold_grpc_frames: true

测试

 grpcurl -v -proto ./proto/SP/Rpc/ping_service.proto -plaintext -import-path ./proto -d '{}'  10.68.10.2:10005 sp.rpc.PingService/Ping

 Resolved method descriptor:
rpc Ping ( .google.protobuf.Empty ) returns ( .google.protobuf.Empty );

Request metadata to send:
(empty)

Response headers received:
content-type: application/grpc
date: Fri, 15 Oct 2021 05:16:59 GMT
server: istio-envoy
x-envoy-decorator-operation: ping-grpc.istio.svc.cluster.local:10005/*
x-envoy-upstream-service-time: 0

Response contents:
{

}

Response trailers received:
(empty)
Sent 1 request and received 1 response

[root@node01 istio-grpc-http1-reverse-bridge]# grpcurl -v -proto ./proto/SP/Rpc/ping_service.proto -plaintext -import-path ./proto -d '{}'  10.68.117.193:8888 sp.rpc.PingService/Ping

Resolved method descriptor:
rpc Ping ( .google.protobuf.Empty ) returns ( .google.protobuf.Empty );

Request metadata to send:
(empty)

Response headers received:
content-type: application/grpc
date: Fri, 15 Oct 2021 05:23:09 GMT
server: istio-envoy
x-envoy-decorator-operation: ping-http.istio.svc.cluster.local:8888/*
x-envoy-upstream-service-time: 0

Response contents:
{

}

Response trailers received:
(empty)
Sent 1 request and received 1 response

gRPC statistics

例子来自:https://github.com/envoyproxy/envoy/tree/main/examples/grpc-bridge

bridge-deploy.yaml

kubectl apply -f bridge-deploy.yaml -n istio
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grpc-server
  labels:
    app: grpc-server
spec:
  selector:
    matchLabels:
      app: grpc-server
  replicas: 1
  template:
    metadata:
      labels:
        app: grpc-server
        version: v1
    spec:
      containers:
        - name: csrf
          image: registry.cn-beijing.aliyuncs.com/hxpdocker/grpc-bridge-server:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 8081
---
apiVersion: v1
kind: Service
metadata:
  name: grpc-server
spec:
  selector:
    app: grpc-server
  ports:
    - name: grpc-server
      protocol: TCP
      port: 8081
      targetPort: 8081
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: grpc-client
  labels:
    app: grpc-client
spec:
  selector:
    matchLabels:
      app: grpc-client
  replicas: 1
  template:
    metadata:
      labels:
        app: grpc-client
        version: v1
    spec:
      containers:
        - name: csrf
          image: registry.cn-beijing.aliyuncs.com/hxpdocker/grpc-bridge-client:latest
          imagePullPolicy: Always
          env:
          - name: CLIENT_PROXY
            value: http://grpc-server:8081

ef-statistics-client.yaml

kubectl apply -f ef-statistics-client.yaml -n istio
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: grpc-client
spec:
  workloadSelector:
    labels:
      app: grpc-client
  configPatches:
  - applyTo: HTTP_FILTER
    match:
        context: SIDECAR_OUTBOUND
        listener:
          name: 0.0.0.0_8081
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
    patch:
        operation: INSERT_BEFORE
        value:           
          name: envoy.filters.http.grpc_http1_bridge
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_http1_bridge.v3.Config
  - applyTo: HTTP_FILTER
    match:
        context: SIDECAR_OUTBOUND
        listener:
          name: 0.0.0.0_8081
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
    patch:
        operation: INSERT_BEFORE
        value:           
          name: envoy.filters.http.grpc_stats
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_stats.v3.FilterConfig
            emit_filter_state: true
            stats_for_all_methods: true
            enable_upstream_stats: true

grpc web

web-grpc-deploy.yaml

kubectl apply -f web-grpc-deploy.yaml -n istio
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-grpc
  labels:
    app: web-grpc
spec:
  selector:
    matchLabels:
      app: web-grpc
  replicas: 1
  template:
    metadata:
      labels:
        app: web-grpc
        version: v1
    spec:
      containers:
        - name: csrf
          image: registry.cn-hangzhou.aliyuncs.com/hxpdocker/server:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 50051
---
apiVersion: v1
kind: Service
metadata:
  name: web-grpc
spec:
  selector:
    app: web-grpc
  ports:
    - name: grpc-web-grpc
      protocol: TCP
      port: 50051
      targetPort: 50051
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-grpc-client
  labels:
    app: web-grpc-client
spec:
  selector:
    matchLabels:
      app: web-grpc-client
  replicas: 1
  template:
    metadata:
      labels:
        app: web-grpc-client
        version: v1
    spec:
      containers:
        - name: csrf
          image: registry.cn-hangzhou.aliyuncs.com/hxpdocker/client:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  name: web-grpc-client
spec:
  selector:
    app: web-grpc-client
  ports:
    - name: http-web-grpc-client
      protocol: TCP
      port: 8080
      targetPort: 8080

注意svc的port的名字grpc-web-grpc,表名用的是grpc协议

vs

vs-web-grpc.yaml

kubectl apply -f vs-web-grpc.yaml -n istio
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: web-grpc
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        prefix: /s12.example
    corsPolicy:
      allowOrigins:
      - exact: http://192.168.198.154:30986
      - exact: http://192.168.198.154:8081
      allowMethods:
      - GET
      - OPTIONS
      - POST
      - PUT
      - DELETE
      maxAge: "1m"
      allowHeaders:
      - keep-alive
      - user-agent
      - cache-control
      - content-type
      - content-transfer-encoding
      - custom-header-1
      - x-accept-content-transfer-encoding
      - x-accept-response-streaming
      - x-user-agent
      - x-grpc-web
      - grpc-timeout
      exposeHeaders: 
      - custom-header-1
      - grpc-status
      - grpc-message
    route:
    - destination:
        host: web-grpc.istio.svc.cluster.local
        port:
          number: 50051

vs-web-grpc-client.yaml

kubectl apply -f vs-web-grpc-client.yaml -n istio
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
  - "*"
  gateways:
  - bookinfo-gateway
  http:
  - match:
    - uri:
        prefix: /
    route:
    - destination:
        host: web-grpc-client.istio.svc.cluster.local
        port:
          number: 8080

ef-web-grpc.yaml

kubectl apply -f ef-web-grpc.yaml -n istio
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: grpc
spec:
  workloadSelector:
    labels:
      app: web-grpc
  configPatches:
  - applyTo: HTTP_FILTER
    match:
        listener:
          filterChain:
            destinationPort: 50051
            transportProtocol: "tls"
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: istio.metadata_exchange
    patch:
        operation: INSERT_BEFORE
        value:           
          name: envoy.filters.http.grpc_web
          typed_config:
            "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb

访问:

http://192.168.198.154:30986/

gRPC-JSON transcoder

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

生成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

需要先将googleapis这个项目(https://github.com/googleapis/googleapis)下载都指定的路径下,并将上述命令中的第一个-I替换成googleapis所在的路径。

服务端代码:

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

客户端代码:

import grpc
import logging
import time
import sys

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

def run():
    option = [('grpc.keepalive_timeout_ms', 10000)]
    while True:
        with grpc.insecure_channel(target='wind-server: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)
        sys.stdout.flush()
        time.sleep(2)
if __name__ == '__main__':
    logging.basicConfig()
    run()

部署文件

grpc-json-deploy.yaml

kubectl apply -f grpc-json-deploy.yaml -n istio
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wind-server
  labels:
    app: wind-server
spec:
  selector:
    matchLabels:
      app: wind-server
  replicas: 1
  template:
    metadata:
      labels:
        app: wind-server
        version: v1
    spec:
      containers:
        - name: csrf
          image: registry.cn-beijing.aliyuncs.com/hxpdocker/wind-server:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 50052
---
apiVersion: v1
kind: Service
metadata:
  name: wind-server
spec:
  selector:
    app: wind-server
  ports:
    - name: grpc-wind-server
      protocol: TCP
      port: 50052
      targetPort: 50052
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wind-client
  labels:
    app: wind-client
spec:
  selector:
    matchLabels:
      app: wind-client
  replicas: 1
  template:
    metadata:
      labels:
        app: wind-client
        version: v1
    annotation:
      sidecar.istio.io/userVolume: '[{"name":"proto-file","hostPath":{"path":"/var/lib/"}}]’ 
      sidecar.istio.io/userVolumeMount: '[{"mountPath":"/etc/envoy/","name":"proto-file"}]'
    spec:
      containers:
        - name: csrf
          image: registry.cn-beijing.aliyuncs.com/hxpdocker/wind-client:latest
          imagePullPolicy: Always

client 使用了hostPath,这个目录放pb文件

grpc-json-client.yaml

kubectl apply -f grpc-json-client.yaml -n istio
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
  name: grpc
spec:
  workloadSelector:
    labels:
      app: wind-client
  configPatches:
  - applyTo: NETWORK_FILTER
    match:
        context: SIDECAR_OUTBOUND
        listener:
          name: 0.0.0.0_50052
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
    patch:
        operation: MERGE
        value:   
          name: envoy.filters.network.http_connection_manager
          typedConfig:
              '@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: outbound|50052||wind-server.istio.svc.cluster.local
                      timeout: 60s
  - applyTo: HTTP_FILTER
    match:
        context: SIDECAR_OUTBOUND
        listener:
          name: 0.0.0.0_50052
          filterChain:
            filter:
              name: envoy.filters.network.http_connection_manager
              subFilter:
                name: envoy.filters.http.router
    patch:
        operation: INSERT_BEFORE
        value:   
                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
赞(0) 打赏
未经允许不得转载:陈桂林博客 » grpc专题分析
分享到

大佬们的评论 抢沙发

全新“一站式”建站,高质量、高售后的一条龙服务

微信 抖音 支付宝 百度 头条 快手全平台打通信息流

橙子建站.极速智能建站8折购买虚拟主机

觉得文章有用就打赏一下文章作者

非常感谢你的打赏,我们将继续给力更多优质内容,让我们一起创建更加美好的网络世界!

支付宝扫一扫打赏

微信扫一扫打赏

登录

找回密码

注册