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 enum
s 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.
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.
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:
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:
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:
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