Dapr Service-to-Service via gRPC
This post describes how to use Dapr’s gRPC proxying capability to handle service-to-service communication.
The benefit here is that Dapr manages all the inter-service communication, allowing you to maintain a ‘standard’ service that provides a gRPC endpoint.
The Dapr service-to-service invocation
building block will give you the following capabilities out of the box:
- Service discovery
- Load balancing
- Resiliency
- Tracing
Creating the gRPC service
We will start by creating a simple gRPC Greeting service which comes with the default grpc dotnet
template.
dotnet new grpc -o GrpcGreeter
In order to run the Greeting service standalone, run the following commands.
cd GrpcGreeter
dotnet run
Creating the gRPC client
The client we will create is a simple console application, which will call the gRPC service.
It will require the following NuGet packages to generate a client from the .proto
file.
- Grpc.Net.Client
- Google.Protobuf
- Grpc.Tools
In order to generate the gRPC client, you must either copy the Proto
folder to the client or place it one level higher so that the server and client can share the same file.
dotnet new console -o GrpcGreeterClient
Add the required NuGet packages in order to generate the gRPC client.
cd GrpcGreeterClient
dotnet add GrpcGreeterClient.csproj package Grpc.Net.Client
dotnet add GrpcGreeterClient.csproj package Google.Protobuf
dotnet add GrpcGreeterClient.csproj package Grpc.Tools
Change the following xml to the .csproj
file of the client to include the .proto
file.
<ItemGroup>
<Protobuf Include="..\Protos\greet.proto" GrpcServices="Client" />
</ItemGroup>
Add the following code to the Program.cs
of the client.
using Grpc.Net.Client;
using GrpcGreeter;
using static System.Console;
// The port number must match the port of the gRPC server.
using var channel = GrpcChannel.ForAddress("http://localhost:5062");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" });
WriteLine("Greeting: " + reply.Message);
WriteLine("Press any key to exit...");
ReadKey();
Run the client.
> dotnet run
Greeting: Hello GreeterClient
Press any key to exit...
Dapr
If we want to have Dapr running next to the gRPC service and handle all the communication, we need to run the Dapr sidecar with the gRPC service.
This can be done via the following dapr
command.
# Run Dapr with the gRPC service on port 5062 and with the app ID 'server'
dapr run -a server -p 5062 -P grpc -- dotnet run
The same applies for the client. The only difference is that the client will communicate to it’s own Dapr sidecar and that the sidecar of the client will find the sidecar of the service.
You’ll need to provide a different port and provide Metadata
while executing the request. Which can be done in the following way:
// Get port number if running with Dapr; otherwise, use the default, so that it will work standalone.
var port = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT") ?? "5062";
// Metadata
var metadata = new Metadata { { "dapr-app-id", "server" } };
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" }, metadata);
All client code:
using Grpc.Core;
using Grpc.Net.Client;
using GrpcGreeter;
using static System.Console;
// The port number must match the port of the gRPC server.
var port = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT") ?? "5062";
var address = $"http://localhost:{port}";
WriteLine($"Connecting to {address}...");
using var channel = GrpcChannel.ForAddress(address);
var client = new Greeter.GreeterClient(channel);
var metadata = new Metadata { { "dapr-app-id", "server" } };
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "GreeterClient" }, metadata);
WriteLine("Greeting: " + reply.Message);
To run the client together with Dapr, execute the following command.
> dapr run -a client -P grpc -- dotnet run
...
== APP == Connecting to http://localhost:57798...
== APP == Greeting: Hello GreeterClient
...
Conclusion
In conclusion, leveraging Dapr’s gRPC proxying capability opens up a world of possibilities for seamless service-to-service communication. By allowing Dapr to manage inter-service communication, you can focus on building standardized services that expose gRPC endpoints. The Dapr service-to-service invocation building block provides essential features such as service discovery, load balancing, resiliency, and tracing out of the box.
In this post, we walked through the process of creating a simple gRPC service and client using the .NET platform. We also explored integrating Dapr into the mix, highlighting the straightforward steps to run Dapr alongside the gRPC service and enabling communication between Dapr-enabled clients and services.
Feel free to experiment with the code samples provided on GitHub.