.NET Core微服务之路:利用DotNetty实现一个简单的通信过程

.NET Core微服务之路:利用DotNetty实现一个简单的通信过程   上一篇我们已经全面的介绍过《基于gRPC服务发现与服务治理的方案》,我们先复习一下RPC的调用过程(笔者会在这一节的几篇文章中反复的强调这个过程调用方案),看下图根据上面图,服务化原理可以分为3步:服务端启动并且向注册中心发送服务信息,注册中心收到后会...
摘要由CSDN通过智能技术生成

.NET Core微服务之路:利用DotNetty实现一个简单的通信过程

  上一篇我们已经全面的介绍过《基于gRPC服务发现与服务治理的方案》,我们先复习一下RPC的调用过程(笔者会在这一节的几篇文章中反复的强调这个过程调用方案),看下图
根据上面图,服务化原理可以分为3步:
  1. 服务端启动并且向注册中心发送服务信息,注册中心收到后会定时监控服务状态(常见心跳检测);
  2. 客户端需要开始调用服务的时候,首先去注册中心获取服务信息;
  3. 客户端创建远程调用连接,连接后服务端返回处理信息;
 
第3步又可以细分,下面说说远程过程调用的原理:
目标:客户端怎么调用远程机器上的公开方法
  1. 服务发现,向注册中心获取服务(这里需要做的有很多:拿到多个服务时需要做负载均衡,同机房过滤、版本过滤、服务路由过滤、统一网关等);
  2. 客户端发起调用,将需要调用的服务、方法、参数进行组装;
  3. 序列化编码组装的消息,这里可以使用json,也可以使用xml,也可以使用protobuf,也可以使用hessian,几种方案的序列化速度还有序列化后占用字节大小都是选择的重要指标,对内笔者建议使用高效的protobuf,它基于TCP/IP二进制进行序列化,体积小,速度快。
  4. 传输协议,可以使用传统的io阻塞传输,也可以使用高效的nio传输(Netty);
  5. 服务端收到后进行反序列化,然后进行相应的处理;
  6. 服务端序列化response信息并且返回;
  7. 客户端收到response信息并且反序列化;
 
  正如上面第三步的第4条所提到,C类向S类调用时,可以选择RPC或者RESTful,而作为内部通讯,笔者强烈建议使用RPC的方式去调用S类上的所有服务,RPC对比RESTful如下:
优点:
  1. 序列化采用二进制消息,性能好/效率高(空间和时间效率都很不错);
  2. 序列化反序列化直接对应程序中的数据类,不需要解析后在进行映射(XML,JSON都是这种方式);
  3. 相比http协议,没有无用的header,简化传输数据的大小,且基于TCP层传输,速度更快,容量更小;
  4. Netty等一些框架集成(重点,也是本篇介绍的主要框架);
缺点:
  1. 使用复杂,维护成本和学习成本较高,调试困难;
  2. 因为基于HTTP2,绝大部多数HTTP Server、Nginx都尚不支持,即Nginx不能将GRPC请求作为HTTP请求来负载均衡,而是作为普通的TCP请求。(nginx1.9版本已支持);
  3. 二进制可读性差,或者几乎没有任何直接可读性,需要专门的工具进行反序列化;
  4. 默认不具备动态特性(可以通过动态定义生成消息类型或者动态编译支持,后续会介绍利用Rosyln进行动态编译的特性);

 

通信传输利器Netty(Net is DotNetty)介绍

  (先埋怨一下微软大大)我们做NET开发,十分羡慕JAVA上能有NETTY, SPRING, STRUTS, DUBBO等等优秀框架,而我们NET就只有干瞪眼,哎,无赖之前生态圈没做好,恨铁不成钢啊。不过由于近来Net Core的发布,慢慢也拉回了一小部分属于微软的天下,打住,闲话扯到这儿。
  DotNetty是Azure团队仿照(几乎可以这么说)JAVA的Netty而出来的(目前已实现Netty的一部分),目前在Github上的Star有1.8K+,地址: https://github.com/Azure/DotNetty,没有任何文档,和代码中少量的注释。虽然比Netty出来晚了很多年,不过我们NET程序员们也该庆幸了,在自己的平台上终于能用上类似Netty这样强大的通信框架了。

传统通讯的问题:

  我们使用通用的应用程序或者类库来实现互相通讯,比如,我们经常使用一个 HTTP 客户端库来从 web 服务器上获取信息,或者通过 web 服务来执行一个远程的调用。
  然而,有时候一个通用的协议或他的实现并没有很好的满足需求。比如我们无法使用一个通用的 HTTP 服务器来处理大文件、电子邮件以及近实时消息,比如金融信息和多人游戏数据。我们需要一个高度优化的协议来处理一些特殊的场景。例如你可能想实现一个优化了的 Ajax 的聊天应用、媒体流传输或者是大文件传输器,你甚至可以自己设计和实现一个全新的协议来准确地实现你的需求。
  另一个不可避免的情况是当你不得不处理遗留的专有协议来确保与旧系统的互操作性。在这种情况下,重要的是我们如何才能快速实现协议而不牺牲应用的稳定性和性能。

解决:

  Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能、可扩展协议的服务器和客户端。
  换句话说,Netty 是一个 NIO 客户端服务器框架,使用它可以快速简单地开发网络应用程序,比如服务器和客户端的协议。Netty 大大简化了网络程序的开发过程比如 TCP 和 UDP 的 socket 服务的开发。
“快速和简单”并不意味着应用程序会有难维护和性能低的问题,Netty 是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验比如 FTP、SMTP、HTTP、许多二进制和基于文本的传统协议.因此,Netty 已经成功地找到一个方式,在不失灵活性的前提下来实现开发的简易性,高性能,稳定性。
  有一些用户可能已经发现其他的一些网络框架也声称自己有同样的优势,所以你可能会问是 Netty 和它们的不同之处。答案就是 Netty 的哲学设计理念。Netty 从开始就为用户提供了用户体验最好的 API 以及实现设计。正是因为 Netty 的哲学设计理念,才让您得以轻松地阅读本指南并使用 Netty。

(DotNetty的框架和实现是怎么回事,笔者不太清楚,但完全可参考Netty官方的文档来学习和使用DotNetty相关的API接口)

 

DotNetty中几个重要的库(程序集):

DotNetty.Buffers: 对内存缓冲区管理的封装。
DotNetty.Codecs: 对编解码是封装,包括一些基础基类的实现,我们在项目中自定义的协议,都要继承该项目的特定基类和实现。
DotNetty.Codecs.Mqtt: MQTT(消息队列遥测传输)编解码是封装,包括一些基础基类的实现。
DotNetty.Codecs.Protobuf: Protobuf 编解码是封装,包括一些基础基类的实现。
DotNetty.Codecs.ProtocolBuffers: ProtocolBuffers编解码是封装,包括一些基础基类的实现。
DotNetty.Codecs.Redis: Redis 协议编解码是封装,包括一些基础基类的实现。
DotNetty.Common: 公共的类库项目,包装线程池,并行任务和常用帮助类的封装。
DotNetty.Handlers: 封装了常用的管道处理器,比如Tls编解码,超时机制,心跳检查,日志等。
DotNetty.Transport: DotNetty核心的实现,Socket基础框架,通信模式:异步非阻塞。
DotNetty.Transport.Libuv: DotNetty自己实现基于Libuv (高性能的,事件驱动的I/O库) 核心的实现。
常用的库有Codecs, Common, Handlers, Buffers, Transport,目前Azure团队正在实现其他Netty中的API(包括非公共Netty的API),让我们拭目以待吧。
 

直接上点对点之间通讯的栗子

  DotNetty的Example文件夹下有许多官方提供的实例,有抛弃服务实例(Discard),有应答服务实例(echo),有Telnet服务实例等等,为了实现直接点对点通讯,笔者采用了Echo的demo,此后的RPC调用也会基于Echo而实现,注释详细,直接上接收端(Server)的代码:
  1 /*
  2 * Netty 是一个半成品,作用是在需要基于自定义协议的基础上完成自己的通信封装
  3 * Netty 大大简化了网络程序的开发过程比如 TCP 和 UDP 的 socket 服务的开发。
  4 * “快速和简单”并不意味着应用程序会有难维护和性能低的问题,
  5 * Netty 是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验比如 FTP、SMTP、HTTP、许多二进制和基于文本的传统协议。
  6 * 因此,Netty 已经成功地找到一个方式,在不失灵活性的前提下来实现开发的简易性,高性能,稳定性。
  7 */
  8 
  9 namespace Echo.Server
 10 {
 11     using System;
 12     using System.Threading.Tasks;
 13     using DotNetty.Codecs;
 14     using DotNetty.Handlers.Logging;
 15     using DotNetty.Transport.Bootstrapping;
 16     using DotNetty.Transport.Channels;
 17     using DotNetty.Transport.Libuv;
 18     using Examples.Common;
 19 
 20     static class Program
 21     {
 22         static async Task RunServerAsync()
 23         {
 24             ExampleHelper.SetConsoleLogger();
 25             
 26             // 申明一个主回路调度组
 27             var dispatcher = new DispatcherEventLoopGroup();
 28 
 29             /*
 30              Netty 提供了许多不同的 EventLoopGroup 的实现用来处理不同的传输。
 31              在这个例子中我们实现了一个服务端的应用,因此会有2个 NioEventLoopGroup 会被使用。
 32              第一个经常被叫做‘boss’,用来接收进来的连接。第二个经常被叫做‘worker’,用来处理已经被接收的连接,一旦‘boss’接收到连接,就会把连接信息注册到‘worker’上。
 33              如何知道多少个线程已经被使用,如何映射到已经创建的 Channel上都需要依赖于 IEventLoopGroup 的实现,并且可以通过构造函数来配置他们的关系。
 34              */
 35 
 36             // 主工作线程组,设置为1个线程
 37             IEventLoopGroup bossGroup = dispatcher; // (1)
 38             // 子工作线程组,设置为1个线程
 39             IEventLoopGroup workerGroup = new WorkerEventLoopGroup(dispatcher);
 40 
 41             try
 42             {
 43                 // 声明一个服务端Bootstrap,每个Netty服务端程序,都由ServerBootstrap控制,通过链式的方式组装需要的参数<
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值