Унарные вызовы и потоковая передача от сервера
Реализуйте унарные вызовы и потоковую передачу со стороны сервера, чтобы передавать данные от сервера клиенту в реальном времени.
«Унарные вызовы и потоковая передача от сервера» — бесплатный урок C# Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.
Типы RPC в gRPC
gRPC поддерживает четыре шаблона RPC: унарный (запрос и ответ), потоковая передача от сервера (клиент отправляет один запрос, сервер — множество ответов), потоковая передача от клиента и двунаправленная потоковая передача. В этом уроке рассматриваются первые два.
Унарный RPC: основной шаблон
Унарный вызов — самый простой шаблон: один запрос и один ответ. Он похож на обычный вызов функции, но передаётся через HTTP/2 с кодированием Protobuf.
// .proto definition
service OrderService {
rpc GetOrder (GetOrderRequest) returns (OrderResponse);
}
message GetOrderRequest { int32 id = 1; }
message OrderResponse { int32 id = 1; string status = 2; double total = 3; }Реализация унарного RPC
Переопределите созданный базовый метод. ServerCallContext предоставляет метаданные, токен отмены, крайний срок и сведения об узле-партнёре.
public class OrderService : OrderService.OrderServiceBase
{
private readonly IOrderRepository _repo;
public OrderService(IOrderRepository repo) => _repo = repo;
public override async Task<OrderResponse> GetOrder(
GetOrderRequest request,
ServerCallContext context)
{
var order = await _repo.GetByIdAsync(request.Id, context.CancellationToken);
if (order is null)
throw new RpcException(new Status(StatusCode.NotFound, "Order not found"));
return new OrderResponse
{
Id = order.Id,
Status = order.Status.ToString(),
Total = (double)order.Total
};
}
}Вызов унарного RPC с клиента
Сгенерированный клиент предоставляет методы, выглядящие как синхронные, и действительно асинхронные методы. В рабочей среде всегда используйте вариант Async.
var client = new OrderService.OrderServiceClient(channel);
// Async call with cancellation
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
var reply = await client.GetOrderAsync(
new GetOrderRequest { Id = 42 },
cancellationToken: cts.Token);
Console.WriteLine($"Order {reply.Id}: {reply.Status} - ${reply.Total}");Потоковая передача от сервера: определение Proto
Потоковая передача от сервера добавляет ключевое слово stream перед типом ответа. Сервер отправляет несколько сообщений через одно и то же соединение, а затем закрывает поток.
service StockService {
// Client sends one symbol, server streams price updates
rpc WatchStock (WatchRequest) returns (stream StockUpdate);
}
message WatchRequest { string symbol = 1; }
message StockUpdate { string symbol = 1; double price = 2; int64 timestamp = 3; }Реализация потоковой передачи от сервера
Используйте IServerStreamWriter<T> для отправки сообщений. Метод возвращает Task — записывайте сообщения до завершения, затем выполните возврат.
public override async Task WatchStock(
WatchRequest request,
IServerStreamWriter<StockUpdate> responseStream,
ServerCallContext context)
{
while (!context.CancellationToken.IsCancellationRequested)
{
var price = await _market.GetPriceAsync(request.Symbol);
await responseStream.WriteAsync(new StockUpdate
{
Symbol = request.Symbol,
Price = price,
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
});
await Task.Delay(1000, context.CancellationToken);
}
}Получение потока от сервера на клиенте
Используйте await foreach с ResponseStream.ReadAllAsync(), чтобы обрабатывать каждое сообщение по мере его поступления.
using var call = client.WatchStock(
new WatchRequest { Symbol = "MSFT" });
await foreach (var update in call.ResponseStream.ReadAllAsync())
{
Console.WriteLine($"{update.Symbol}: ${update.Price:F2}");
}
// Continues until server closes the stream or client cancelsОтмена потока от сервера
Передайте CancellationToken при открытии вызова, чтобы остановить поток на стороне клиента. Сервер получает отмену через собственный токен.
var cts = new CancellationTokenSource();
// Cancel after 30 seconds
cts.CancelAfter(TimeSpan.FromSeconds(30));
using var call = client.WatchStock(
new WatchRequest { Symbol = "AAPL" },
cancellationToken: cts.Token);
try
{
await foreach (var update in
call.ResponseStream.ReadAllAsync(cts.Token))
{
Console.WriteLine($"Price: {update.Price}");
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Stream cancelled.");
}Метаданные: заголовки запроса и завершающие заголовки
gRPC поддерживает метаданные (пары «ключ — значение»), отправляемые в заголовках перед ответом и завершающих заголовках после ответа. Используйте их для токенов аутентификации, идентификаторов трассировки или курсоров постраничной навигации.
// Server: send initial metadata
await context.WriteResponseHeadersAsync(new Metadata
{
{ "x-correlation-id", Guid.NewGuid().ToString() }
});
// Server: set trailing metadata
context.ResponseTrailers.Add("x-total-count", "1500");
// Client: read headers
var headers = await call.ResponseHeadersAsync;
var trailerId = headers.GetValue("x-correlation-id");gRPC для браузерных клиентов
Браузеры не могут использовать обычный gRPC (завершающие заголовки HTTP/2 не поддерживаются). Используйте Grpc.AspNetCore.Web и JavaScript-клиент grpc-web или Grpc.Net.Client.Web в Blazor.
// dotnet add package Grpc.AspNetCore.Web
app.UseGrpcWeb();
app.MapGrpcService<GreeterService>().EnableGrpcWeb();
// Blazor client:
var handler = new GrpcWebHandler(GrpcWebMode.GrpcWeb,
new HttpClientHandler());
using var channel = GrpcChannel.ForAddress(
"https://localhost:7042",
new GrpcChannelOptions { HttpHandler = handler });Практический пример: служба потоковой передачи журналов
Служба агрегации журналов в реальном времени передаёт записи журналов приложения подключённым клиентам мониторинга, используя потоковую передачу от сервера.
public override async Task StreamLogs(
LogStreamRequest request,
IServerStreamWriter<LogEntry> responseStream,
ServerCallContext context)
{
await foreach (var log in _logChannel.Reader.ReadAllAsync(
context.CancellationToken))
{
if (log.Level >= request.MinLevel)
{
await responseStream.WriteAsync(new LogEntry
{
Level = log.Level.ToString(),
Message = log.Message,
Timestamp = Timestamp.FromDateTime(log.Timestamp)
});
}
}
}Быстрая проверка
Какое ключевое слово в файле .proto указывает на RPC с потоковой передачей от сервера?
Итоги: унарные RPC и потоковая передача от сервера
Основные выводы:
- Унарный RPC: один запрос, один ответ — наиболее распространённый шаблон
- Потоковая передача от сервера: один запрос и несколько сообщений ответа, поступающих со временем
- Реализуйте потоковую передачу от сервера, записывая данные в
IServerStreamWriter<T> - Получайте потоки от сервера с помощью
await foreachиReadAllAsync() - Используйте CancellationToken, чтобы корректно останавливать потоки с любой стороны
- gRPC-Web позволяет браузерным клиентам вызывать службы gRPC
Часто задаваемые вопросы
Урок «Унарные вызовы и потоковая передача от сервера» бесплатный?
Да — полный текст урока «Унарные вызовы и потоковая передача от сервера» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.
Чему я научусь в уроке «Унарные вызовы и потоковая передача от сервера»?
Реализуйте унарные вызовы и потоковую передачу со стороны сервера, чтобы передавать данные от сервера клиенту в реальном времени. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать C# Academy?
Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Унарные вызовы и потоковая передача от сервера»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке C# Academy?
Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы gRPC и Protobuf
- Унарные вызовы и потоковая передача от сервера
- Потоковая передача от клиента и двунаправленная передача
- Крайние сроки, отмена и перехватчики