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

.net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端

.NET Core ❤ gRPC

千呼万唤的 .NET Core 3.0 终于在 9 月份正式发布,在它的众多新特性中,除了性能得到了大大提高,比较受关注的应该是 http://ASP.NET Core 3.0 对 gRPC 的集成了。 它的源码托管在 grpc-dotnet 这个 Github 库中,由微软 .NET 团队与谷歌 gRPC 团队共同维护.

.NET Core 对 gRPC 的支持在 grpc 官方仓库早已有实现(grpc/csharp),但服务端没有很好地与 http://ASP.NET Core 集成,使用起来还需要自己进行一些集成扩展。 而 http://ASP.NET Core 3.0 新增了 gRPC 服务的托管功能,能让 gRPC 与 http://ASP.NET Core 框架本身的特性很好地结合,如日志、依赖注入、身份认证和授权,并由 Kestrel 服务器提供 HTTP/2 链接,性能上得到充分保障。

推荐把项目中已有的 RPC 框架或者内部服务间 REST 调用都迁移到 gRPC 上,因为它已经是云原生应用的标准 RPC 框架,在整个 CNCF 主导下的云原生应用开发生态里 gRpc 有着举足轻重的地位。

对于 gRPC 的使用方式,前段时间已经有其他大神写的几篇文章了,这里就不再赘述了。 本文主要介绍的是区别于标准使用规范的,但对.NET 应用更加友好的使用方式,最后会提供源码来展示。

作为对比,还是要列一下标准的使用步骤:

  1. 定义 proto 文件,包含服务、方法、消息对象的定义
  2. 引入 Grpc.Tools Nuget 包并添加指定 proto 路径和生成模式
  3. 生成项目,得到服务端的抽象类或客户端的调用客户端组件
  4. 实现服务端抽象类,并在 http://ASP.NET Core 注册这个服务的路由端点
  5. DI 注册 gRPC 服务。
  6. 客户端用 Grpc.Net.ClientFactory Nuget 包进行统一配置和依赖注入

.NET Core 对 gRPC 的大力支持使开发者开发效率大大提高,入门难度也减少了许多,完全可以成为跟 WebApi 等一样的 .NET Core 技术栈的标配。

proto 在单一语言系统架构中的局限性

使用 proto 文件的好处是多语言支持,同一份 proto 可以生成各种语言的服务和客户端,可以让用不同语言开发的微服务直接互相远程调用。但 proto 文件作为不同服务间的契约,不可以经常修改,否则就会对使用了它的服务造成不同程度的影响,因此对 proto 文件的版本控制需要得到重视。

另外,我们的应用程序还不应该与 gRPC 耦合,否则就会导致系统架构被这些实现细节所绑架。直接依赖 proto 文件和由它生成的代码,就是对 gRPC 的强耦合。

例如,当应用程序在演进的过程中,复杂度还未达到完全部署隔离的必要时,为了避免因“完全边界”引入的部署运维复杂性,又能预留隔离的可能性,需要有一层接口层作为“不完全边界”。

又比如,目前在 windows 系统的 iis 上还不支持 grpc-dotnet,当有 windows 上的应用程序需要使用 RPC,就需要换成 REST 的实现了。

因此,为了不让应用程序对 gRPC 过于依赖,还应该使用一层抽象(接口)层与其解耦,用接口来隔离对 RPC 实现的依赖,这样在需要使用不同的实现时,可以通过注册不同的实现来方便地切换。

在这些场景下,本文要介绍的 Code-First gRPC 使用方法就发挥作用了。

Code-First gRPC

说了这么久,我好像还没正式介绍 Code-First gRPC,到底他有多适合在单一语言系统架构中实现 gRPC 呢?下面要介绍的就是基于大名鼎鼎的 protobuf-net 实现的 gRPC 框架,protobuf-net.Grpc

protobuf-net 是在过去十几年前到现在一直在 .NET 中有名的 Protobuf 库,想用 Protobuf 序列化时就会用到这个库。他的特性就是可以把 C# 代码编写的类能以 Protobuf 的协议进行序列化和反序列化,而不是 proto 文件再生成这些类。而 protobuf-net.Grpc 则是一脉相承,可以把 C# 写的接口,在服务端方便地把接口的实现类注册成 http://ASP.NET Core 的 gRPC 服务,在客户端把接口动态代理实现为调用客户端,调用前面的这个服务端。

用法很简单,只要声明一个接口为您的服务契约:

[ServiceContract]
public interface IMyAmazingService {
    ValueTask<SearchResponse> SearchAsync(SearchRequest request);
    // ...
}

然后实现该接口的服务端:

public class MyServer : IMyAmazingService {
    // ...
}

或者向系统获取客户端:

var client = http.CreateGrpcService<IMyAmazingService>();
var results = await client.SearchAsync(request);

这相当于以下 .proto 中的服务:

service MyAmazingService {
    rpc Search (SearchRequest) returns (SearchResponse) {}
	// ...
}

protobuf-net.Grpc 同样通过普通类型定义支持 gRPC 的四种模式,把 C# 8.0 中最新的 IAsyncEnumerable 类型识别成 proto 中的 stream,单向流、双向流都可以实现!而且用 IAsyncEnumerable 实现可比 proto 生成的类方便很多。

例如 proto 双向流定义:

rpc chat(stream ChatRequest) returns ( stream ChatResponse);

生成出来的方法是:

Task BathTheCat(IAsyncStreamReader<ChatRequest> requestStream, IServerStreamWriter<ChatResponse> responseStream)

protobuf-net.Grpc 只要定义一个方法:

IAsyncEnumerable<ChatResponse> SubscribeAsync(IAsyncEnumerable<ChatRequest> requestStream);

总结

由此可见,protobuf-net.Grpc 无需在契约层引入第三方库,充分运用了 C# 类型系统,把方法、类型映射到兼容了 gRPC 的服务定义上。

上文所说的 proto 局限也迎刃而解了,函数调用、gRPC、REST 都能方便切换。(REST 实现可以参考我的开源框架 shriek-fx 中的 Shriek.ServiceProxy.Http )组件。

下一篇,我将主要介绍利用 protobuf-net.Grpc 的 gRPC 双向流模式与 Blazor 实现一个简单的在线即时聊天室。

a0db4053bc4f3f00f1cd0b4090b1dbf8.gif

相关链接:

  • protobuf-net.Grpc:https://github.com/protobuf-net/protobuf-net.Grpc
  • shriek-fx:https://github.com/Shriek-Projects/shriek-fx
  • GrpcChat: https://github.com/ElderJames/GrpcChat

相关文章:

  • C# 联合查询_直击数据库面试题:数据库查询语句
  • using namespace std_STD币最高价格-每日分享短线币种策略观点
  • 机器人码垛搬运编程程序_焊接机器人、码垛搬运机器人的差异和区别是什么?...
  • conteos7.2 安装php_广播将军免安装中文版下载
  • ajax nginx 转发 sessionid_Web服务器06下Nginx配置虚拟主机及访问控制
  • 智能机器人及其应用ppt课件_地铁车辆智能化检修和智能巡检机器人应用浅谈
  • python出现invalid syntax-python学习笔记之调用eval函数出现invalid syntax错误问题
  • 复指数与高斯函数乘积的傅里叶变换_希尔伯特变换:将实数信号变换成解析信号?节省带宽,提升效率...
  • 最大正整数和最小负整数的代码_【国际数学竞赛】一个正整数等于最小的四个正因子平方和...
  • 如何实现数据监控_如何在数据中心中安装环境监控系统
  • ps制作20种特效文字_如何用PS制作人体特效?
  • 华为手机相册怎么镜像翻转_怎么制作照片视频?利用手机相册快速制作卡点视频...
  • 项目管理软件对比分析_做项目管理的痛,你不懂!
  • win10打开程序响应很慢_Win10按alt+tab切换很慢?联想电脑Alt+Tab切换窗口很卡解决方法...
  • python 进入E盘_零基础学Python—基础班04期
  • 2017年终总结、随想
  • 4月23日世界读书日 网络营销论坛推荐《正在爆发的营销革命》
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • Docker容器管理
  • HashMap ConcurrentHashMap
  • JavaScript/HTML5图表开发工具JavaScript Charts v3.19.6发布【附下载】
  • Javascript编码规范
  • PHP 的 SAPI 是个什么东西
  • Python socket服务器端、客户端传送信息
  • Python中eval与exec的使用及区别
  • Yii源码解读-服务定位器(Service Locator)
  • 彻底搞懂浏览器Event-loop
  • 第13期 DApp 榜单 :来,吃我这波安利
  • 缓存与缓冲
  • 看图轻松理解数据结构与算法系列(基于数组的栈)
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 使用Tinker来调试Laravel应用程序的数据以及使用Tinker一些总结
  • 字符串匹配基础上
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (007)XHTML文档之标题——h1~h6
  • (C#)Windows Shell 外壳编程系列9 - QueryInfo 扩展提示
  • (floyd+补集) poj 3275
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • .naturalWidth 和naturalHeight属性,
  • .Net转前端开发-启航篇,如何定制博客园主题
  • @Documented注解的作用
  • @ModelAttribute 注解
  • @Query中countQuery的介绍
  • @vue/cli 3.x+引入jQuery
  • []FET-430SIM508 研究日志 11.3.31
  • [<MySQL优化总结>]
  • [2018/11/18] Java数据结构(2) 简单排序 冒泡排序 选择排序 插入排序
  • [ACL2022] Text Smoothing: 一种在文本分类任务上的数据增强方法
  • [c#基础]DataTable的Select方法
  • [c++] 什么是平凡类型,标准布局类型,POD类型,聚合体
  • [CareerCup] 12.3 Test Move Method in a Chess Game 测试象棋游戏中的移动方法
  • [CC2642R1][VSCODE+Embedded IDE+IAR Build+Cortex-Debug] TI CC2642R1基于VsCode的开发环境
  • [CTO札记]盛大文学公司名称对联
  • oracle dmp 转 sql_【分享】Oracle 常用运维命令整理
  • 汉诺塔(三)
  • 迷失lost结局什么意思_《lost》《迷失》大结局,没看懂的进,详细讲解
  • linux5 syscall 流程_Linux系统调用(syscall)原理(转)
  • Java基础语法学习6——关系运算符
  • 普联技术java工程师_【普联技术(TP-LINK)java工程师面试】直接面试,过程还算顺利哦-看准网...
  • 鞍部在哪里_富春江,富春江在哪里_富春江在哪个省_属于哪个省_就去旅游网
  • [补档]从OI学麻将
  • android 关闭jack_Android7.0编译 jack常见错误类型及解决办法
  • JDK动态代理具体解释