简体   繁体   English

将服务重定向到双向 grpc

[英]Redirecting service to bidirectional grpc

I'm currently working on a grpc server that will receive streaming grpc calls from the first server and redirect these calls to the second server, and redirect responses from the second server as streams to the first one.我目前正在开发一个 grpc 服务器,它将接收来自第一台服务器的流式 grpc 调用并将这些调用重定向到第二台服务器,并将来自第二台服务器的响应作为流重定向到第一台服务器。

I have 2 proto files first proto我有 2 个 proto 文件,第一个 proto

First file:第一个文件:

syntax = "proto3";

package first.proto.pack;

service FirstProtoService {
  rpc StreamingCall(stream RequestToFirstServer) returns (stream ResponseForFirstServer){}
}

message RequestToFirstServer {
    oneof firstStreamingRequest {
        int32 x = 1;
        int32 y = 2;
    }
}

message ResponseForFirstServer {
  string someprocessedinformation = 1;
}

Second file:第二个文件:

syntax = "proto3";

package second.proto.pack;

service SecondProtoService {
  rpc StreamingCall(stream RequestToSecondServer) returns (stream ResponseFromSecondServer){}
}

message RequestToSecondServer {
  oneof secondStreamingRequest {
    int32 processedX = 1;
    int32 procesdedY = 2;
  }
}

message ResponseFromSecondServer {
  string computedInformation = 1;
}

First server knows about first proto file but doesn't know about second.第一个服务器知道第一个 proto 文件,但不知道第二个。

Second server knows about second proto file but doesn't know about first.第二个服务器知道第二个 proto 文件,但不知道第一个。

Middle server knows about first and second proto.中间服务器知道第一个和第二个原型。

Need to write a server that will transmit requests from one server from one server to another需要编写一个服务器,将来自一台服务器的请求从一台服务器传输到另一台服务器

I started writing it on Java.我开始用 Java 编写它。 But faced the problem of sending to much requests to second server但是面临向第二台服务器发送大量请求的问题

That how my service middle implementation looks on Java:我的服务中间实现在 Java 上的样子:

package middle.server.pack;


import first.proto.pack.First;
import first.proto.pack.FirstProtoServiceGrpc;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import io.grpc.stub.StreamObserver;
import second.proto.pack.Second;
import second.proto.pack.SecondProtoServiceGrpc;

import java.util.logging.LogManager;
import java.util.logging.Logger;

public class MiddleService extends FirstProtoServiceGrpc.FirstProtoServiceImplBase {
    private final ManagedChannel channel = ManagedChannelBuilder.forTarget("localhost:8080").build();
    private final Logger logger = LogManager.getLogManager().getLogger(MiddleService.class.getName());

    @Override
    public StreamObserver<First.RequestToFirstServer> streamingCall(StreamObserver<First.ResponseForFirstServer> responseObserver) {
        return new StreamObserver<First.RequestToFirstServer>() {
            @Override
            public void onNext(First.RequestToFirstServer value) {
                SecondProtoServiceGrpc.SecondProtoServiceStub stub = SecondProtoServiceGrpc.newStub(channel);
                StreamObserver<Second.RequestToSecondServer> requestObserver = stub.streamingCall(
                        new StreamObserver<Second.ResponseFromSecondServer>() {
                            @Override
                            public void onNext(Second.ResponseFromSecondServer value) {
                                doProcessOnResponse(value);
                                First.ResponseForFirstServer responseForFirstServer =
                                        mapToFirstResponse(value);
                                responseObserver.onNext(responseForFirstServer);
                            }

                            @Override
                            public void onError(Throwable t) {
                                logger.info(t.getMessage());
                            }

                            @Override
                            public void onCompleted() {
                                logger.info("sucess");
                            }
                        }
                );
                Second.RequestToSecondServer requestToSecondServer = mapToSecondRequest(value);
                requestObserver.onNext(requestToSecondServer);
                requestObserver.onCompleted();
            }

            @Override
            public void onError(Throwable t) {
                logger.info(t.getMessage());
            }

            @Override
            public void onCompleted() {
                logger.info("Everything okay");
            }
        };
    }
}

After a request from the first client on the side of the middle server, I get the following errors:从中间服务器端的第一个客户端发出请求后,我收到以下错误:

CANCELLED: Failed to read message.
CANCELLED: io.grpc.Context was cancelled without error

I know that I am doing it wrong.我知道我做错了。 So the question is how to make it right or if I can't make it on java could I make it in any other language?所以问题是如何使它正确,或者如果我不能在 Java 上制作它,我可以用任何其他语言制作它吗?

As far as I see, the problem is that in onNext() you're initiating new streamingCall to SecondProtoServiceGrpc each time MiddleService receives a new message from the FirstProtoServiceGrpc .据我所知,问题是在onNext() ,每次MiddleService收到来自FirstProtoServiceGrpc新消息时,您都会向SecondProtoServiceGrpc发起新的streamingCall SecondProtoServiceGrpc Even though it's the same stream from the first server, the first message creates its own stream to second server, the second message creates its own stream to second server, etc. That explains why you get into the situation with "sending to much requests to second server".即使它是来自第一个服务器的相同流,第一个消息创建自己的流到第二个服务器,第二个消息创建自己的流到第二个服务器,等等。这解释了为什么你会遇到“发送大量请求到第二台服务器”。

Instead, the middle layer should mirror what first server does (for this example, we're looking only at the direction first -> middle -> second).相反,中间层应该反映第一个服务器所做的事情(在这个例子中,我们只看方向 first -> middle -> second)。 When the first server creates a new stream (1) to the middle, the middle creates a new stream (2) to the second.当第一个服务器创建一个新的流(1)到中间时,中间创建一个新的流(2)到第二个。 When the middle server gets a message on the stream (1) (from the first server), it sends it to stream (2).当中间服务器在流 (1)(来自第一个服务器)上收到一条消息时,它会将其发送到流 (2)。 When stream (1) is closed, stream (2) is closed too.当流 (1) 关闭时,流 (2) 也会关闭。 And so on.等等。

Same for the opposite direction, but vice-versa.反方向相同,反之亦然。

There are several problems here:这里有几个问题:

  • as pointed by @SergiiTkachenko, you create a new RPC to the second server per each message from the first.正如@SergiiTkachenko 所指出的,您为每个来自第一个服务器的消息创建一个到第二个服务器的新 RPC。 To solve this, Move the call to the second server 3 lines up to the beginning of the outer method.为了解决这个问题,将对第二个服务器的调用移动到外部方法的开头 3 行。
  • a call to requestObserver.onCompleted() should be moved to onCompleted() of StreamObserver<First.RequestToFirstServer> few lines below (next to logger.info("Everything okay"); ).requestObserver.onCompleted()的调用应该移到StreamObserver<First.RequestToFirstServer> onCompleted()下面几行(在logger.info("Everything okay");旁边)。
  • you never call responseObserver.onCompleted() .你永远不会调用responseObserver.onCompleted() You should do so in onCompleted() of StreamObserver<Second.ResponseFromSecondServer> (next to logger.info("sucess"); ).您应该在StreamObserver<Second.ResponseFromSecondServer> onCompleted()中这样做(在logger.info("sucess");旁边)。
  • you should signal errors received from one server to the other (relation between onError(...) methods should be analogical to onCompleted() )您应该将收到的错误从一台服务器发送到另一台( onError(...)方法之间的关系应该类似于onCompleted()
  • you should handle cancellations by the first server using setOnCancelHandler(...) and propagate them to the second.您应该使用setOnCancelHandler(...)处理第一个服务器的取消并将它们传播到第二个。

So generally speaking (and as @SergiiTkachenko also pointed), receiving a given callback ( onNext , onError , onCompleted , " onCancel ") from one of the servers should trigger issuing the corresponding call to the other server (after "translating" the argument where needed).所以一般来说(正如@SergiiTkachenko 也指出的),从其中一台服务器接收给定的回调( onNextonErroronCompleted 、“ onCancel ”)应该触发向另一台服务器发出相应的调用(在“翻译”参数之后需要)。

Finally, you should respect both servers' readiness using methods from CallStreamObserver : disableAutoInboundFlowControl() , request(1) , isReady() and setOnReadyHandler(...) .最后,您应该使用CallStreamObserver 中的方法尊重两台服务器的准备情况: disableAutoInboundFlowControl()request(1)isReady()setOnReadyHandler(...) You can always cast your outbound StreamObserver s to CallStreamObserver s.您始终可以将出站StreamObserverCallStreamObserver More specifically, you should cast responseObserver to ServerCallStreamObserver and requestObserver to ClientCallStreamObserver .更具体地说,您应该将responseObserverServerCallStreamObserver并将requestObserverClientCallStreamObserver
This should be implemented criss-cross:这应该交叉实施:

  • You should request(1) message from a given server at the end of processing a message from it (in onNext() ) if the other server isReady .如果其他服务器isReady则您应该在处理来自给定服务器的消息(在onNext() )结束时从给定服务器request(1)消息。
  • receiving " onReady " callback from one server should trigger request(...) -ing 1 message from the other server.从一台服务器接收“ onReady ”回调应该触发request(...)来自另一台服务器的 1 条消息。

As far as I remember, after returning the observer from the outer method you should receive initial " onReady " callbacks from the both servers, which will put everything in motion.据我所知,从外部方法返回观察者后,您应该收到来自两台服务器的初始“ onReady ”回调,这将使一切都处于运动状态。 However I'm not 100% sure about the callback from the second server and cannot verify it at the moment: in case you don't receive the initial callback from it, simply request 1 initial message from the first server before returning the observer.但是,我不是 100% 确定来自第二个服务器的回调,目前无法验证它:如果您没有收到来自它的初始回调,只需在返回观察者之前从第一个服务器请求 1 个初始消息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM