Netty异步非阻塞事件驱动及原理详解

       本文基于 Netty 4.1 展开介绍相关理论模型、使用场景、基本组件、整体架构,知其然且知其所以然,希望给大家在实际开发实践、学习开源项目方面提供参考。
       Netty 是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端。

一、JDK 原生 NIO 程序的问题

JDK 原生也有一套网络应用程序 API,但是存在一系列问题,主要如下:

  • NIO 的类库和 API 繁杂,使用麻烦。你需要熟练掌握 Selector、ServerSocketChannel、SocketChannel、ByteBuffer 等。
  • 需要具备其他的额外技能做铺垫。例如熟悉 Java 多线程编程,因为 NIO 编程涉及到 Reactor 模式,你必须对多线程和网路编程非常熟悉,才能编写出高质量的 NIO 程序。
  • 可靠性能力补齐,开发工作量和难度都非常大。例如客户端面临断连重连、网络闪断、半包读写、失败缓存、网络拥塞和异常码流的处理等等。

NIO 编程的特点是功能开发相对容易,但是可靠性能力补齐工作量和难度都非常大。

  • JDK NIO 的 Bug。例如臭名昭著的 Epoll Bug,它会导致 Selector 空轮询,最终导致 CPU 100%。

官方声称在 JDK 1.6 版本的 update 18 修复了该问题,但是直到 JDK 1.7 版本该问题仍旧存在,只不过该 Bug 发生概率降低了一些而已,它并没有被根本解决。

二、Netty 的特点

Netty 对 JDK 自带的 NIO 的 API 进行封装,解决上述问题,主要特点有:

  • 设计优雅,适用于各种传输类型的统一 API 阻塞和非阻塞 Socket;基于灵活且可扩展的事件模型,可以清晰地分离关注点;高度可定制的线程模型 - 单线程,一个或多个线程池;真正的无连接数据报套接字支持(自 3.1 起)。
  • 使用方便,详细记录的 Javadoc,用户指南和示例;没有其他依赖项,JDK 5(Netty 3.x)或 6(Netty 4.x)就足够了。
  • 高性能,吞吐量更高,延迟更低;减少资源消耗;最小化不必要的内存复制。
  • 安全,完整的 SSL/TLS 和 StartTLS 支持。
  • 社区活跃,不断更新,社区活跃,版本迭代周期短,发现的 Bug 可以被及时修复,同时,更多的新功能会被加入。

三、Netty 常见使用场景

Netty 常见的使用场景如下:

  • 互联网行业。在分布式系统中,各个节点之间需要远程服务调用,高性能的 RPC 框架必不可少,Netty 作为异步高性能的通信框架,往往作为基础通信组件被这些 RPC 框架使用。

       典型的应用有:阿里分布式服务框架 Dubbo 的 RPC 框架使用 Dubbo 协议进行节点间通信,Dubbo 协议默认使用 Netty 作为基础通信组件,用于实现各进程节点之间的内部通信。

  • 游戏行业。无论是手游服务端还是大型的网络游戏,Java 语言得到了越来越广泛的应用。Netty 作为高性能的基础通信组件,它本身提供了 TCP/UDP 和 HTTP 协议栈。

       非常方便定制和开发私有协议栈,账号登录服务器,地图服务器之间可以方便的通过 Netty 进行高性能的通信。

  • 大数据领域。经典的 Hadoop 的高性能通信和序列化组件 Avro 的 RPC 框架,默认采用 Netty 进行跨界点通信,它的 Netty Service 基于 Netty 框架二次封装实现。

三、Netty 高性能设计

       Netty 作为异步事件驱动的网络,高性能之处主要来自于其 I/O 模型线程处理模型,前者决定如何收发数据,后者决定如何处理数据。

3.1、I/O 模型

用什么样的通道将数据发送给对方,BIO、NIO 或者 AIO,I/O 模型在很大程度上决定了框架的性能。

(1)、同步阻塞IO

       阻塞指的是I/O方法调用(比如read)在数据或者状态还没有就绪的时候,一直等待,直到就绪才返回。比如阻塞模式下的Socket的read方法,如果没有接收到数据,read方法将会阻塞,程序就会停在那里,不能做其他的事情。这种模型下,如果服务器想处理多个连接,那么就要为每个Socket连接创建一个单独的线程,开销会很大。

传统阻塞型 I/O(BIO)可以用下图表示:

特点如下:

  • 每个请求都需要独立的线程完成数据 Read,业务处理,数据 Write 的完整操作问题。
  • 当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。
  • 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费。

(2)、同步非阻塞IO 

       非阻塞模型是I/O方法调用(比如read)无论数据有没有就绪,会马上返回。如果有数据就会读到数据,如果没有数据就返回一个错误码。应用程序需要用轮询的方式不断去检测数据有没有就绪,但是程序不会被阻塞,除了轮询程序还可以做其他事情。但是这种轮询的方式会浪费CPU的时间,效率不够高。

(3)、IO多路复用

       IO多路复用模型是建立在内核提供的多路分离函数select基础之上的,使用select函数可以避免同步非阻塞IO模型中轮询等待的问题。

       如上图《多路分离函数select》所示,用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。
       从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。
       然而,使用select函数的优点并不仅限于此。虽然上述方式允许单线程内处理多个IO请求,但是每个IO请求的过程还是阻塞的(在select函数上阻塞),平均时间甚至比同步阻塞IO模型还要长。如果用户线程只注册自己感兴趣的socket或者IO请求,然后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU的利用率。

       如上图《Reactor实现多路复用》所示,EventHandler抽象类表示IO事件处理器,它拥有IO文件句柄Handle(通过get_handle获取),以及对Handle的操作handle_event(读/写等)。继承于EventHandler的子类可以对事件处理器的行为进行定制。Reactor类用于管理EventHandler(注册、删除等),并使用handle_events实现事件循环,不断调用同步事件多路分离器(一般是内核)的多路分离函数select,只要某个文件句柄被激活(可读/写等),select就返回(阻塞),handle_events就会调用与文件句柄关联的事件处理器的handle_event进行相关操作。

       如上图《Reactor实现多路复用》所示,通过Reactor的方式,可以将用户线程轮询IO操作状态的工作统一交给handle_events事件循环进行处理。用户线程注册事件处理器之后可以继续执行做其他的工作(异步),而Reactor线程负责调用内核的select函数检查socket状态。当有socket被激活时,则通知相应的用户线程(或执行用户线程的回调函数),执行handle_event进行数据读取、处理的工作。由于select函数是阻塞的,因此多路IO复用模型也被称为异步阻塞IO模型。注意,这里的所说的阻塞是指select函数执行时线程被阻塞,而不是指socket。一般在使用IO多路复用模型时,socket都是设置为NONBLOCK的,不过这并不会产生影响,因为用户发起IO请求时,数据已经到达了,用户线程一定不会被阻塞。
       IO多路复用是最常使用的IO模型,但是其异步程度还不够“彻底”,因为它使用了会阻塞线程的select系统调用。因此IO多路复用只能称为异步阻塞IO,而非真正的异步IO。 

(4)、异步IO 

       异步模型是指用户给I/O方法调用(比如read)提供一个回调方法,I/O方法调用会立刻返回,等到数据就绪时回调方法会执行。这个看上去很好用,把所有的处理都推给了系统,用户只需要关心回调方法中的数据处理就行了,但是在高并发情况下,处理好系统I/O程序和用户程序之间的CPU竞争比较困难。

       如上图《Proactor设计模式》所示,Proactor模式和Reactor模式在结构上比较相似,不过在用户(Client)使用方式上差别较大。Reactor模式中,用户线程通过向Reactor对象注册感兴趣的事件监听,然后事件触发时调用事件处理函数。而Proactor模式中,用户线程将AsynchronousOperation(读/写等)、Proactor以及操作完成时的CompletionHandler注册到AsynchronousOperationProcessor。AsynchronousOperationProcessor使用Facade模式提供了一组异步操作API(读/写等)供用户使用,当用户线程调用异步API后,便继续执行自己的任务。AsynchronousOperationProcessor 会开启独立的内核线程执行异步操作,实现真正的异步。当异步IO操作完成时,AsynchronousOperationProcessor将用户线程与AsynchronousOperation一起注册的Proactor和CompletionHandler取出,然后将CompletionHandler与IO操作的结果数据一起转发给Proactor,Proactor负责回调每一个异步操作的事件完成处理函数handle_event。虽然Proactor模式中每个异步操作都可以绑定一个Proactor对象,但是一般在操作系统中,Proactor被实现为Singleton模式,以便于集中化分发操作完成事件。

       如上图《Proactor实现异步IO》所示,异步IO模型中,用户线程直接使用内核提供的异步IO API发起read请求,且发起后立即返回,继续执行用户线程代码。不过此时用户线程已经将调用的AsynchronousOperation和CompletionHandler注册到内核,然后操作系统开启独立的内核线程去处理IO操作。当read请求的数据到达时,由内核负责读取socket中的数据,并写入用户指定的缓冲区中。最后内核将read的数据和用户线程注册的CompletionHandler分发给内部Proactor,Proactor将IO完成的信息通知给用户线程(一般通过调用用户线程注册的完成事件处理函数),完成异步IO。
       相比于IO多路复用模型,异步IO并不十分常用,不少高性能并发服务程序使用IO多路复用模型+多线程任务处理的架构基本可以满足需求。况且目前操作系统对异步IO的支持并非特别完善,更多的是采用IO多路复用模型模拟异步IO的方式(IO事件触发时不直接通知用户线程,而是将数据读写完毕后放到用户指定的缓冲区中)。Java7之后已经支持了异步IO,感兴趣的读者可以尝试使用。

3.2、Reactor多线程模型

(1)、大部分网络服务包括以下处理步骤

  1. read request(读取客户端发送过来的byte数据)
  2. decode request(把byte数据解码成特定类型的数据)
  3. process (compute) service(根据请求数据进行业务处理)
  4. encode reply(把处理结果转换成byte数据)
  5. send reply(发送byte数据给客户端)

(2)、Reactor多线程模型图

  1. Reactor - 负责响应IO事件,把事件分发给相应的处理代码。Reactor运行在一个独立的线程中(非Thread Pool中的线程)。具体来说,Reactor主要有两个职责,一个是处理来自客户端的连接事件,处理代码由acceptor实现;另一个是处理读取和发送数据的事件,处理代码由Handler实现。
  2. Acceptor - 用以接受客户端的连接请求,然后创建Handler对连接进行后续的处理(读取,处理,发送数据)。
  3. Handler - 事件处理类,用以实现具体的业务逻辑。图中read,decode,compute,encode和send都是由handler实现的。
  4. Thread Pool - Thread Pool中的thread被称作worker thread。Handler中的decode,compute和encode是用worker thread执行的。

值得注意的是Handler中的read和send方法是在Reactor线程而不是worker thread中执行的。这意味着对socket数据的读取发送数据和对数据的处理是在不同的线程中进行的.

(3)、Reactor多线程模型的主要问题

  1. read和send会影响接受客户端连接的性能
前面分析过read和send是在Reactor线程中执行的,接受客户端的连接请求也是在Reactor线程中执行。这使得如果有read或者send耗时较长,会影响其他客户端连接的速度。
  2. Read和send性能不够高效
网络服务对于来自同一客户端的read和send是串行的,但是对于不同客户端之间的read和send是可以并行进行的。由于read和send运行在Reactor单线程中,不能充分发挥硬件能力。
  3. 线程上下文切换带来额外开销
前面提到的处理客户端请求的步骤依次是read,decode,process,encode,send。由于read和send是在Reactor线程中执行,而decode,process和encode是在worker thread线程中执行,引入了额外的线程切换开销,这种开销在高并发的时候会体现出来。

(4)、实际应用中的多线程模型(改进后的模型图)

       Reactor线程专门用于接受客户端连接(通过acceptor);创建多个Event Loop ,组成Event Loop Pool,每个Event Loop都有自己的Selector,并且运行在独立的线程上;Acceptor对于每一个客户端的连接从EventLoopPool中选择一个Event Loop进行处理,并且保证每个客户端连接在整个生命周期中都是由同一个Event Loop线程来处理,从而使得Handler中的实现-read,decode,process,encode,send-都在同一个线程中执行。整个线程模型除了高效的性能,还有非常重要的一点是Handler的实现不需要加锁,一方面对性能有帮助,另一方面避免多线程编程的复杂度。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/480889.shtml

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

相关文章

文本预处理跑得慢?抱抱脸团队又放福利,1GB文本语料分词只需20s!

一只小狐狸带你解锁NLP/DL/ML秘籍正文素材来源:量子位缘起BERTBERT带来的并不是只有一大波paper和嗷嗷上线,还带火了一个NLP团队——Huggingface(中文译作抱抱脸)。抱抱脸团队是一个创业团队。他们的Transformers是github上最火的…

基于深度学习的FAQ问答系统

| 导语 问答系统是信息检索的一种高级形式,能够更加准确地理解用户用自然语言提出的问题,并通过检索语料库、知识图谱或问答知识库返回简洁、准确的匹配答案。相较于搜索引擎,问答系统能更好地理解用户提问的真实意图, 进一步能更有效地满足用…

基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba的企业级微服务敏捷开发系统架构

一、项目总体架构图 二、功能介绍 2.1、统一认证功能 支持oauth2的四种模式登录 支持用户名、密码加图形验证码登录 支持手机号加密码登录 支持openId登录 支持第三方系统单点登录 2.2、分布式系统基础支撑 服务注册发现、路由与负载均衡 服务降级与熔断 服务限流(url/方法级别…

征文通知 | 2018年全国知识图谱与语义计算大会

2018年全国知识图谱与语义计算大会China Conference on Knowledge Graph and Semantic Computing (CCKS 2018)2018年8月15日-18日,天津征稿截止: 2018年5月18日全国知识图谱与语义计算大会(CCKS: China Conference on Knowledge Graph and Semantic Comp…

如何让聊天机器人懂情感?这是一篇来自清华的论文

原载:Paperweekly作者:黄民烈,清华大学老师关注自然语言处理、人机对话情感分析等方向aihuangtsinghua.edu.cn当你悲伤的时候,机器人可以安慰你;当你高兴的时候,机器人为你高兴。悲你所悲,喜你所…

戴着口罩也要开心过年吖!

一只小狐狸带你解锁NLP/ML/DL秘籍素材来源于网络新的一年????就要到来了作为超(bu)高(shan)智(yan)商(ci)的程序员如何向亲朋好友证明我们是人群中最靓的仔呢小夕为你准备了专属程序员的新春祝福~~普天同庆for (;;) { print("鼠年快乐"); }(满屏的新年…

【HTML/CSS】margin塌陷和合并问题

1 margin塌陷问题 1.1 示例 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>margin塌陷</title…

集群、分布式、微服务的概念及异同

一、什么是集群&#xff1f; 集群是指将多台服务器集中在一起&#xff0c;每台服务器都实现相同的业务&#xff0c;做相同的事&#xff1b;但是每台服务器并不是缺一不可&#xff0c;存在的主要作用是缓解并发能力和单点故障转移问题。 集群主要具有以下特征&#xff1a;&…

论文浅尝 | Complex Embeddings for Simple Link Prediction

The ́o Trouillon, Johannes Welb, Sebastian Riedel, ÉricGaussier, Guillaume Bouchard . Complex Embeddings for Simple Link Prediction. In Proceedings of the 33ndInternational Conference on Machine Learning, pages 2071– 2080 (ICML2016)论文链接&#xff1a;…

详解医学顶刊《柳叶刀》最新发表新型冠状病毒研究论文

我是夕小瑶&#xff0c;一只热爱科研的小狐狸作者&#xff1a;夕小瑶&#xff0c;小鹿鹿鹿&#xff0c;QvQ前言新型冠状病毒&#xff08;2019-nCoV&#xff09;的突发恰逢春节&#xff0c;千家万户都对疫情十分关注。令人心痛的是&#xff0c;病毒带来的不止有病痛&#xff0c;…

AI学习笔记--人机对话的四种形态

AI学习笔记--人机对话的四种形态 ><div class"show-content-free"><p>最近在筹备转行 AI PM 由于之前的产品线也有做过类似 AI 的智能客服产品&#xff0c;所以打算先从人机对话这个角度入手。</p><p>以下文章结合了一些 case&#xff0c…

Spring Cloud简介,为什么需要Spring Cloud?

一、为什么需要Spring Cloud&#xff1f; 从分布式/微服务的角度而言&#xff0c;就是把我们一个大的项目分解成多个小的模块&#xff0c;这些小的模块组合起来&#xff0c;完成功能&#xff1b;而拆分出多个模块以后&#xff0c;就会出现各种各样的问题&#xff0c;而Spring C…

论文浅尝 | 「知识图谱」领域近期值得读的 6 篇顶会论文

本文转载自公众号&#xff1a;PaperWeekly。CIKM 2017■ 论文 | Hike: A Hybrid Human-Machine Method for Entity Alignmentin Large-Scale Knowledge Bases■ 链接 | https://www.paperweekly.site/papers/1528■ 解读 | 罗丹&#xff0c;浙江大学硕士1. Motivation 随着语义…

NLP最佳入门与提升路线

一只小狐狸带你解锁NLP/ML/DL秘籍作者&#xff1a;夕小瑶&#xff0c;小鹿鹿鹿&#xff0c;QvQ前言对突如其来的长假感到惶恐和不安&#xff1f;紧盯2019-nCoV的最新消息却依然感觉很空虚&#xff1f;腰酸萎靡脖子僵甚至怀疑自己有点发烧&#xff1f;这是长时间没学习的症状。 …

揭秘任务型对话机器人(下篇)

本文来自专栏语言、知识与人工智能&#xff0c;作者腾讯知文实验室 近年来比较热门的任务型对话的研究主要集中在端到端的框架的研究&#xff0c;基本跟传统任务型算法框架——语言理解模块&#xff08;Spoken Language Understanding&#xff0c;对话管理模块(Dialogue Manage…

斯坦福大学NLP公开课CS224n上映啦!华人助教陪你追剧

一只小狐狸带你解锁NLP/DL/ML秘籍作者&#xff1a;小鹿鹿鹿&#xff0c;QvQ&#xff0c;夕小瑶CS224n: Natural Language Processing with Deep LearningStanford / Winter 2020开课啦&#xff01;&#xff01;在大量开源工具的席卷下&#xff0c;NLP领域也不可避免的受到了“调…

论文浅尝 | Improved Neural Relation Detection for KBQA

Yu M, Yin W, Hasan K S, etal. Improved Neural Relation Detection for Knowledge Base QuestionAnswering[J]. 2017.论文链接&#xff1a;https://arxiv.org/pdf/1704.06194.pdf概要本文提出了一种基于不同粒度对关系和问题进行文本匹配的关系检测的模型&#xff08;HR-BiLS…

多轮对话之对话管理(Dialog Management)

多轮对话之对话管理(Dialog Management)徐阿衡人工智能与机器学习工程师​关注她115 人赞同了该文章开始涉猎多轮对话&#xff0c;这一篇想写一写对话管理&#xff08;Dialog Management&#xff09;&#xff0c;感觉是个很庞大的工程&#xff0c;涉及的知识又多又杂&#xff0…

互联网公司常用架构模式梳理

一、管理和监控 1.1、大使模式&#xff1a;创建代表消费者服务或应用程序发送网络请求的帮助服务 进程外的代理服务&#xff08;很多框架层面的事情可以以软件框架的形式寄宿在进程内&#xff0c;也可以以独立的代理形式做一个网络中间件&#xff09;。这里的大使模式意思就是…

知识工场 | CN-DBpedia 漫游指南

本文转载自公众号&#xff1a;知识工场。CN-DBpedia于2015年12月上线以来&#xff0c;已经有两年多的时间了&#xff0c;在数据层面做了大大小小百余项完善&#xff0c;在CN-DBpedia 3.0 即将上线之际&#xff0c;尽管我们觉得离完美还有距离&#xff0c;但也是时候拿出来让大家…