什么是Netty?
Netty是由JBOOS提供的一个java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
Netty和Tomcat有什么区别?
Netty和Tomcat最大的区别就在于通信协议,Tomcat是基于Http协议的,他实质是一个基于http协议的web容器。但是Netty可以通过编程自定义各种协议,因为Netty能够通过codec自己来编码/解码字节流,完成类似redis访问的功能,这就是netty和tomcat最大的不同。
Netty为什么高并发
Netty是基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。这里可以用两张图来了解他们之间的区别。
![](https://i-blog.csdnimg.cn/blog_migrate/d7cba55b7f22c423fd698d13c41fd4dc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cc1e1329a7f312f6938d7419fa4b5775.png)
的通信方式
从图片可以看出NIO的单线程处理的连接数量要比BIO要多的多。而为什么NIO的单线程能处理更多的连接呢?因为图二中的selector。
当一个连接建立之后,他会遵循两步,第一部是接收客户端发送过来的完全数据,第二步是服务器处理完请求业务后返回response给客户端。而NIO和BIO的区别主要在于第一步。
在BIO中,等待客户端发送数据这一步骤是阻塞的,这样就造成了一个线程只能处理一个请求,而机器能支持的最大线程量是有限的,所以这就是BIO并不支持高并发的原因。
而NIO中,当一个Socket建立好之后,Thread并不会阻塞去接受一个Socket,而是将这个请求交给selector,selector会不断去遍历所有的Socket,一旦有一个Scoket建立完成,他会通知Thread,然后Thread处理完数据后再返回给客户端——这个过程是不阻塞的,这样能让一个Thread处理多个请求。
其实给我感觉就是BIO像是买东西排队一样,你没到店员面前无法买东西,一直要站着等。而NIO像是现在的手机下单一样,我们可以很多个人一起下单,然后我们就可以去干其他事情了。
下面是两张图基于BIO的处理流程和NIO的处理流程:
![](https://i-blog.csdnimg.cn/blog_migrate/85ef6420b31610d140d4218bb7a3d2c4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4a8a6c739b861a551120c6c843e13193.png)
除了BIO和NIO之外还有很多IO模型。下面是常见的5种模型处理流程:
![](https://i-blog.csdnimg.cn/blog_migrate/772dc86e26a30b988d0e9b40ceb75c05.png)
- BIO,同步阻塞IO,阻塞整个步骤,如果连接少,他的延迟是最低的,因为一个线程只处理一个连接,适用于少连接且延迟低的场景,比如说数据库连接。
- NIO,同步非阻塞IO,阻塞业务处理但不阻塞数据接收,适用于高并发且处理简单的场景,比如聊天软件。
- 多路复用IO,他的两个步骤处理是分开的,也就是说,一个连接可能他的数据接收是线程a完成的,数据处理是线程b完成的,他比BIO能处理更多请求。
- 信号驱动IO,这种IO模型主要用在嵌入式开发。
- 异步IO,他的数据请求和数据处理都是异步的,数据请求一次返回一次,适用于长连接的业务场景。
以上摘自Linux IO模式及 select、poll、epoll详解
Netty为什么传输快?
Netty传输快其实就是依赖了NIO的一个特性——零拷贝。我们都知道java的内存有堆内存、栈内存和字符串常量池等等。其中堆内存是占用空间最大的一块,也就是java存储对象的地方。一般我们的数据如果需要从IO读取到堆内存,中间需要经过Socket缓冲区,也就是说,一个数据在读取中会被拷贝两次才能到达终点。如果数据量过大的话会造成不必要的资源浪费。
而Netty针对这种情况,使用了NIO中的零拷贝,当他需要接收数据的时候,他会在堆内存之外开辟一个新的内存,数据就是直接从IO读到这块的内存中,然后Netty会通过ByteBuf可以对数据进行直接操作,从而加快了他的传输速度。
下两图就介绍了两种拷贝方式的区别,摘自Linux 中的零拷贝技术,第 1 部分
![](https://i-blog.csdnimg.cn/blog_migrate/665a53f79731fbb9ddb03aa47213092a.png)
![](https://i-blog.csdnimg.cn/blog_migrate/38216d558364e260ae531bc858a22665.png)
传统传输需要经过Socket缓冲区而他会对数据进行备份,而零拷贝则是不经过Socket缓存区,直接将数据传输到另一个内存中,再通过ByteBuf直接操作。
Netty的一些重要概念
- Channel:数据传输流,与Channel相关的概念有以下四个。上一张图让你了解Netty中的Channel。
![](https://i-blog.csdnimg.cn/blog_migrate/d48135840b6cb95d9e805e27f511cb9e.png)
- Channel:表示一个连接,可以理解为每一个请求,就是一个Channel。
- ChannelHandler:核心处理业务就在这里,用于处理业务请求。
- ChannelHandlerContext:用于传输业务数据。
- ChannelPipeline:用于保存处理过程需要用到的ChannelHandler和ChannelHandlerContext。
- ByteBuf
ByteBuf是一个存储字节的容器,最大的特点就是使用方便。他有自己的读索引和写索引方式,方便你对整段的字节缓存进行读写。也支持get/set,方便你对每个字节进行读写,他的数据结构如下图所示:
![](https://i-blog.csdnimg.cn/blog_migrate/20e3bed3fe1ceeacf5ac7ef60d199d92.png)
他有三种使用模式:
1.Heap Buffer 堆缓冲区
堆缓冲区是ByteBuf最常用的模式,他讲数据存储在堆空间。
2.Direct Buffer 直接缓冲区
直接缓冲区是ByteBuf的另一种常见模式,他的内存分配都不在堆中,jdk1.4后引入NIO的ByteBuffer类允许JVM通过本地方法调用分配内存。
通过这样免去中间的交换内存拷贝,提升IO处理速度,直接缓存区的内容可以留在在垃圾回收扫描的堆区以外。
DirectBuffer在(-XX:MaxDirectMemorySize=xxM)大小的限制下,使用Heap之外的内存。GC无法处理,也就意味着规避了在高负载下频繁的GC过程对应用线程的中断影响。
3.Composite Buffer 复合缓冲区
复合缓冲区相当于多个不同的ByteBuf的视图,这里是Netty提供的。jdk不具备这样的功能。
除此之外,他还提供一大堆api方便你使用,在这里我就不一一列出了,具体参见ByteBuf字节缓存。
Codec
Netty中的编码/解码器,通过他你能完成字节与pojo、pojo与pojo的相互转换,从而达到自定义协议的目的。
在Netty里面中比较常用的的就是HttpRequestDecoder和HttpResponseEncoder。
写在最后
本篇文章很大参考了Netty入门教程——认识Netty,侵删。本人也是新手,在学习的路上,文中有些是自己看完之后的感觉并不一定正确,如有错误或者不足的的地方可以提出,感谢。