Home > Blockchain >  How to implement two way communication with gRpc?
How to implement two way communication with gRpc?

Time:11-17

For various reasons, over the years the company I work for have invested considerably into using Windows Communication Foundation (WCF). We are currently in the process of upgrading our main development platform from Visual Studio 2015 to Visual Studio 2022. Sadly, we are discovering that our WCF client and server applications need more work than we expected to upgrade them to Visual Studio 2022 and newer .NET frameworks; they certainly are not as slick as they used to be. We are planning on keeping our existing applications that use WCF as they are but are considering other options for new applications. The main option that seems to be the one being pushed is gRpc, however, from what I have learnt about it so far, for some of the kinds of things we want to do, it would seem to be a very poor substitute. For us some of the key things we value in WCF are:

  • One way function calls (i.e. a call that so long as we know gets delivered, we do not need to wait until it completes). I understand that this is achievable with gRpc.
  • Full two way communication (i.e. clients can make calls onto the server, and also the other way around; the server can make calls onto the clients). I understand that using “streams” this can be approximated to with gRPC, but not easily.

To illustrate, consider a simplified scenario involving three application types:

  • Sound Requester. An application that requests the playing of different sounds at different locations, and also change the sound parameters.
  • Sound Player. An application that can play requested sounds and change their parameters. It can give notification that a sound has finished.
  • Sound Handler Proxy. An application that sits between sound requesters and sound players.

Our WCF implementation of this has the Sound Handler Proxy as the WCF server, the Sound Requester and Sound Player applications are clients that connect to the server. This suits us nicely; the proxy does not need to know which players and requesters to connect to, they connect to it (and can do so at any time). The proxy just provides a ‘marshalling’ service. Below is a simplified sequence diagram to illustrate the kind of calls that could be made:

enter image description here

My question is “If implementing this using gRpc, how would we implement the calls on SoundPlayer (e.g. CreateSound, PlaySound etc.)?” If my understanding is correct, since SoundPlayer is a client (not a server), we would need to implement these as streams that come as ‘returns’ from a call from the player to the server. Is there an easier way? (I appreciate that we could make the SoundPlayer a server that the SoundHandlerProxy could connect to as a client – but that would mean the proxy would need to know about all the players it is going to connect to, something we would rather avoid.) Alternatively, is there something other than gRpc that we could migrate to (preferably, something that is going to be stable for at least the next decade)?

CodePudding user response:

I'm not a .Net expert, so I may get some details wrong. But I think the gist is right.

One-way services aren't all that interesting on-the-wire, as they are still use HTTP request/response. From the One-Way Sample

When the operation is called, the service returns an HTTP status code of 202 before the service operation has executed... The client that called the operation blocks until it receives the 202 response from the service.

So they are more of a stub feature and can be emulated without too much trouble in gRPC. In gRPC, you'd define the method to return google.protobuf.Empty, and on server-side start long-running work in another thread and return immediately.

service Foo {
  rpc Bar(BarRequest) returns (google.protobuf.Empty);
}
public class FooService : Foo.FooBase
{
    public override Task<Empty> Bar(BarRequest request,
        ServerCallContext context)
    {
        startProcessingInAnotherThread(request);
        return Task.FromResult(new Empty {});
    }
}

Full two way communication seems to be the same as Duplex Services. These work by configuring the client to know how the server can reach it (its host:port). Then the client sends that information to the server for the server send RPCs back to it in the future. There are definitely security impacts of such a design and you should look into the security model you are using.

This would be emulated in gRPC by the client just sending the server a channel address string and then the server creating a channel to that address.

service SoundHandler {
  rpc Connect(ConnectRequest) returns (google.protobuf.Empty);
}

message ConnectRequest {
  enum Type {
    TYPE_UNKNOWN = 0;
    TYPE_SOUND_REQUESTER = 1;
    TYPE_SOUND_PLAYER = 2;
  };

  string channelAddress = 1;
  Type type = 2;
}

service SoundPlayer {
  rpc CreateSound(CreateSoundRequest) returns (CreateSoundResponse);
  // ...
}

service SoundEventReceiver {
  rpc SoundFinished(SoundFinishedRequest) returns (SoundFinishedResponse);
}

Then, in the service you'd create a channel to the provided address for callbacks.

public class SoundHandlerService : SoundHandler.SoundHandlerBase
{
    public override Task<Empty> Bar(ConnectRequest request,
        ServerCallContext context)
    {
        var channel = GrpcChannel.ForAddress(request.channelAddress);
        if (request.type == TYPE_SOUND_PLAYER) {
          var channel = GrpcChannel.ForAddress(request.channelAddress);
          var client = new SoundPlayer.SoundPlayerClient(channel);
          registerSoundPlayer(channel, client);
        } // ...
        return Task.FromResult(new Empty {});
    }
}
  • Related