grpc-services
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesegRPC Services in .NET
.NET 中的 gRPC 服务
Proto File
Proto 文件
protobuf
syntax = "proto3";
option csharp_namespace = "MyApp.Grpc";
package catalog;
service CatalogService {
rpc GetProduct (GetProductRequest) returns (ProductResponse);
rpc ListProducts (ListProductsRequest) returns (ListProductsResponse);
rpc CreateProduct (CreateProductRequest) returns (ProductResponse);
rpc StreamProducts (StreamRequest) returns (stream ProductResponse);
}
message GetProductRequest { int32 id = 1; }
message ListProductsRequest {
int32 page = 1;
int32 page_size = 2;
string search = 3;
}
message ListProductsResponse {
repeated ProductResponse products = 1;
int32 total_count = 2;
}
message CreateProductRequest {
string name = 1;
string description = 2;
double price = 3;
}
message ProductResponse {
int32 id = 1;
string name = 2;
string description = 3;
double price = 4;
}
message StreamRequest { string filter = 1; }protobuf
syntax = "proto3";
option csharp_namespace = "MyApp.Grpc";
package catalog;
service CatalogService {
rpc GetProduct (GetProductRequest) returns (ProductResponse);
rpc ListProducts (ListProductsRequest) returns (ListProductsResponse);
rpc CreateProduct (CreateProductRequest) returns (ProductResponse);
rpc StreamProducts (StreamRequest) returns (stream ProductResponse);
}
message GetProductRequest { int32 id = 1; }
message ListProductsRequest {
int32 page = 1;
int32 page_size = 2;
string search = 3;
}
message ListProductsResponse {
repeated ProductResponse products = 1;
int32 total_count = 2;
}
message CreateProductRequest {
string name = 1;
string description = 2;
double price = 3;
}
message ProductResponse {
int32 id = 1;
string name = 2;
string description = 3;
double price = 4;
}
message StreamRequest { string filter = 1; }Service Implementation
服务实现
csharp
public sealed class CatalogGrpcService(ICatalogService catalog, ILogger<CatalogGrpcService> logger)
: CatalogService.CatalogServiceBase
{
public override async Task<ProductResponse> GetProduct(
GetProductRequest request, ServerCallContext context)
{
var product = await catalog.GetByIdAsync(request.Id, context.CancellationToken)
?? throw new RpcException(new Status(StatusCode.NotFound, $"Product {request.Id} not found"));
return MapToResponse(product);
}
public override async Task StreamProducts(
StreamRequest request, IServerStreamWriter<ProductResponse> responseStream, ServerCallContext context)
{
await foreach (var product in catalog.StreamAsync(request.Filter, context.CancellationToken))
{
await responseStream.WriteAsync(MapToResponse(product));
}
}
}csharp
public sealed class CatalogGrpcService(ICatalogService catalog, ILogger<CatalogGrpcService> logger)
: CatalogService.CatalogServiceBase
{
public override async Task<ProductResponse> GetProduct(
GetProductRequest request, ServerCallContext context)
{
var product = await catalog.GetByIdAsync(request.Id, context.CancellationToken)
?? throw new RpcException(new Status(StatusCode.NotFound, $"Product {request.Id} not found"));
return MapToResponse(product);
}
public override async Task StreamProducts(
StreamRequest request, IServerStreamWriter<ProductResponse> responseStream, ServerCallContext context)
{
await foreach (var product in catalog.StreamAsync(request.Filter, context.CancellationToken))
{
await responseStream.WriteAsync(MapToResponse(product));
}
}
}Server Setup
服务器配置
csharp
builder.Services.AddGrpc();
app.MapGrpcService<CatalogGrpcService>();csharp
builder.Services.AddGrpc();
app.MapGrpcService<CatalogGrpcService>();Client (with Aspire)
客户端(搭配Aspire)
csharp
builder.Services.AddGrpcClient<CatalogService.CatalogServiceClient>(o =>
{
o.Address = new("https+http://catalog-api");
})
.AddStandardResilienceHandler();csharp
builder.Services.AddGrpcClient<CatalogService.CatalogServiceClient>(o =>
{
o.Address = new("https+http://catalog-api");
})
.AddStandardResilienceHandler();gRPC vs REST Decision Matrix (from official docs)
gRPC vs REST 决策矩阵(来自官方文档)
| Aspect | gRPC | REST/HTTP |
|---|---|---|
| Serialization | Protocol Buffers (binary) | JSON (text) |
| Protocol | HTTP/2 | HTTP/1.1 or HTTP/2 |
| Payload size | Small, compressed | Larger, human-readable |
| Latency | Low (~10x faster) | Higher |
| Browser support | Limited (gRPC-Web) | Full |
| Code generation | Automatic (contract-first) | Manual |
| Streaming | Native, bidirectional | Limited |
| Debugging | Binary (harder) | Easy (HTTP tools) |
| Use case | Microservices, real-time | Public APIs, browsers |
| 维度 | gRPC | REST/HTTP |
|---|---|---|
| 序列化方式 | Protocol Buffers(二进制) | JSON(文本) |
| 协议 | HTTP/2 | HTTP/1.1 或 HTTP/2 |
| 负载大小 | 小体积、已压缩 | 体积较大、人类可读 |
| 延迟 | 低(约快10倍) | 较高 |
| 浏览器支持 | 有限(需使用gRPC-Web) | 完全支持 |
| 代码生成 | 自动生成(契约优先) | 手动编写 |
| 流处理 | 原生支持、双向 | 有限支持 |
| 调试难度 | 二进制格式(较难) | 简单(可使用HTTP工具) |
| 适用场景 | 微服务、实时通信 | 公开API、浏览器端 |
All 4 RPC Patterns (from official docs)
四种RPC模式(来自官方文档)
Client Streaming
客户端流模式
csharp
// Stream of requests → single response
public override async Task<HelloReply> LotsOfGreetings(
IAsyncStreamReader<HelloRequest> requestStream, ServerCallContext context)
{
var count = 0;
await foreach (var request in requestStream.ReadAllAsync())
{
count++;
}
return new HelloReply { Message = $"Processed {count} greetings" };
}csharp
// 请求流 → 单个响应
public override async Task<HelloReply> LotsOfGreetings(
IAsyncStreamReader<HelloRequest> requestStream, ServerCallContext context)
{
var count = 0;
await foreach (var request in requestStream.ReadAllAsync())
{
count++;
}
return new HelloReply { Message = $"Processed {count} greetings" };
}Bidirectional Streaming
双向流模式
csharp
// Stream requests ↔ stream responses simultaneously
public override async Task BidiHello(
IAsyncStreamReader<HelloRequest> requestStream,
IServerStreamWriter<HelloReply> responseStream,
ServerCallContext context)
{
await foreach (var request in requestStream.ReadAllAsync())
{
await responseStream.WriteAsync(new HelloReply
{
Message = $"Echo: {request.Name}"
});
}
}csharp
// 请求流 ↔ 响应流同时进行
public override async Task BidiHello(
IAsyncStreamReader<HelloRequest> requestStream,
IServerStreamWriter<HelloReply> responseStream,
ServerCallContext context)
{
await foreach (var request in requestStream.ReadAllAsync())
{
await responseStream.WriteAsync(new HelloReply
{
Message = $"Echo: {request.Name}"
});
}
}Project Setup (from official docs)
项目配置(来自官方文档)
xml
<!-- .csproj -->
<ItemGroup>
<Protobuf Include="Protos\greet.proto" />
</ItemGroup>bash
dotnet add package Grpc.AspNetCore
dotnet add package Grpc.Toolsxml
<!-- .csproj -->
<ItemGroup>
<Protobuf Include="Protos\greet.proto" />
</ItemGroup>bash
dotnet add package Grpc.AspNetCore
dotnet add package Grpc.ToolsBest Practices
最佳实践
- Use gRPC for internal service-to-service communication
- Use REST/HTTP for external APIs (browser compatibility)
- Use server streaming for large result sets
- Use for request timeouts
Deadline - Use interceptors for cross-cutting concerns (logging, auth)
- Add files to
.protoitem group in .csproj<Protobuf> - Binary format = harder debugging; use gRPC reflection for tooling
- 内部服务间通信使用gRPC
- 外部API使用REST/HTTP(考虑浏览器兼容性)
- 处理大型结果集时使用服务器流模式
- 为请求设置超时时间
Deadline - 使用拦截器处理横切关注点(日志、认证)
- 将文件添加到.csproj的
.proto项组中<Protobuf> - 二进制格式调试难度较高;使用gRPC反射工具辅助调试