Click here to Skip to main content
15,879,326 members
Articles / Programming Languages / C#

Bidirectional Messages Streaming with .NET Core 3.0 Using gRPC

Rate me:
Please Sign up or sign in to vote.
4.56/5 (18 votes)
26 Jul 2019CPOL4 min read 40.1K   1.2K   19   21
The article presents compact and simple-to-use infrastructure for full duplex messages streaming with .NET Core 3.0 and gRPC.

Introduction

One of the most important new features of .NET Core 3.0 is its support for gRPC messaging. Wikipedia describes gRPC as follows:

gRPC (gRPC Remote Procedure Calls) is an open source remote procedure call (RPC) system initially developed at Google. It uses HTTP/2 for transport, Protocol Buffers as the interface description language, and provides features such as authentication, bidirectional streaming and flow control, blocking or nonblocking bindings, and cancellation and timeouts. It generates cross-platform client and server bindings for many languages. Most common usage scenarios include connecting services in microservices style architecture and connect mobile devices, browser clients to backend services.

The "killer feature" of gRPC is said to be a support of simultaneous full duplex streaming. HTTP/2 protocol is used for that matter as a transport protocol. Protocol Buffers is used for effective serialization.

As it was said above, .NET Core 3.0 Framework supports gRPC based communication. Visual Studio 2019 offers a boilerplate code for gRPC service. However, this code provides only one-way communication with no message streaming. The project acts as departure point to the current work. In my code, I tried to pick up common code for server and client and combine it to convenient infrastructure that supports bidirectional message streaming with .NET Core 3.0 using gRPC.

When this article was initially published .NET Core 3.0  was in its pre-release version. Now the sample was tested with official release of .NET Core 3.0 and no changes were required.

Software Description

Infrastructure projects (DLLs) GrpcServerHelper and GrpcClientHelper are placed in Helpers folder. They may be referenced and used by concrete server and client respectively. The helper DLLs were developed assuming full duplex gRPC messages exchange between client and server. But there are no limitations on message structure and processing. Both these features are up to concrete server and client.

gRPC data and processing are defined in proto file communication.proto shared by concrete server and client. The file provides different types for client-to-server and server-to-client messages, appropriate enums and function CreateStreaming() to start messages exchange. Visual Studio 2019 generates C# classes out of proto files.

Server Infrastructure (GrpcServerHelper)

Class GeneralGrpcService<TRequest, TResponse> provides base infrastructure for bidirectional streaming for the server side. The class is parametrized with request and response message types TRequest, TResponse respectively. Its method CreateDuplexStreaming() handles main routine procedures like subscribers management, request message processing, response message generation and sending back to clients.

C#
public async Task CreateDuplexStreaming(
	IAsyncStreamReader<TRequest> requestStream,
	IServerStreamWriter<TResponse> responseStream,
	ServerCallContext context)
{
    var httpContext = context.GetHttpContext();
    Logger.LogInformation($"Connection id: {httpContext.Connection.Id}");

    if (!await requestStream.MoveNext())
        return;

    var clientId = _messageProcessor.GetClientId(requestStream.Current);
    Logger.LogInformation($"{clientId} connected");
    var subscriber = new SubscribersModel<TResponse>
    {
        Subscriber = responseStream,
        Id = $"{clientId}"
    };

    _serverGrpcSubscribers.AddSubscriber(subscriber);

    do
    {
        if (requestStream.Current == null)
            continue;

        var resultMessage = _messageProcessor.Process(requestStream.Current);
        if (resultMessage == null)
            continue;

        await _serverGrpcSubscribers.BroadcastMessageAsync(resultMessage);
    } while (await requestStream.MoveNext());

    _serverGrpcSubscribers.RemoveSubscriber(subscriber);
    Logger.LogInformation($"{clientId} disconnected");
}

Class ServerGrpcSubscribersBase<TResponse> manages subscribers (clients), while abstract class MessageProcessorBase<TRequest, TResponse> exposes abstract method abstract TResponse Process(TRequest message) for processing of request message and generation of a response one. For the sake of flexibility, it is assumed that concrete implementation of these methods takes care of possible exceptions, if any. Finally, class SubscribersModel<TResponse> supplies a subscriber model to send response message back to client(s).

Client Infrastructure (GrpcClientHelper)

The only abstract class GrpcClientBase<TRequest, TResponse> provides method Do() to ensure message streaming from the client side.

C#
public async Task Do(
	Channel channel, 
	Action onConnection = null, 
	Action onShuttingDown = null)
{
    using (var duplex = CreateDuplexClient(channel))
    {
        onConnection?.Invoke();

        var responseTask = Task.Run(async () =>
        {
            while (await duplex.ResponseStream.MoveNext(CancellationToken.None))
            Console.WriteLine($"{duplex.ResponseStream.Current}");
        });

        string payload;
        while (!string.IsNullOrEmpty(payload = MessagePayload))
        await duplex.RequestStream.WriteAsync(CreateMessage(payload));

        await duplex.RequestStream.CompleteAsync();
    }

    onShuttingDown?.Invoke();
    await channel.ShutdownAsync();
}

GrpcServer and GrpcClient

Concrete server GrpcServer was created by Visual Studio 2019 with ASP.MVC Core template as gRPC project. It implements class MessageProcessor : MessageProcessorBase<RequestMessage, ResponseMessage> and class ServerGrpcSubscribers : ServerGrpcSubscribersBase<ResponseMessage> extended appropriate stub classes of GrpcServerHelper. Class MessageProcessor provides required processing of client request message and sends response message if requied. Class ServerGrpcSubscribers tunes subscribers management (in our concrete case, it does not make any difference with respect to its ancestor and is used for illustration purposes only). Both classes ServerGrpcSubscribers and MessageProcessor should be added as singletons to services collection in method ConfigureServices(IServiceCollection services) of Startup class:

C#
services.AddSingleton<ServerGrpcSubscribers>();
services.AddSingleton<MessageProcessor>();

The service class DuplexService extending generated Messaging.MessagingBase type should be mapped to endpoints in method Startup.Configure() as follows:

C#
app.UseEndpoints(endpoints =>
{
    // ...
    endpoints.MapGrpcService<DuplexService>();
});

Server and client support exchange of encrypted messages (HTTPS). For that matter, server uses grpcServer.pfx file in its Kestrel configuration. and client uses certificate file certificate.crt. Both files were created with OpenSSL application installed according to guidelines provided e.g. here.

In order to use Proto Buffer serialization, server and client share the same proto file communication.proto. The file defines streaming messaging service:

C#
service Messaging
{
    rpc CreateStreaming (stream RequestMessage) returns (stream ResponseMessage);
}

request and response messages format and appropriate enumerators.

I failed to find a way in Visual Studio to include proto file into server and client projects to ensure generation of appropriate C# code. So I perform this operation manually in GrpcServer.csproj and GrpcClient.csproj project files. In those files, I added sections marked with comment <!-- PROTO: The following section was added manually -->.

Run Sample

To run the sample, please build the entire solution (this will also cause generation of classes out of proto file). Then run GrpcServer with...

dotnet GrpcServer.dll

...command and after its full start, proceed with one or more...

dotnet GrpcClient.dll

...commands.

In the client console, type some message concluded with Enter. The message will be received and processed (place for the processing code is reserved in method MessageProcessor.Process() as may see appropriate comment) by the server. If your message contains question mark ?, then the server broadcasts response message to all its current subscribers. Clients output the response in their consoles.

Conclusions

This work presents infrastructure for simultaneous bidirectional message streaming with .NET 3.0 using gRPC procedure. Usage of this infrastructure reduces code of concrete server and client to just few lines of code. Hope it will be of some help to respectful colleagues of mine.

History

  • 26th July, 2019: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Israel Israel


  • Nov 2010: Code Project Contests - Windows Azure Apps - Winner
  • Feb 2011: Code Project Contests - Windows Azure Apps - Grand Prize Winner



Comments and Discussions

 
GeneralMy vote of 5 Pin
erik@nl7-Oct-20 11:33
erik@nl7-Oct-20 11:33 
GeneralRe: My vote of 5 Pin
Igor Ladnik10-Dec-20 10:18
professionalIgor Ladnik10-Dec-20 10:18 
QuestionIs there a Postman-like tool available for gRPC ? Pin
Member 360622510-Aug-19 2:58
Member 360622510-Aug-19 2:58 
AnswerRe: Is there a Postman-like tool available for gRPC ? Pin
Member 360622510-Aug-19 10:20
Member 360622510-Aug-19 10:20 
GeneralRe: Is there a Postman-like tool available for gRPC ? Pin
Igor Ladnik10-Aug-19 20:37
professionalIgor Ladnik10-Aug-19 20:37 
QuestionCan we use JSON serialization with gRPC? Pin
Member 360622510-Aug-19 2:45
Member 360622510-Aug-19 2:45 
AnswerRe: Can we use JSON serialization with gRPC? Pin
Igor Ladnik10-Aug-19 20:26
professionalIgor Ladnik10-Aug-19 20:26 
GeneralRe: Can we use JSON serialization with gRPC? Pin
Member 360622510-Aug-19 23:24
Member 360622510-Aug-19 23:24 
GeneralRe: Can we use JSON serialization with gRPC? Pin
Igor Ladnik11-Aug-19 0:00
professionalIgor Ladnik11-Aug-19 0:00 
PraiseNice Description Pin
Shaun Stewart29-Jul-19 21:59
Shaun Stewart29-Jul-19 21:59 
GeneralRe: Nice Description Pin
Igor Ladnik30-Jul-19 16:27
professionalIgor Ladnik30-Jul-19 16:27 
QuestionSmall question Pin
Sacha Barber27-Jul-19 20:30
Sacha Barber27-Jul-19 20:30 
AnswerRe: Small question Pin
Igor Ladnik27-Jul-19 21:54
professionalIgor Ladnik27-Jul-19 21:54 
GeneralRe: Small question Pin
Sacha Barber28-Jul-19 21:59
Sacha Barber28-Jul-19 21:59 
QuestionGrpc Pin
Sacha Barber26-Jul-19 12:17
Sacha Barber26-Jul-19 12:17 
AnswerRe: Grpc Pin
Igor Ladnik27-Jul-19 21:58
professionalIgor Ladnik27-Jul-19 21:58 
PraiseGood Article Pin
Mahsa Hassankashi26-Jul-19 11:04
Mahsa Hassankashi26-Jul-19 11:04 
GeneralRe: Good Article Pin
Igor Ladnik26-Jul-19 17:17
professionalIgor Ladnik26-Jul-19 17:17 
GeneralRe: Good Article Pin
Mahsa Hassankashi27-Jul-19 11:14
Mahsa Hassankashi27-Jul-19 11:14 
GeneralRe: Good Article Pin
Sacha Barber27-Jul-19 20:28
Sacha Barber27-Jul-19 20:28 
GeneralRe: Good Article Pin
Mahsa Hassankashi27-Jul-19 23:24
Mahsa Hassankashi27-Jul-19 23:24 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.