当前位置: 首页 > news >正文

C#_gRPC


6.3 gRPC:高性能跨语言服务间通信

gRPC是一个高性能、开源、通用的RPC(Remote Procedure Call)框架,由Google开发并基于其多年的内部使用经验(Stubby)。它现在是Cloud Native Computing Foundation(CNCF)的项目之一。

6.3.1 为什么选择gRPC?

与基于HTTP/JSON的REST API相比,gRPC具有显著优势,特别适合服务间的内部通信:

  1. 高性能

    • 协议缓冲区(Protocol Buffers):gRPC使用ProtoBuf作为其接口定义语言(IDL)和底层的消息交换格式。ProtoBuf是一种高效的二进制序列化格式,比JSON/XML更小、更快。
    • HTTP/2:gRPC构建在HTTP/2之上,而不是HTTP/1.1。这带来了多路复用(多个请求/响应在一个TCP连接上并行)、头部压缩、服务器推送等特性,显著减少了延迟并提高了吞吐量。
  2. 强契约和代码生成

    • 你需要在 .proto 文件中明确定义服务和消息的结构。这个文件是服务契约的唯一真相源。
    • 通过工具,可以自动为各种语言(C#, Go, Java, Python等)生成强类型的客户端和服务器端代码。这消除了手动序列化/反序列化的需要,并保证了类型安全。
  3. 跨语言支持:上述的代码生成使得用不同语言编写的服务可以轻松、无缝地相互通信。这是构建多语言技术栈的微服务系统的理想选择。

  4. 丰富的通信模式

    • 一元(Unary):简单的请求-响应。
    • 服务器流(Server streaming):客户端发送一个请求,服务器返回一个消息流。
    • 客户端流(Client streaming):客户端发送一个消息流,服务器返回一个单一的响应。
    • 双向流(Bidirectional streaming):双方都使用一个读写流发送一系列消息。

6.3.2 核心概念:.proto 文件与代码生成

一切始于一个 .proto 文件。它定义了服务的方法以及这些方法的输入和输出消息类型。

示例:一个简单的gRPC服务定义 (greeter.proto)

// 指定proto语法版本
syntax = "proto3";// 选项,指定C#的命名空间和输出路径
option csharp_namespace = "GrpcGreeterClient";// 包名,用于防止命名冲突
package greet;// 服务定义
service Greeter {// 定义一个一元RPC方法rpc SayHello (HelloRequest) returns (HelloReply);
}// 请求消息
message HelloRequest {string name = 1; // 字段有唯一的编号,用于二进制格式中的标识
}// 响应消息
message HelloReply {string message = 1;
}

代码生成
在项目文件中添加 Grpc.Tools 等包后,.NET构建过程会自动调用 protoc 编译器来生成C#代码。

<!-- 在 .csproj 文件中 -->
<ItemGroup><Protobuf Include="Protos\greeter.proto" GrpcServices="Server" /> <!-- GrpcServices="Server" 表示生成服务器端代码 --><!-- GrpcServices="Client" 则表示生成客户端代码 -->
</ItemGroup>

构建后,你会得到生成的类,如 GreeterBase(用于服务器继承)和 Greeter.GreeterClient(用于客户端调用)。

6.3.3 在ASP.NET Core中实现gRPC服务

ASP.NET Core对gRPC提供了一流的支持。gRPC服务与Web API Controller非常相似。

  1. 配置服务器
    首先,需要在 Program.cs 中注册gRPC服务。gRPC需要HTTP/2,并且通常使用明文(用于开发)或TLS终端。

    var builder = WebApplication.CreateBuilder(args);// 添加gRPC服务
    builder.Services.AddGrpc();
    // 可选:添加全局拦截器、配置JSON序列化等
    // builder.Services.AddGrpc(options => { options.EnableDetailedErrors = true; });var app = builder.Build();// 映射gRPC服务
    app.MapGrpcService<GreeterService>(); // 类似于MapControllersapp.Run();
    
  2. 实现服务
    创建一个继承自生成的基础类的服务类,并重写其方法。

    // 在自动生成的代码中,会有一个抽象的 GreeterBase 类
    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 });}
    }
    

    ServerCallContext 提供了对当前调用元数据的访问,类似于HTTP API中的 HttpContext

6.3.4 创建gRPC客户端

调用gRPC服务同样简单,得益于强大的代码生成。

  1. 配置客户端
    在客户端项目的 Program.cs 中,注册gRPC客户端。指定通道的地址(服务器的地址)。

    // 注册一个类型化客户端(推荐)
    builder.Services.AddGrpcClient<Greeter.GreeterClient>(options =>
    {options.Address = new Uri("https://localhost:7043");
    })
    .ConfigurePrimaryHttpMessageHandler(() => 
    {// 配置Handler来处理服务器证书验证(仅开发环境可能需要)var handler = new HttpClientHandler();handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; // ⚠️ 仅用于开发!return handler;
    });
    
  2. 在客户端代码中使用
    只需将生成的强类型客户端注入到你的类中即可使用。

    public class MyApiController : ControllerBase
    {private readonly Greeter.GreeterClient _grpcClient;public MyApiController(Greeter.GreeterClient grpcClient) => _grpcClient = grpcClient;[HttpGet("/greet/{name}")]public async Task<ActionResult> Greet(string name){// 调用gRPC服务,就像调用本地方法一样var reply = await _grpcClient.SayHelloAsync(new HelloRequest { Name = name });return Ok(reply.Message);}
    }
    

6.3.5 流式处理(Streaming)

gRPC的流式处理能力是其强大功能之一,非常适合传输大量数据或实时通信场景。

服务器流示例(Server Streaming)

// .proto 文件
service StockTicker {rpc GetStockUpdates (StockRequest) returns (stream StockUpdate);
}message StockRequest {string symbol = 1;
}message StockUpdate {string symbol = 1;double price = 2;google.protobuf.Timestamp time = 3;
}
// 服务器端实现
public override async Task GetStockUpdates(StockRequest request, IServerStreamWriter<StockUpdate> responseStream, ServerCallContext context)
{while (!context.CancellationToken.IsCancellationRequested){// 模拟从某个数据源获取实时股价var update = _stockService.GetLatestUpdate(request.Symbol);// 将更新写入流中,发送给客户端await responseStream.WriteAsync(update);await Task.Delay(1000, context.CancellationToken); // 每秒发送一次}
}// 客户端调用
using var call = _client.GetStockUpdates(new StockRequest { Symbol = "MSFT" });
await foreach (var update in call.ResponseStream.ReadAllAsync()) // 异步流遍历
{Console.WriteLine($"{update.Symbol}: {update.Price} at {update.Time}");
}

6.3.6 生态系统与进阶主题

  • 截止时间(Deadlines):gRPC客户端可以指定一个截止时间(例如,5秒后超时)。服务器可以检测到截止时间是否已过,从而中止昂贵的工作。这比传统的TCP超时更有效。
  • 拦截器(Interceptors):类似于ASP.NET Core中间件,可以用于实现跨切面关注点,如日志记录、认证、指标收集和重试逻辑。
  • 健康检查:gRPC有一个标准的健康检查协议,可以被负载均衡器或编排系统用来检查服务状态。
  • 与现有HTTP API共存:同一个ASP.NET Core应用程序可以同时托管gRPC服务和MVC/Web API控制器,让你可以根据需要逐步采用gRPC。

6.3.7 架构师视角:何时选择gRPC?

场景推荐选择理由
服务间通信(微服务)gRPC高性能、强契约、跨语言支持的优势得到最大发挥。
浏览器客户端通信REST/GraphQL浏览器对gRPC-Web的支持仍在发展中,而REST/GraphQL得到普遍支持。
移动客户端通信视情况而定gRPC对移动网络(高延迟、不稳定连接)的优化很好,但需要评估应用大小(ProtoBuf库会增加体积)。
需要实时流式数据gRPCgRPC的流式处理是第一公民,比WebSockets或SSE更高效、更易用。
公共APIREST/GraphQLREST的生态系统更成熟,工具链(如Swagger/OpenAPI)更完善,对消费者更友好。

gRPC的挑战

  • 可观察性:二进制协议对人类不友好,调试和日志记录需要额外的工具(如服务器端拦截器)。
  • 浏览器支持有限:虽然gRPC-Web解决了这个问题,但它需要一个代理来转换HTTP/1.1和HTTP/2之间的流量,增加了复杂性。
  • 防火墙策略:一些严格的网络环境可能对HTTP/2流量有特殊限制。

总结
gRPC是构建高性能、类型安全、跨语言的分布式系统的强大工具。它在微服务架构的内部通信中尤其出色。ASP.NET Core为其提供了卓越的支持,使得在.NET生态中采用gRPC变得非常简单。

决策不应是“gRPC好还是REST好”,而应是“在什么上下文下哪种工具更合适”。一个常见的成功模式是:在服务间(服务到服务)的通信中使用gRPC以追求性能,同时为外部客户端和浏览器提供RESTful API或GraphQL端点以追求通用性和易用性。这种混合方法可以让你两全其美。

http://www.dtcms.com/a/347936.html

相关文章:

  • RabbitMQ--消费端异常处理与 Spring Retry
  • 阿里云拉取dockers镜像
  • 在JavaScript中,比较两个数组是否有相同元素(交集)的常用方法
  • 今日科技热点 | AI加速创新,5G与量子计算引领未来
  • wpf之DockPanel
  • 3D打印机管理后台与RabbitMQ集成的业务场景
  • RabbitMQ面试精讲 Day 29:版本升级与平滑迁移
  • 【图像处理基石】基于 Python 的图像行人删除技术:实现街景无干扰化处理
  • 性能比拼: .NET (C#) vs. Fiber (Go)
  • Kaggle项目:一次 Uber 出行数据分析的完整思路
  • 高空作业安全监控难题突破!陌讯自适应识别算法实现安全带穿戴检测准确率↑93%
  • 深度学习——详细教学:神经元、神经网络、感知机、激活函数、损失函数、优化算法(梯度下降)
  • 大数据管理与应用系列丛书《数据挖掘》读书笔记之集成学习(1)
  • 基于PHP服装租赁管理系统/基于php的服装管理系统的设计与实现
  • 基于电磁频谱地图的辐射源定位算法复现
  • 算法训练营day60 图论⑩ Bellman_ford 队列优化算法、判断负权回路、单源有限最短路(修改后版本)
  • [两数之和](哈希表做法)
  • priority_queue和仿函数
  • Trip Footprint旅行足迹App技术架构全解析
  • 题解:P13754 【MX-X17-T3】Distraction_逆序对_前缀和_Ad-hoc_算法竞赛C++
  • GECP高程控制点数据集进行展示
  • 视觉革命:云渲染如何让创意不再受限于硬件
  • RustFS的边缘计算优化方案在5G MEC场景下的实测数据如何?
  • 迭代器模式与几个经典的C++实现
  • 双目密集匹配(stereo dense matching)
  • 从人工巡检到智能监测:工业设备管理的颠覆性变革
  • 97. 小明逛公园,Floyd 算法,127. 骑士的攻击,A * 算法
  • [Redis进阶]---------持久化
  • std::uncaught_exceptions 详解
  • 大模型——深度评测智能体平台Coze Studio