Exercise 2: Server-Side Streaming - Live Temperature Updates
๐งช Exercise 2: Server-Side Streaming - Live Temperature Updates
In our first exercise, we explored a simple unary gRPC call. Now, we'll dive into one of gRPC's most powerful features: **server-side streaming**. This is perfect for scenarios where a single client request results in a continuous stream of data from the server, such as live data feeds or real-time updates.
In this exercise, you will modify your existing gRPC server to continuously send mock temperature updates for a specified city to your Blazor client.
✅ Step 1: Update `.proto` in gRPC Server
We need a new service definition for our weather streamer. In your `GrpcServerDemo` project, create a new file named `Protos/weather.proto`.
// Protos/weather.proto
syntax = "proto3";
option csharp_namespace = "GrpcServerDemo";
// Defines a service for streaming weather data
service WeatherStreamer {
// A single request returns a stream of WeatherReply messages
rpc StreamWeather(WeatherRequest) returns (stream WeatherReply);
}
// Message sent from the client to the server
message WeatherRequest {
string city = 1;
}
// Message sent from the server to the client
message WeatherReply {
string city = 1;
float temperature = 2;
string timestamp = 3;
}
The key change here is the `returns (stream WeatherReply)` part. This tells the gRPC framework that the server will be sending back multiple messages in response to a single `WeatherRequest`.
Next, update your `GrpcServerDemo.csproj` file to include the new `.proto` file and generate server-side code.
<ItemGroup>
<Protobuf Include="Protos\greeter.proto" GrpcServices="Server" />
<Protobuf Include="Protos\weather.proto" GrpcServices="Server" /> <!-- Add this line -->
</ItemGroup>
✅ Step 2: Implement Server Streaming Service
Now, create the implementation for the new `WeatherStreamer` service in a new file named `Services/WeatherStreamerService.cs`.
// Services/WeatherStreamerService.cs
using Grpc.Core;
using GrpcServerDemo;
public class WeatherStreamerService : WeatherStreamer.WeatherStreamerBase
{
public override async Task StreamWeather(WeatherRequest request, IServerStreamWriter<WeatherReply> responseStream, ServerCallContext context)
{
var rand = new Random();
// Loop to simulate sending a stream of updates
for (int i = 0; i < 10; i++)
{
// Check for client cancellation
if (context.CancellationToken.IsCancellationRequested)
{
break;
}
// Create a new weather update message
var reply = new WeatherReply
{
City = request.City,
Temperature = (float)(20 + rand.NextDouble() * 10), // Generate a random temperature
Timestamp = DateTime.Now.ToString("HH:mm:ss")
};
// Write the message to the response stream
await responseStream.WriteAsync(reply);
// Wait for 2 seconds before sending the next update
await Task.Delay(2000);
}
}
}
The `StreamWeather` method receives an `IServerStreamWriter
Next, update `Program.cs` in the `GrpcServerDemo` project to map your new service.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
var app = builder.Build();
app.MapGrpcService<GreeterService>();
app.MapGrpcService<WeatherStreamerService>(); <!-- Map the new service -->
app.MapGet("/", () => "Use a gRPC client to communicate.");
app.Run();
✅ Step 3: Blazor Client - Stream Weather Data
Now, in your `BlazorGrpcClient` project, you'll need to update the `.csproj` file to generate client-side code for the `weather.proto` file.
<ItemGroup>
<Protobuf Include="Protos\greeter.proto" GrpcServices="Client" />
<Protobuf Include="Protos\weather.proto" GrpcServices="Client" /> <!-- Add this line -->
</ItemGroup>
Next, you'll need to configure the gRPC client for the new `WeatherStreamer` service in `BlazorGrpcClient`'s `Program.cs`.
builder.Services.AddSingleton(services =>
{
var channel = GrpcChannel.ForAddress("https://localhost:7001");
// Add both clients here
return new Greeter.GreeterClient(channel);
});
builder.Services.AddSingleton(services =>
{
var channel = GrpcChannel.ForAddress("https://localhost:7001");
return new WeatherStreamer.WeatherStreamerClient(channel);
});
Finally, create a new Razor component `Pages/WeatherStream.razor` to display the incoming data.
@page "/weather"
@inject GrpcServerDemo.WeatherStreamer.WeatherStreamerClient WeatherClient
<h3>Weather Streaming</h3>
<p>Real-time temperature updates from the server.</p>
<div class="input-group mb-3">
<input type="text" class="form-control" @bind="City" placeholder="Enter city">
<button class="btn btn-success" type="button" @onclick="StartStreaming">Start Streaming</button>
</div>
<h4>Live Updates:</h4>
<ul class="list-unstyled">
@foreach (var update in Updates)
{
<li><code>@update</code></li>
}
</ul>
@code {
private string City { get; set; } = "New York";
private List<string> Updates { get; set; } = new();
private async Task StartStreaming()
{
Updates.Clear(); // Clear previous updates
try
{
// Make the server streaming call
var call = WeatherClient.StreamWeather(new WeatherRequest { City = City });
// Read the response stream asynchronously
await foreach (var response in call.ResponseStream.ReadAllAsync())
{
Updates.Add($"{response.Timestamp} | {response.City}: {response.Temperature:F2} °C");
StateHasChanged(); // Force the UI to update with each new message
}
}
catch (RpcException ex)
{
Updates.Add($"Error: {ex.Status.Detail}");
StateHasChanged();
}
}
}
Run both projects. When you navigate to `/weather` in your Blazor app and click "Start Streaming," you will see a new temperature update appear every two seconds, demonstrating the power of server-side streaming in gRPC.
Comments
Post a Comment