DotNetty是微软的Azure团队,使用C#实现的Netty的版本发布

Netty(DotNetty)原理解析

一、背景介绍

DotNetty是微软的Azure团队,使用C#实现的Netty的版本发布。不但使用了C#和.Net平台的技术特点,并且保留了Netty原来绝大部分的编程接口。让我们在使用时,完全可以依照Netty官方的教程来学习和使用DotNetty应用程序。

Netty 是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。

二、NIO

他并不是 Java 独有的概念,NIO代表的一个词汇叫着IO多路复用。它是由操作系统提供的系统调用,早期这个操作系统调用的名字是select,但是性能低下,后来渐渐演化成了 Linux 下的epoll和Mac里的kqueue。我们一般就说是epoll,因为没有人拿苹果电脑作为服务器使用对外提供服务。而Netty就是基于Java NIO技术封装的一套框架。为什么要封装,因为原生的Java NIO使用起来没那么方便,而且还有臭名昭著的bug,Netty把它封装之后,提供了一个易于操作的使用模式和接口,用户使用起来也就便捷多了。

说NIO之前先说一下BIO(Blocking IO),如何理解这个Blocking呢?

1.客户端监听(Listen)时,Accept是阻塞的,只有新连接来了,Accept才会返回,主线程才能继读写socket时,Read是阻塞的,只有请求消息来了,Read才能返回,子线程才能继续处理。

2.读写socket时,Write是阻塞的,只有客户端把消息收了,Write才能返回,子线程才能继续读取下一个请求。

3.传统的BIO模式下,从头到尾的所有线程都是阻塞的,这些线程就干等着,占用系统的资源,什么事也不干。

Netty 的非阻塞 I/O 的实现关键是基于 I/O 复用模型,这里用 Selector 对象表示:

1.Netty 的 IO 线程 NioEventLoop 由于聚合了多路复用器 Selector,可以同时并发处理成百上千个客户端连接。

2.当线程从某客户端 Socket 通道进行读写数据时,若没有数据可用时,该线程可以进行其他任务。

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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
while true {
    events = takeEvents(fds)  // 获取事件,如果没有事件,线程就休眠
    for event in events {
        if event.isAcceptable {
            doAccept() // 新链接来了
        } elif event.isReadable {
            request = doRead() // 读消息
            if request.isComplete() {
                doProcess()
            }
        } elif event.isWriteable {
            doWrite()  // 写消息
        }
    }
}复制代码

  

Reactor线程模型

Reactor单线程模型

一个NIO线程+一个accept线程:

由于Reactor模式使用的是异步非阻塞IO,所有的IO操作都不会导致阻塞,理论上一个线程可以独立处理所有IO相关的操作。从架构层面看,一个NIO线程确实可以完成其承担的职责。例如,通过Acceptor类接收客户端的TCP连接请求消息,链路建立成功之后,通过Dispatch将对应的ByteBuffer派发到指定的Handler上进行消息解码。用户线程可以通过消息编码通过NIO线程将消息发送给客户端。

对于一些小容量应用场景,可以使用单线程模型。但是对于高负载、大并发的应用场景却不合适,主要原因如下:

1)一个NIO线程同时处理成百上千的链路,性能上无法支撑,即便NIO线程的CPU负荷达到100%,也无法满足海量消息的编码、解码、读取和发送;

2)当NIO线程负载过重之后,处理速度将变慢,这会导致大量客户端连接超时,超时之后往往会进行重发,这更加重了NIO线程的负载,最终会导致大量消息积压和处理超时,成为系统的性能瓶颈;

3)可靠性问题:一旦NIO线程意外跑飞,或者进入死循环,会导致整个系统通信模块不可用,不能接收和处理外部消息,造成节点故障。

Reactor多线程模型

Reactor多线程模型的特点:

1)有专门一个NIO线程-Acceptor线程用于监听服务端,接收客户端的TCP连接请求;

2)网络IO操作-读、写等由一个NIO线程池负责,线程池可以采用标准的JDK线程池实现,它包含一个任务队列和N个可用的线程,由这些NIO线程负责消息的读取、解码、编码和发送;

3)1个NIO线程可以同时处理N条链路,但是1个链路只对应1个NIO线程,防止发生并发操作问题。

在绝大多数场景下,Reactor多线程模型都可以满足性能需求;但是,在极个别特殊场景中,一个NIO线程负责监听和处理所有的客户端连接可能会存在性能问题。例如并发百万客户端连接,或者服务端需要对客户端握手进行安全认证,但是认证本身非常损耗性能。在这类场景下,单独一个Acceptor线程可能会存在性能不足问题,为了解决性能问题,产生了第三种Reactor线程模型-主从Reactor多线程模型。

Reactor主从模型

主从Reactor线程模型的特点是:服务端用于接收客户端连接的不再是个1个单独的NIO线程,而是一个独立的NIO线程池。Acceptor接收到客户端TCP连接请求处理完成后(可能包含接入认证等),将新创建的SocketChannel注册到IO线程池(sub reactor线程池)的某个IO线程上,由它负责SocketChannel的读写和编解码工作。Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证,一旦链路建立成功,就将链路注册到后端subReactor线程池的IO线程上,由IO线程负责后续的IO操作。

利用主从NIO线程模型,可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题。

它的工作流程总结如下:

  1. 从主线程池中随机选择一个Reactor线程作为Acceptor线程,用于绑定监听端口,接收客户端连接;
  2. Acceptor线程接收客户端连接请求之后创建新的SocketChannel,将其注册到主线程池的其它Reactor线程上,由其负责接入认证、IP黑白名单过滤、握手等操作;
  3. 步骤2完成之后,业务层的链路正式建立,将SocketChannel从主线程池的Reactor线程的多路复用器上摘除,重新注册到Sub线程池的线程上,用于处理I/O的读写操作.

Netty可以基于如上三种模型进行灵活的配置。

总结

Netty是建立在NIO基础之上,Netty在NIO之上又提供了更高层次的抽象。

在Netty里面,Accept连接可以使用单独的线程池去处理,读写操作又是另外的线程池来处理。

Accept连接和读写操作也可以使用同一个线程池来进行处理。而请求处理逻辑既可以使用单独的线程池进行处理,也可以跟放在读写线程一块处理。线程池中的每一个线程都是NIO线程。用户可以根据实际情况进行组装,构造出满足系统需求的高性能并发模型。

DotNetty是微软的Azure团队,使用C#实现的Netty的版本发布的更多相关文章

  1. 团队第三次作业:Alpha版本发布

    这个作业属于哪个课程 课程链接 这个作业要求在哪里 作业要求链接 团队名称 众志陈成 这个作业的目标 通过团队协作了解软件开发的大致流程,并在这个过程中体会调整与优化程序的方法,为以后真实的软件开发奠 ...

  2. 本号讯 | 永不消失的协作“空间站”开课;微软推出微软云Azure文档网站

    8月29日,针对企业常面临的“协同办公”困难,开展以“还有这种操作?永不消失的协作'空间站'”为主题的协同办公培训课. 课程内容包含:在Office 365环境中,如何利用Teams与Groups等功 ...

  3. 微软云Azure Website 远程调试

    微软云Azure Website 远程调试 是可以的 但是只有48小时,要在后台开启,所以还是很麻烦的啊! 但是安全性提高了,不得不承认哦

  4. 微软推 Azure 机器学习工具:Algorithm Cheat Sheet

    微软推 Azure 机器学习工具:Algorithm Cheat Sheet [日期:2015-05-15] 来源:CSDN  作者:Linux [字体:大 中 小]   Azure Machine ...

  5. 微软云 azure 数据迁移之oracle11g dataguard

    背景,将本地的oracle数据迁移到微软云azure云上面的oracleserver. 1.复制本地的rman备份集到微软云azure的oracleserver上 scp -r -P56922 201 ...

  6. 30分钟连接树莓派到微软云 Azure IoT Hub,并将数据进行可视化

    更多内容,关注公众号: 树莓派是很多动手达人必备的小玩具,本节内容,让我们拿出树莓派,在30分钟内,将树莓派连接到微软云Azure的IoT Hub,然后将温湿度曲线可视化.(本节内容完整视频在文章末尾 ...

  7. 【Azure 环境】连接到微软云Azure中国区 By VS 2019, VS Code, Powershell

    问题情形 最近,在使用最新的VS Code插件连接到中国区的Azure时候,出现了依旧是global版的登录连接.这个问题是当前Azure Account插件最新版的问题,可以使用V0.8.11版本登 ...

  8. [.net 面向对象程序设计进阶] (27) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git

    [.net 面向对象程序设计进阶] (26) 团队开发利器(六)分布式版本控制系统Git——在Visual Studio 2015中使用Git 本篇导读: 接上两篇,继续Git之旅 分布式版本控制系统 ...

  9. [.net 面向对象程序设计进阶] (26) 团队开发利器(五)分布式版本控制系统Git——图形化Git客户端工具TortoiseGit

    [.net 面向对象程序设计进阶] (26) 团队开发利器(五)分布式版本控制系统Git——图形化Git客户端工具TortoiseGit 读前必备: 接上篇: 分布式版本控制系统Git——使用GitS ...

随机推荐

  1. mysql 查询今天,昨天,上个月sql语句

    今天 select * from 表名 where to_days(时间字段名) = to_days(now()); 昨天Select * FROM 表名 Where TO_DAYS( NOW( ) ...

  2. 7、Python变量流程基础(变量、赋值、格式化、运算符、流程控制、range()函数)

    一.执行Python程序的两种方式 1.交互式 在终端内输入“python3”,然后输入python代码 2.命令行式 在终端内输入“python3 文本文件路径” 二.变量 1.变量的组成 Pyth ...

  3. python - django 执行数据库迁移后数据库并未更新 和 InternalError: (1054, u"Unknown column 'xxx' in 'field list'")问题

    一.发生情况:当你修改数据库结构后进行 python manage.py makemigrations 和 python manage.py migrate 后发现控制台会给你返回一个下面的结果,但是 ...

  4. js form 表单属性学习

    一.<form></form>标签      引用借鉴:http://www.cnblogs.com/fizx/p/6703370.html form标签的属性规定了当前网页上 ...

  5. MySQL 硬链接删除大表

    在清理整个大表时,我们推荐使用drop,而非delete.但是如果表实在太大,即使是drop,也需要消耗一定的时间.这时可以利用linux的硬连接来快速删除大表,操作过程如下:有一个大表test,共有 ...

  6. php保存canvas导出的base64图片

    代码如下: <?php $img='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAABxCAYAAABoUdWRAAAAAXNSR0IAr ...

  7. 使用RedisDesktopManager客户端无法连接Redis服务器问题解决办法

    是否遇到安装完成后连不上的问题? 那么这篇教程能解决. 执行步骤: 1.修改redis文件夹下redis.cong文件,在bind 127.0.0.1行前面加#注释掉这一行,使能远程连接(默认只能使用 ...

  8. (11)Go方法/接收者

    方法和接收者 Go语言中的方法(Method)是一种作用于特定类型变量的函数.这种特定类型变量叫做接收者(Receiver).接收者的概念就类似于其他语言中的this或者 self. 方法的定义格式如 ...

  9. mybatis 获取新增数据的主键

    <insert id="insert" parameterType="com.mmall.pojo.Shipping" useGeneratedKeys= ...

  10. fluent meshing建立周期性网格

    原视频下载地址:https://pan.baidu.com/s/1pKUXKgz 密码: 6pwh