gRPC 与 Protobuf 基础
在 .proto 文件中定义服务契约,使用 Grpc.Tools 生成 C# 代码,并了解 gRPC 传输机制。
gRPC 与 Protobuf 基础 是 CoddyKit 上的免费 C# Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。
什么是 gRPC
gRPC 是由 Google 开发的高性能、与编程语言无关的 RPC 框架。它使用 HTTP/2 进行传输,并使用 Protocol Buffers(Protobuf) 作为序列化格式——与通过 HTTP/1.1 传输 JSON 相比,速度更快且更加紧凑。
Protocol Buffers:IDL
您可以在 .proto 文件中定义服务契约。Protobuf IDL(接口定义语言)具有强类型且与语言无关——同一个文件可以生成 C#、Go、Python 等语言的客户端和服务器端代码。
// greet.proto
syntax = "proto3";
option csharp_namespace = "GrpcService";
package greet;
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}在 .NET 中设置 gRPC 服务器
使用 dotnet new grpc 创建 gRPC 项目。将 .proto 文件添加到项目中,Grpc.Tools 会在构建时自动生成 C# 类。
// .csproj snippet
<ItemGroup>
<Protobuf Include="Protos\greet.proto" GrpcServices="Server" />
</ItemGroup>
// Program.cs
builder.Services.AddGrpc();
var app = builder.Build();
app.MapGrpcService<GreeterService>();
app.Run();实现 gRPC 服务
继承生成的基类并重写 RPC 方法。框架会自动处理序列化、HTTP/2 帧处理和路由。
using Grpc.Core;
using GrpcService;
public class GreeterService : Greeter.GreeterBase
{
private readonly ILogger<GreeterService> _logger;
public GreeterService(ILogger<GreeterService> logger) => _logger = logger;
public override Task<HelloReply> SayHello(
HelloRequest request,
ServerCallContext context)
{
_logger.LogInformation("Saying hello to {Name}", request.Name);
return Task.FromResult(new HelloReply
{
Message = $"Hello, {request.Name}!"
});
}
}创建 gRPC 客户端
Protobuf 编译器还会生成强类型的客户端类。使用 GrpcChannel 连接并调用服务,就像调用本地方法一样。
// Client project: add Grpc.Net.Client package
using var channel = GrpcChannel.ForAddress("https://localhost:7042");
var client = new Greeter.GreeterClient(channel);
var reply = await client.SayHelloAsync(
new HelloRequest { Name = "Alice" });
Console.WriteLine(reply.Message); // Hello, Alice!Protobuf 类型与字段编号
Protobuf 消息中的每个字段都有唯一的字段编号(1–536870911)。字段编号会编码到二进制格式中——部署后绝不能更改,否则会破坏向后兼容性。
message Product {
int32 id = 1;
string name = 2;
double price = 3;
int32 stock = 4;
bool is_active = 5;
repeated string tags = 6; // array
}
// Supported scalar types:
// int32, int64, uint32, uint64, float, double
// bool, string, bytes, enum枚举与嵌套消息
Protobuf 支持枚举和嵌套消息类型。您可以使用它们在服务契约中建模复杂的领域对象。
enum OrderStatus {
ORDER_STATUS_UNSPECIFIED = 0;
ORDER_STATUS_PENDING = 1;
ORDER_STATUS_SHIPPED = 2;
ORDER_STATUS_CANCELLED = 3;
}
message Order {
int32 id = 1;
OrderStatus status = 2;
repeated OrderLine lines = 3;
}
message OrderLine {
int32 product_id = 1;
int32 quantity = 2;
double price = 3;
}HTTP/2 与 gRPC 传输
gRPC 使用支持多路复用的 HTTP/2——在单个 TCP 连接上并发执行多个 RPC 调用——与 HTTP/1.1 相比,可以降低延迟和连接开销。
// gRPC requires HTTP/2
// For local dev with HTTP (not HTTPS), enable HTTP/2 cleartext:
builder.WebHost.ConfigureKestrel(opt =>
opt.ListenLocalhost(5000, o => o.Protocols = HttpProtocols.Http2));
// Client for cleartext (dev only)
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
using var channel = GrpcChannel.ForAddress("http://localhost:5000");使用 StatusCode 处理错误
gRPC 使用自己的状态码(不同于 HTTP)。抛出带有 Status 的 RpcException,即可向客户端发送类型化的错误响应。
public override Task<ProductReply> GetProduct(
ProductRequest request,
ServerCallContext context)
{
var product = _repo.FindById(request.Id);
if (product is null)
throw new RpcException(
new Status(StatusCode.NotFound, $"Product {request.Id} not found"));
return Task.FromResult(MapToReply(product));
}用于开发的 gRPC 反射
启用 gRPC 反射后,grpcurl 和 Postman 等工具无需直接访问 .proto 文件即可发现您的服务。
// dotnet add package Grpc.AspNetCore.Server.Reflection
builder.Services.AddGrpcReflection();
if (app.Environment.IsDevelopment())
app.MapGrpcReflectionService();
// Now use grpcurl:
// grpcurl -plaintext localhost:5000 list
// grpcurl -plaintext localhost:5000 greet.Greeter/SayHello真实案例:微服务产品查询
一个产品服务公开了供订单服务内部使用的 gRPC 端点,这是经典的服务间通信模式。
// product.proto
service ProductService {
rpc GetProduct (GetProductRequest) returns (ProductResponse);
rpc ListProducts (ListProductsRequest) returns (ListProductsResponse);
}
// Order service calls it:
public class OrderService
{
private readonly ProductService.ProductServiceClient _products;
public OrderService(ProductService.ProductServiceClient p) => _products = p;
public async Task<Order> PlaceOrderAsync(int productId, int qty)
{
var product = await _products.GetProductAsync(
new GetProductRequest { Id = productId });
return new Order { ProductName = product.Name, Quantity = qty };
}
}快速检查
为什么部署后绝不能更改现有的 Protobuf 字段编号?
回顾:gRPC 与 Protobuf 基础
要点:
- gRPC 使用 HTTP/2 + Protobuf,实现快速且强类型的服务间通信
- 在
.proto文件中定义服务契约,并在构建时生成代码 - 字段编号用于线路编码,绝不能更改或重复使用
- 继承生成的基类并重写 RPC 方法
- 使用带有 StatusCode 的 RpcException 返回类型化的错误响应
- 在开发环境中启用 gRPC 反射,为工具提供支持
常见问题解答
「gRPC 与 Protobuf 基础」课时是免费的吗?
是的 — 「gRPC 与 Protobuf 基础」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「gRPC 与 Protobuf 基础」这节课中我会学到什么?
在 .proto 文件中定义服务契约,使用 Grpc.Tools 生成 C# 代码,并了解 gRPC 传输机制。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「gRPC 与 Protobuf 基础」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- gRPC 与 Protobuf 基础
- 一元与服务器流式 RPC
- 客户端与双向流式传输
- 截止时间、取消与拦截器