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.pswp.cn/web/94346.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94346.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 多版本 librealsense 与 realsense_ros 编译并兼容 L515 等设备

这篇博客用来记录如何在 Ubuntu 下编译多个版本的 librealsense 并使用 L515 和 D435i 等设备。核心在于 L515 这款设备已经停止维护&#xff0c;直接安装的方案不支持这个设备&#xff0c;只支持 D435i 等后期产品。如果想要同时使用新老的 realsense 产品&#xff0c;需要单独…

Java 泛型 T、E、K、V、?、S、U、V

目录 一、概述 二、为什么使用泛型 三、常见泛型类型参数的含义与用途 示例一&#xff1a;使用 T 定义泛型类 示例二&#xff1a;使用 E 表示集合元素 示例三&#xff1a;使用 K 和 V 表示键值对 示例四&#xff1a;使用 ? 通配符处理未知类型 四、通配符 ? 的扩展用…

1688拍立淘接口数据全面解析详细说明(item_search_img)

一、接口概述 1688拍立淘接口是阿里巴巴1688平台提供的基于图像识别的商品搜索服务&#xff0c;允许开发者通过上传商品图片来搜索平台上的同款或相似商品。该接口的主要功能是接收用户上传的图片&#xff08;或图片的相关信息&#xff09;&#xff0c;并通过1688平台的图像识…

【Docker项目实战】使用Docker部署轻量级LetsMarkdown文本编辑器

【Docker项目实战】使用Docker部署轻量级Markdown文本编辑器一、LetsMarkdown介绍1.1 LetsMarkdown简介1.2 主要特点二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍三、本地环境检查3.1 检查Docker服务状态3.2 检查Docker版本3.3检查docker compose 版本四、拉取容器镜像五…

Node.js自研ORM框架深度解析与实践

Node.js自研ORM框架深度解析与实践 前言 在现代Web开发中&#xff0c;对象关系映射&#xff08;ORM&#xff09;框架扮演着至关重要的角色。它们为开发者提供了一层抽象&#xff0c;使得数据库操作变得更加简单和直观。本文将深入解析一个基于Node.js和MySQL的自研ORM框架&…

汇总图片拖进ps中 photoshop同时打开几个文件夹

如果你有许多文件夹&#xff0c;你想选中一部分&#xff0c;然后把里面的图片全部拖进photoshop当中&#xff0c;但是文件夹又不能直接拖进去&#xff0c;那么你可以尝试使用一下这个工具&#xff0c;首先测试一下直接拖文件夹。选中你要处理的文件夹&#xff0c;直接拖进photo…

mysql 5.7 查询运行时间较长的sql

开发过程遇到sql 执行时间长&#xff0c;又取消不了的情况 可使用 kill query ID 杀死进程获取正在运行的sqlSELECT ID, -- 进程ID&#xff1a;MySQL服务器分配给每个连接的唯一标识符&#xff0c;用于区分不同的客户端连接USER, …

MongoDB 从入门到实践:全面掌握文档型 NoSQL 数据库核心操作

目录 一、MongoDB 基础准备 1. 官方资源获取 2. 安装步骤解析 二、MongoDB 核心指令详解 1. 数据库操作指令 2. 集合操作指令 3. 文档操作指令 查询文档 插入文档 修改文档 删除文档 三、进阶查询技巧 1. 运算符的灵活运用 比较运算符 逻辑运算符 范围与成员运算…

CVPR2025丨遥感领域,全模态与秒超高清遥感建模重大突破,性能提升创新点

关注gongzhonghao【CVPR顶会精选】刚入门遥感建模时&#xff0c;总好奇别人为什么总能提出新方法&#xff1f;慢慢摸索后才发现&#xff0c;创新点并不是硬憋出来的&#xff0c;而是要从数据特性、传感器差异、地物细节以及环境变化中发现机会。不同波段、不同分辨率、不同时相…

HTML5详篇

前端三剑客 前端三剑客是指HTML、CSS和JavaScript: HTML超文本标记语言(Hyper Text Markup Language):简单理解描述网页结构的;用于网页内容的语言。它通过使用不同的HTML标签来定义页面中的各种元素,例如标题、段落、图像、链接等【无羽毛的小鸟模型】 CSS层叠样式表(…

【Transient-Free 3DGS】delayed densification + coarse to fine增加GS的鲁棒性

25年最新连接去除场景瞬态对象工程与3DGS的pipeline&#xff0c;改进了spotlesssplats&#xff0c;已开源&#xff1a; [2506.02751] RobustSplat: Decoupling Densification and Dynamics for Transient-Free 3DGSAbstract page for arXiv paper 2506.02751: RobustSplat: De…

【MySQL】CRUD基础详解

CRUD基础前言&#xff1a;数据库的层级结构一、新增&#xff08;Create&#xff09;1. 单行数据 全列插入2. 单行数据的简写插入3. 指定列插入4. 多行数据插入二、查询&#xff08;Retrieve&#xff09;1. 全列查询2. 指定列查询3. 查询结果为表达式&#xff08;1&#xff09;…

互联网大厂Java求职面试实录:核心技术栈与业务场景解析

互联网大厂Java求职面试实录&#xff1a;核心技术栈与业务场景解析 面试场景设定 本文通过一个严肃的面试官和搞笑的水货程序员大面条之间的对话&#xff0c;模拟互联网大厂Java岗位的技术面试过程。面试涵盖Java SE、Spring生态、数据库、微服务、缓存、安全、消息队列、AI等多…

response对象的elapsed属性

在Python的requests库中&#xff0c;当我们发送一个请求后&#xff0c;会得到一个Response对象&#xff0c;这个对象有一个elapsed属性&#xff0c;它返回一个timedelta对象&#xff0c;表示从发送请求到收到响应所经过的时间。response.elapsed.total_seconds() 是 Python req…

【ansible】5.在受管主机部署文件和Jinja2模板

1.Ansible 中&#xff0c;如何用模块创建一个文件并设置权限644并设置SELinux类型&#xff0c;如何从受管主机中删除文件&#xff1f;使用ansible.builtin集合中的 file 模块&#xff0c;添加state&#xff1a;touch 创建文件&#xff0c;mode&#xff1a;‘0644’ 设置权限&am…

雪花算法数据库主键

雪花算法&#xff08;Snowflake&#xff09;作为一种分布式 ID 生成方案&#xff0c;在分布式系统中具有显著优势&#xff0c;能够解决多个关键问题。以下是它的核心好处及主要应用场景&#xff1a;雪花算法的核心好处全局唯一性&#xff1a;通过时间戳、机器 ID、数据中心 ID …

C/C++ 头文件命名约定

有的时候&#xff0c;在C的代码中&#xff0c;可以看到有如下的头文件引用的代码: #include <iostream> #include <unistd.h> #include <csignal>其中有一些是引用了.h文件&#xff0c;另外一些是引用了模块式的比如iostream和csignal&#xff0c;那么为什么…

异质结3.0时代的降本提效革命:捷造科技设备技术创新与产业拐点分析

光伏产业经历了从PERC到TOPCon和异质结&#xff08;HJT&#xff09;的技术迭代&#xff0c;而2025年将成为异质结技术规模化应用的关键转折点。捷造科技通过一系列突破性技术创新&#xff0c;将GW级异质结整线设备价格降至2亿元&#xff0c;较行业平均水平降低约40%&#xff0c…

【网络】http 协议中 Vary 标头的作用

在 HTTP 协议中&#xff0c;Vary 标头是一个关键的缓存控制机制&#xff0c;用于告知缓存服务器&#xff08;或代理&#xff09;&#xff1a;响应内容的生成依赖于请求中的哪些特定头部字段。其核心作用是确保缓存服务器能根据这些字段的差异&#xff0c;正确区分和返回不同版本…

CSS 进阶用法

一、选择器进阶复杂选择器组合详解后代选择器后代选择器使用空格分隔两个选择器&#xff0c;例如div p&#xff0c;表示选择div元素内所有的p元素。这种选择方式会匹配所有层级的后代元素&#xff0c;包括子元素、孙元素等任意深度的嵌套元素。应用示例&#xff1a;/* 选中arti…