gRPC-web 如何处理来自 server-side 流的字节数据?

How gRPC-web handles bytes data from server-side streaming?

我想使用 grpc-web 将示例视频文件从后端 grpc 服务传输到浏览器,我根据 official hello world tutorial 做了一些调整。顺便说一句,特使配置没有任何变化。视频文件被分成17块,我在浏览器中可以收到17条消息,但是里面什么都没有,我应该怎么做才能得到数据?

protobuf定义:

syntax = "proto3";

package helloworld;

service Greeter {
  rpc SayHello (HelloRequest) returns (stream HelloReply);
}

message HelloRequest {}

message HelloReply {
  bytes message = 1;
}

server.js:

var PROTO_PATH = __dirname + '/helloworld.proto';

var grpc = require('grpc');
var fs = require('fs');
var protoLoader = require('@grpc/proto-loader');
var packageDefinition = protoLoader.loadSync(
    PROTO_PATH,
    {keepCase: true,
     longs: String,
     enums: String,
     defaults: true,
     oneofs: true
    });
var protoDescriptor = grpc.loadPackageDefinition(packageDefinition);
var helloworld = protoDescriptor.helloworld;

function doSayHello(call) {
  let count = 0;
  let videoDataStream = fs.createReadStream('./sample.mp4');
  videoDataStream.on('data',function(chunk){
      console.log(chunk);
      console.log(++count);
      call.write({videoStream: chunk});
//      call.write(chunk);
  }).on('end',function(){
      call.end();
  });
}

function getServer() {
  var server = new grpc.Server();
  server.addService(helloworld.Greeter.service, {
    sayHello: doSayHello,
  });
  return server;
}

if (require.main === module) {
  var server = getServer();
  server.bind('0.0.0.0:9090', grpc.ServerCredentials.createInsecure());
  server.start();
}

exports.getServer = getServer;

client.js:

const {HelloRequest, HelloReply} = require('./helloworld_pb.js');
const {GreeterClient} = require('./helloworld_grpc_web_pb.js');

var client = new GreeterClient('http://localhost:8080');

var request = new HelloRequest();

client.sayHello(request).on('data', function(chunk){
  //console.log(chunk.getMessage());
  console.log(chunk);
});

无论如何,如果代理有问题,下面是我的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: listener_0
    address:
      socket_address: { address: 0.0.0.0, port_value: 8080 }
    filter_chains:
    - filters:
      - name: envoy.http_connection_manager
        config:
          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
                  max_grpc_timeout: 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.grpc_web
          - name: envoy.cors
          - name: envoy.router
  clusters:
  - name: greeter_service
    connect_timeout: 0.25s
    type: logical_dns
    http2_protocol_options: {}
    lb_policy: round_robin
    hosts: [{ socket_address: { address: host.docker.internal, port_value: 9090 }}]

服务器端记录的字节数:

浏览器控制台输出以下:

您是否尝试过使用 gRPC(而不是 gRPC Web)客户端进行测试以消除代理问题的可能性?

我不是很熟悉 Node.JS 实现,但是...

不应该是server.addProtoService(...)吗?

服务器流式传输的消息也是:

message HelloReply {
  bytes message = 1;
}

但是你:

call.write({videoStream: chunk});

不应该是:

call.write({message: chunk});