Java高阶必备之Netty基础原理

Netty是Java程序员通向高阶之路必须要过的门槛之一。干了几年的Java程序员发现业务开发似乎就是在SSH的世界里摸滚打爬的时候,会开始感到迷茫,难道程序员的日子就是如此枯燥么?深入使用一下Netty,另一个世界的大门就会开始打开。枯燥的编码会渐渐变得有趣,自主思考的能力也会开始加强。

Netty是建立在Java NIO基础之上最广泛使用的高性能网络框架。了解Netty之前,必须对NIO的概念有所了解。

NIO的意思是非阻塞IO,也就是说单个线程可以同时进行多个IO操作,而不会被任何IO操作阻塞住。同一个线程即能同时Accept网络套件字,又可以同时对套件字进行读写操作,然后还可以同时处理消息。

NIO基于事件机制,所有的IO操作都能抽象成一个事件。当新连接到来时,可以从内核中拿到ServerSocket的可读事件。当连接上的消息到来时,可以从内核中拿到Socket的读事件。当Socket中的缓冲区未满的时候,可以从内核中拿到Socket的可写事件。

当NIO线程从内核中拿到一个事件Event,就会开始使用相应的事件处理器EventHandler对这个事件进行处理。如果拿到ServerSocket可读事件,就会调用ServerSocket.accept获取一个新的Socket连接,然后将这个Socket连接加入到感兴趣的描述符列表中,如果拿到Socket可读事件就会开始调用Socket.read读取套件字的消息进行处理,处理完毕将返回结果序列化成一个字节数组,当Socket可以拿到可写事件时,说明套件字缓冲区未满,就拼命的将字节数组往Socket里灌,也就是调用Socket.write进行IO的写操作。

NIO从内核中拿事件的操作使用的是Selector.select函数调用,它对应操作系统界面的IO多路复用API。在现代操作系统里mac平台上对应的是kqueue模型,linux平台对应的是epoll模型,windows平台对应的是iocp模型。Java是一个跨平台的语言,JVM底层对操作系统的具体实现进行了抽象,统一向上层提供的是Selector系列API。用户只需要使用Selector提供的通用API来处理NIO相关功能即可,而无需关系底层具的操作系统API的差别了。

Selector可以理解为一个描述符对象[SocketChannel]列表,Selector通过调用操作系统API,传递一个描述符列表参数,然后就可以拿到内核提供的与所有的描述符相关的事件[Key]列表。

上面提到的NIO线程是一个单线程,但是实际上它可以是一个线程池,线程池中的每个线程负责一部分描述符的读写操作。它也可以是两个线程池,一个线程池只用来处理ServerSocket描述符建立新连接,另一个线程池专门干Socket读写的事。

Netty提供了良好的封装,可以让我们很方便的配置线程池的功用。代码中的NioEventLoopGroup代表的就是一个线程池,池中每个线程都是一个独立的NioEventLoop,即Nio事件循环。当acceptor线程池接收到一个新连接后会将这个连接通过队列发送到读写线程池继续进行处理。线程池分开的好处是当读写线程池繁忙的时候不影响acceptor接收新连接。

NIO的读写操作也是一系列复杂的过程。当NIO读事件发生时,线程使用read操作读取到的消息可能是不完整的,剩下的部分可能还要在接下来多次读事件发生后才能读到完整的一个消息对象字节数组。也可能read操作读取到的消息包含多个消息对象,最后剩下的部分又是一个不完整的消息,这就需要在每个描述符关联对象中保存中间半包的状态。消息和消息之间又有组合关系,比如HTTP POST消息包含HTTP Header和HTTP Body两个部分,而HTTP Body又可能因为太大而分解为多个HTTP Chunks进行传输,这就要求NIO的读写消息的设计包含结构层级。写操作也不是一个简单的write操作就了事了,写操作要考虑到内核为每个套件字分配的buffer大小,如果buffer不够了,write写进去的数组是不能完全写进去的,写不进去的字节数据必须保留起来,等待下次写事件发生时,也就是内核缓冲有空闲空间了,才可以将剩下的数据发送过去。

Netty将消息的读写抽象为pipeline消息管道,结构上有点类似于计算机网络分层结构。pipeline的每一层会对应一个Handler,以上一层输出的消息结构作为输入,输出新的消息结构作为下一层的输入。pipeline对象挂接在每一个Socket链路上。

代码中我们在pipeline里定义了四层Handler,第一个是处理ReadTimeout,当一个连接长达60s没有任何消息的情况下会向下一层输出一个读超时消息。第二层是一个Redis消息解码器,将Socket中的字节流转换成Redis命令对象,第三层是一个Redis消息编码器,将Redis输出对象转称字节流,第四层是消息处理器,用来逐个处理Redis命令逻辑,这里一般就是我们复杂的业务逻辑所在地,我们会在业务逻辑里最终给Socket回馈消息输出,这个消息输出又会走一遍pipeline的每一层,直到转换成字节流写到内核socket缓冲区中才算完事。

然后我们设置一些套件字的特殊属性,比如监听队列大小、读写缓冲警戒水位大小、是否延迟发送等,然后绑定监听指定端口,服务器就可以开始永无止尽地工作了。

阅读更多相关文章,关注知乎专栏【码洞】

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,875评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,569评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,475评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,459评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,537评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,563评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,580评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,326评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,773评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,086评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,252评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,921评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,566评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,190评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,435评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,129评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,125评论 2 352

推荐阅读更多精彩内容