一般转发 GRPC 调用

Generically forwarding a GRPC call

我有一个 GRPC API,其中在重构之后重命名了一些包。这包括我们定义 API 的原型文件之一中的 package 声明。像这样:

package foo;

service BazApi {
    rpc FooEventStream(stream Ack) returns (stream FooEvent);
}

已更改为

package bar;

service BazApi {
    rpc FooEventStream(stream Ack) returns (stream FooEvent);
}

服务器端使用 grpc-java 实现,顶部是 scala 和 monix。

这对于使用新 proto 文件的客户端来说一切正常,但对于构​​建在旧 proto 文件之上的旧客户端,这会导致问题:UNIMPLEMENTED: Method not found: foo.BazApi/FooEventStream.

通过 GRPC API 传递的消息的实际数据格式没有改变,只有包。

由于我们需要保持向后兼容性,我一直在寻找一种方法,让旧客户端在保持名称更改的同时也能正常工作。

我希望使用通用的 ServerInterceptor 来完成这项工作,它能够检查来电,看看它来自旧客户端(我们在 headers 中有客户端版本) 和 redirect/forward 它到重命名的服务。 (因为只是更改了包名称,所以很容易弄清楚,例如 foo.BazApi/FooEventStream -> bar.BazApi/FooEventStream

但是,似乎没有一种优雅的方法可以做到这一点。我认为可以通过将新的 ClientCall 启动到正确的端点,然后通过委托给 ClientCall 在拦截器中处理 ServerCall 来实现,但这将需要一堆管道代码来妥善处理 unary/clientStreaming/serverStreaming/bidiStreaming 个电话。

有更好的方法吗?

使用较低级别的 "channel" API 你可以不用 太多的工作就可以创建一个代理。您主要只是代理从 ServerCall.ListenerClientCallClientCall.ListenerServerCall 的事件。您将了解较低级别的 MethodDescriptor 和很少使用的 HandlerRegistry。处理流量控制也有一些复杂性(isReady()request())。

前段时间我做了一个例子,但一直没有花时间将它合并到 grpc-java 本身。当前可用 on my random branch。您应该能够通过更改 localhost:8980 并重写传递给 channel.newCall(...)MethodDescriptor 来让它工作。类似于:

MethodDescriptor desc = serverCall.getMethodDescriptor();
if (desc.getFullMethodName().startsWith("foo.BazApi/")) {
  String newName = desc.getFullMethodName().replace("foo.BazApi/", "bar.BazApi/");
  desc = desc.toBuilder().setFullMethodName(newName).build();
}
ClientCall<ReqT, RespT> clientCall
    = channel.newCall(desc, CallOptions.DEFAULT);

如果您可以轻松更改服务器,则可以同时支持这两个名称。您可以考虑使用两个不同的描述符注册两次服务的解决方案。

每个服务都有一个 bindService() 方法,即 returns 一个 ServerServiceDefinition。您可以通过正常的 serverBuilder.addService().

将定义传递给服务器

所以你可以得到正常的ServerServiceDefinition然后改写成新的名字然后注册新的名字

BazApiImpl service = new BazApiImpl();
serverBuilder.addService(service); // register "bar"

ServerServiceDefinition barDef = service.bindService();
ServerServiceDefinition fooDefBuilder = ServerServiceDefinition.builder("foo.BazApi");
for (ServerMethodDefinition<?,?> barMethodDef : barDef.getMethods()) {
  MethodDescriptor desc = barMethodDef.getMethodDescriptor();
  String newName = desc.getFullMethodName().replace("foo.BazApi/", "bar.BazApi/");
  desc = desc.toBuilder().setFullMethodName(newName).build();
  foDefBuilder.addMethod(desc, barMethodDef.getServerCallHandler());
}
serverBuilder.addService(fooDefBuilder.build()); // register "foo"