异步事件驱动的网络应用程序框架:Netty(DotNetty)原理解析
一、背景介绍DotNetty是微软的Azure团队 , 使用C#实现的Netty的版本发布 。 不但使用了C#和.Net平台的技术特点 , 并且保留了Netty原来绝大部分的编程接口 。 让我们在使用时 , 完全可以依照Netty官方的教程来学习和使用DotNetty应用程序 。
Netty是一个异步事件驱动的网络应用程序框架 , 用于快速开发可维护的高性能协议服务器和客户端 。
二、NIO他并不是Java独有的概念 , NIO代表的一个词汇叫着IO多路复用 。 它是由操作系统提供的系统调用 , 早期这个操作系统调用的名字是select , 但是性能低下 , 后来渐渐演化成了Linux下的epoll和Mac里的kqueue 。 我们一般就说是epoll , 因为没有人拿苹果电脑作为服务器使用对外提供服务 。 而Netty就是基于JavaNIO技术封装的一套框架 。 为什么要封装 , 因为原生的JavaNIO使用起来没那么方便 , 而且还有臭名昭著的bug , Netty把它封装之后 , 提供了一个易于操作的使用模式和接口 , 用户使用起来也就便捷多了 。
说NIO之前先说一下BIO(BlockingIO),如何理解这个Blocking呢?
3.传统的BIO模式下 , 从头到尾的所有线程都是阻塞的 , 这些线程就干等着 , 占用系统的资源 , 什么事也不干 。
Netty的非阻塞I/O的实现关键是基于I/O复用模型 , 这里用Selector对象表示:
3.线程通常将非阻塞IO的空闲时间用于在其他通道上执行IO操作 , 所以单独的线程可以管理多个输入和输出通道 。
4.由于读写操作都是非阻塞的 , 这就可以充分提升IO线程的运行效率 , 避免由于频繁I/O阻塞导致的线程挂起 。
5.一个I/O线程可以并发处理N个客户端连接和读写操作 , 这从根本上解决了传统同步阻塞I/O一连接一线程模型 , 架构的性能、弹性伸缩能力和可靠性都得到了极大的提升 。
基于Buffer传统的I/O是面向字节流或字符流的 , 以流式的方式顺序地从一个Stream中读取一个或多个字节,因此也就不能随意改变读取指针的位置 。
在NIO中 , 抛弃了传统的I/O流 , 而是引入了Channel和Buffer的概念 。 在NIO中 , 只能从Channel中读取数据到Buffer中或将数据从Buffer中写入到Channel 。
基于Buffer操作不像传统IO的顺序操作 , NIO中可以随意地读取任意位置的数据 。
事件驱动模型通常 , 我们设计一个事件处理模型的程序有两种思路:
1.轮询方式 , 线程不断轮询访问相关事件发生源有没有发生事件 , 有发生事件就调用事件处理逻辑 。 2.事件驱动方式 , 发生事件 , 主线程把事件放入事件队列 , 在另外线程不断循环消费事件列表中的事件 , 调用事件对应的处理逻辑处理事件 。 事件驱动方式也被称为消息通知方式 , 其实是设计模式中观察者模式的思路 。事件机制 , 它可以用一个线程把Accept , 读写操作 , 请求处理的逻辑全干了 。 如果什么事都没得做 , 它也不会死循环 , 它会将线程休眠起来 , 直到下一个事件来了再继续干活 , 这样的一个线程称之为NIO线程 。 用伪代码表示:
1)一个NIO线程同时处理成百上千的链路 , 性能上无法支撑 , 即便NIO线程的CPU负荷达到100% , 也无法满足海量消息的编码、解码、读取和发送;
2)当NIO线程负载过重之后 , 处理速度将变慢 , 这会导致大量客户端连接超时 , 超时之后往往会进行重发 , 这更加重了NIO线程的负载 , 最终会导致大量消息积压和处理超时 , 成为系统的性能瓶颈;
3)可靠性问题:一旦NIO线程意外跑飞 , 或者进入死循环 , 会导致整个系统通信模块不可用 , 不能接收和处理外部消息 , 造成节点故障 。
Reactor多线程模型
2)网络IO操作-读、写等由一个NIO线程池负责 , 线程池可以采用标准的JDK线程池实现 , 它包含一个任务队列和N个可用的线程 , 由这些NIO线程负责消息的读取、解码、编码和发送;
3)1个NIO线程可以同时处理N条链路 , 但是1个链路只对应1个NIO线程 , 防止发生并发操作问题 。
在绝大多数场景下 , Reactor多线程模型都可以满足性能需求;但是 , 在极个别特殊场景中 , 一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题 。 例如并发百万客户端连接 , 或者服务端需要对客户端握手进行安全认证 , 但是认证本身非常损耗性能 。 在这类场景下 , 单独一个Acceptor线程可能会存在性能不足问题 , 为了解决性能问题 , 产生了第三种Reactor线程模型-主从Reactor多线程模型 。
Reactor主从模型
它的工作流程总结如下:
从主线程池中随机选择一个Reactor线程作为Acceptor线程 , 用于绑定监听端口 , 接收客户端连接;Acceptor线程接收客户端连接请求之后创建新的SocketChannel , 将其注册到主线程池的其它Reactor线程上 , 由其负责接入认证、IP黑白名单过滤、握手等操作;步骤2完成之后 , 业务层的链路正式建立 , 将SocketChannel从主线程池的Reactor线程的多路复用器上摘除 , 重新注册到Sub线程池的线程上 , 用于处理I/O的读写操作.Netty可以基于如上三种模型进行灵活的配置 。
总结Netty是建立在NIO基础之上 , Netty在NIO之上又提供了更高层次的抽象 。
在Netty里面 , Accept连接可以使用单独的线程池去处理 , 读写操作又是另外的线程池来处理 。
【异步事件驱动的网络应用程序框架:Netty(DotNetty)原理解析】Accept连接和读写操作也可以使用同一个线程池来进行处理 。 而请求处理逻辑既可以使用单独的线程池进行处理 , 也可以跟放在读写线程一块处理 。 线程池中的每一个线程都是NIO线程 。 用户可以根据实际情况进行组装 , 构造出满足系统需求的高性能并发模型 。
推荐阅读
- 澎湃新闻|仝卓事件被写入公职人员学习读本 仝卓事件怎么回事
- 仝卓事件通报成《公职人员政务处分法》学习读本案例
- Acer|[图]NVIDIA发布452.06新驱动,《魔兽世界:暗影之境》将启用光追
- 驱动中国|实地智慧小区再添利器如影智能门“软硬”结合守护业主安全
- 航空|吉祥航空:网传不雅照片事件涉事飞行员已被停飞
- 孟晚舟|加拿大与FBI合谋在机场故意拖延时间搜查孟晚舟,外交部:这是一起严重政治事件
- 红星美凯龙集团|红星美凯龙南三环店爆发商户维权事件 商场生意冷清或是矛盾根结
- 疫情|韩连续4天单日新增病例过百!首都圈教会群聚感染事件频发致疫情反弹
- 孟晚舟|加拿大与FBI合谋在机场故意拖延时间搜查孟晚舟 外交部:这是一起严重政治事件 加拿大扮演了美方帮凶角色
- 纽约|美国纽约过去36小时约40人卷入枪击事件 造成2人死亡
