grpc-web使用

 

一,grpc-web数据交互支持

二,proto协议编译js文件

hello.proto文件

 1 syntax = "proto3";
 2 
 3 package api;
 4 
 5 // 这里可以写服务注释
 6 service HelloWorldService {
 7     // 这里可以写方法注释
 8     rpc SayHello (HelloRequest) returns (HelloResponse) {}
 9 }
10 
11 // 这里可以写请求结构注释
12 message HelloRequest {
13     // 这里可以写参数注释
14     string name = 1;
15 }
16 
17 // 这里可以写响应结构注释
18 message HelloResponse {
19     // 这里可以写参数注释
20     string message = 1;
21 }

环境准备

cd /home/shimon/pkg

# 将下载后的内容移动到bin路径中方便使用
mv protoc-gen-grpc-web-1.4.1-linux-x86_64 /usr/local/bin/protoc-gen-grpc-web
# 增加可执行权限
chmod +x /usr/local/bin/protoc-gen-grpc-web

# 根据hello.proto生成js调用文件.如果协议中使用stream,则使用mode=grpcwebtext编译
sudo protoc hello.proto --js_out=import_style=commonjs,binary:./output  --grpc-web_out=import_style=commonjs,mode=grpcwebtext:./output

 

三,前端调用grpc服务

a.将hello_grpc_web_pb.js和hello_pb.js移到前端pb目录下

b.项目安装依赖yarn add google-protobuf grpc-web

c.调用代码:

import { HelloRequest } from './pb/hello_pb';
import { HelloWorldServiceClient } from './pb/hello_grpc_web_pb';

// 注意这个端口是代理服务器的端口,不是grpc的端口
const client = new HelloWorldServiceClient('http://localhost:8889',
    null, null);

const request = new HelloRequest();
request.setName('World');

client.sayHello(request, {}, (err, response) => {
    console.log(response.getMessage());
});

四,代理请求

由于浏览器的特性,gRPC-web其实没办法直接向gRPC-server发送HTTP/2请求的,只有通过envoy(推荐)/nginx代理,将来自gRPC-web的HTTP/1的请求转换为gRPC-server能够接收的HTTP/2请求

envoy配置:

envoy.yaml

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

static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address: { address: 0.0.0.0, port_value: 8889 }
      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
              codec_type: auto
              stat_prefix: ingress_http
              route_config:
                name: local_route
                virtual_hosts:
                  - name: local_service
                    domains: ["*"]
                    routes:
                      - match: { prefix: "/" }
                        route:
                          cluster: greeter_service
                          timeout: 0s
                          max_stream_duration:
                            grpc_timeout_header_max: 0s
                    cors:
                      allow_origin_string_match:
                        - prefix: "*"
                      allow_methods: GET, PUT, DELETE, POST, OPTIONS
                      allow_headers: 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
                      max_age: "1728000"
                      expose_headers: custom-header-1,grpc-status,grpc-message
              http_filters:
                - name: envoy.filters.http.grpc_web
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.grpc_web.v3.GrpcWeb
                - name: envoy.filters.http.cors
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.cors.v3.Cors
                - name: envoy.filters.http.router
                  typed_config:
                    "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
  clusters:
    - name: greeter_service
      connect_timeout: 0.25s
      type: logical_dns
      http2_protocol_options: {}
      lb_policy: round_robin
      # win/mac hosts: Use address: host.docker.internal instead of address: localhost in the line below
      load_assignment:
        cluster_name: cluster_0
        endpoints:
          - lb_endpoints:
            - endpoint:
                address:
                  socket_address:
                    address: 192.168.0.1
                    port_value: 8888
sudo docker run --name redirect_envoy -p 8889:8889 -p 8890:8890 -v ${PWD}/envoy.yaml:/etc/envoy/envoy.yaml -d --restart=always envoyproxy/envoy:v1.24-latest

 

nginx配置:

但是此处有限制,只能使用mode=grpcweb编译的js,如果使用grpcwebtext编译的js则报跨域问题,暂时未找到解决办法

此配置文件在${PWD}/nginx/conf.d/目录下

server {
  listen 8889;
  server_name _;

  access_log /tmp/grpc.log;
  error_log /tmp/grpc.log debug;

  location ~ \.(html|js)$ {
    root /var/www/html;
  }
  location / {
    # 重点!!需要将Content-Type更改为 application/grpc
    # grpc-web过来的是application/grpc-web+proto || application/grpc-web+text (取决于生成js代码时grpc-web_out 的mode选项,本文用grpcweb 则为application/grpc-web+proto)
    grpc_set_header Content-Type application/grpc;
    grpc_pass 192.168.0.1:8888;
    # 因浏览器有跨域限制,这里直接在nginx支持跨域
    if ($request_method = 'OPTIONS') {
      add_header 'Access-Control-Allow-Origin' '*';
      add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
      add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,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';
      add_header 'Access-Control-Max-Age' 1728000;
      add_header 'Content-Type' 'text/plain charset=UTF-8';
      add_header 'Content-Length' 0;
      return 204;
    }

    if ($request_method = 'POST') {
      add_header 'Access-Control-Allow-Origin' '*';
      add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
      add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,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';
      add_header 'Access-Control-Expose-Headers' 'Content-Transfer-Encoding, grpc-message,grpc-status';
    }
  }
}
sudo docker run --name redirect_nginx -p 8889:8889 -v ${PWD}/nginx/conf.d:/etc/nginx/conf.d -v ${PWD}/nginx/logs:/tmp --privileged=true -d --restart=always nginx

 

posted @ 2022-10-25 10:38  卓扬  阅读(1154)  评论(0编辑  收藏  举报