说起Netty,思绪一下子拉回到了2017年,当时因为Netty名声在外,所以就开始学Netty,但是说实话,当时没有什么应用场景,就是纯学不用,导致最近在用到的时候遇到了很多问题。例如:在InHandler的read()中使用while循环写,为什么写一会儿就卡死?服务端断开连接客户端如何捕获到,并发起重新连接?如何避免拆包、分包的问题?等等。
于是,在解决问题的过程中,相当于又将Netty学了一遍,也对Netty的使用有了一些新的认知。也趁着这个机会,将Netty所学所得的知识整理一下。
我在学习Netty的时候,看过不少文章,很多都是从IO、NIO开始,层层递进最后讲到Netty。因为I/O是在刚学习Java的时候,就会学到的知识,所以通过这种方式可以更好的认识和理解Netty。
首先了解什么是I/O,I/O是Input/Output的缩写,说白了就是数据的输入和输出。例如,我们在键盘敲下“阿柒”,从键盘 -> 计算机系统这就是Input、然后“阿柒”显示在屏幕上,从计算机系统 -> 屏幕这就是Output。我们聊天、打开文件等等只要涉及数据交换,都是I/O。
在Java中,提供了类将I/O来具象化。例如FileInputStream可以将文件数据读取到JVM,FileOutStream将JVM中的数据写入到文件中。
而最最常用的,像各种聊天APP,利用Socket构建服务端、客户端来进行交互,底层使用TCP协议实现网络数据交换,这种网络I/O也是I/O,也是促使NIO/Netty出现的一个重要驱动。
说起socket,大家可能很陌生。但是说起Redis、Tomcat这种具体的应用可能都很熟悉。我们在服务器启动服务,监听一个端口,例如redis的6735、tomcat的8080端口,其实这都是Socket服务端。
我们访问Redis、Tomcat时,都是作为一个socket客户端,去连接服务端。例如Jedis源码中:
也是创建一个Socket对象,然后设置IP和端口访问redis,通过这个Socket连接,可以向redis发送命令执行。我们使用的原生Socket是阻塞的,什么意思呢?
在Socket对象上,read() 用来读取网络I/O的数据,write() 用来向网络I/O写入数据,在read和write的过程中,是阻塞的。一旦调用了read(),就要一直等待对端写入数据,否则就要一直等待,这时线程就被阻塞了,相当于Thread.sleep。
同样,在调用了write()之后,如果因为sendBuf或者receiveBuf缓冲池满了,无法写入,这时线程也会陷入阻塞。
只有当sendBuf或receiveBuf空闲可以写入之后,线程才会继续执行其他操作。还是拿Jedis举例:
如图,Jedis执行get()向Redis请求数据,Jedis先通过IP和端口连接到Redis,发送get命令,然后Redis服务端返回结果数据,然后交互完毕,然后向下执行其他命令。
如果我们的主机和Redis服务器之间,网络不稳定或者Redis服务繁忙,执行1、2操作时就会很慢,整体反映出来的现象就是Java程序很慢。
所以,我们可以得出一个结论:I/O是同步阻塞的。设想一个极端情况,两台主机就像两个人一样,见了面都在等对方先说话,如果两个人见了面都不说话,也不吃饭睡觉,就一直等,必须有一个人打破这种僵局先开口说话,另一人回话才能结束这种等待。
所以,什么能打破这种僵局呢,NIO、Netty!!
本篇文章是Netty系列的第一篇文章,没有代码,只有一些基础理论掺杂着个人的一些理解,目的想层层递进,以现有的理论知识,来关联推导出NIO及Netty为什么会出现、解决了什么问题。
本篇写出了I/O的痛点,下一篇就会写NIO如何解决这种痛点。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。