当前位置: 首页 > 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.xdnf.cn/news/1361323.html

相关文章:

  • 【图像处理基石】基于 Python 的图像行人删除技术:实现街景无干扰化处理
  • 6.1Element UI布局容器
  • leetcode 162 寻找峰值
  • Polkadot - JAM
  • 13种常见机器学习算法总结
  • 青少年软件编程(python六级)等级考试试卷-客观题(2023年3月)
  • 学习制作记录(选项UI以及存档系统)8.24
  • 基于RISC-V架构的国产MCU在eVTOL领域的应用研究与挑战分析
  • 【Ollama】本地OCR
  • 波兰密码破译机bomba:二战密码战的隐形功臣
  • Shell 循环实战:while 与 until 的趣味编程之旅
  • 3.4 磁盘存储器 (答案见原书 P194)
  • 【重学MySQL】八十八、8.0版本核心新特性全解析
  • Unity的Cursor.lockState
  • DeepSeek对采用nginx实现透传以解决OpenShift 4.x 私有数据中心和公有云混合部署一套集群的解答
  • 【SBP】Unity 打包构建管线原理解析于对比
  • 联想win11笔记本音频失效,显示差号(x)
  • 半年网络安全转型学习计划表(每天3小时)
  • 从成本中心到价值创造者:网络安全运维的实施框架与价值流转
  • VMware centos磁盘容量扩容教程
  • Windows 系统下 Android SDK 配置教程
  • 使用 Frida 运行时检测 Android 应用的真实权限状态 (App Ops)
  • 强逆光干扰漏检率↓78%!陌讯多模态融合算法在光伏巡检的实战优化
  • Java全栈开发面试实战:从基础到高并发场景的深度解析
  • Python性能优化实战(二):让循环跑得比博尔特还快
  • 27.编程思想
  • 【golang长途旅行第30站】channel管道------解决线程竞争的好手
  • Teams Bot机器人实时语音识别的多引擎的处理
  • TCP--执行Linux命令(虚拟xshell)
  • 数据建模怎么做?一文讲清数据建模全流程