gRPC server streaming example
By: Suraj Gharat
Tags: Grpc, Scala, Net Core 3

This post is an extension to my previous post, where we saw gRPC basics with a simple non-stream based example. In this post, we will see a server-side streaming example.

In this type of gRPC call, a client sends a message to a server, and gets a stream to read a sequence of messages back. The client read from the returned stream until there are no more messages.

Service specification

Below service.proto describes a service call that expects DemoNumber as an input and returns a stream of DemoNumber as its output. Please note the “stream” modifier before the return data type. The type DemoNumber is just a wrapper around primitive integer type, since gRPC does not support primitive types as its input or output types. Here the example requirement is to build a service that returns all prime numbers within a given input range.

service DemoService { rpc GetAllPrimeNumbers(DemoNumber) returns (stream DemoNumber) {} } message DemoNumber{ int32 value = 1; }

Server implementation

At Scala based server side, now we need to implement the above service by implementing a method that takes two parameters with Unit as the return value. The first input parameter is of type DemoNumber, which gives the service input provided by its invoking client, however, the second parameter is of type StreamObserver, which allows the server to send a sequence of output in the form of stream to its client. StreamObserver is a gRPC generated stub to access streams. We can use it to read and write the data from and to a stream.

override def getAllPrimeNumbersWithinGivenRange(request: DemoNumber, responseObserver: StreamObserver[DemoNumber]): Unit = { // Iterate through all numbers in the given range for(i <- 2 to request.value){ // if the number is prime then send to client if(isPrime(i)) { responseObserver.onNext(DemoNumber(i)) } } // send stream completion signal to client responseObserver.onCompleted() }

Client implementation

In .NET Core client app, we get to access the stream through an object on which we can call methods like MoveNext to check if there is any element to read from the stream, and a property to read the current element, just like how we retrieve elements out of an iterator.

LogInfo("[Server streaming gRPC call] about to start"); using (AsyncServerStreamingCall<DemoNumber> call = client.GetAllPrimeNumbers(new DemoNumber { Value = n })) { LogInfo("[Server streaming gRPC call] starting to read server stream"); while (await call.ResponseStream.MoveNext()) { LogInfo("[Server streaming gRPC call] received server stream element :" + call.ResponseStream.Current.Value); } LogInfo("[Server streaming gRPC call] done with reading server stream"); }

I tried to test the scenarios where the client is slower to process than its server (Aka backpressure) who is sending it data in the form of a stream, and I observed a fast server does not stop and rather completes its work, though its slow client still processes its output at slower pace. Nevertheless, after making the client to not process at all and instead pause for a user input, I observed my Scala server threw memory out of exception.