- Netty 介绍:
Netty 是基于 Java NIO 的网络应用框架,
Netty 是一个 NIO client-server(客户端服务器)框架,使用 Netty 可以快速开发网络应用,例如服务器和客户端协议。Netty 供了一种新的方式来使开发网络应用程序,这种新的方式使得它很容易使用和有很强的扩展性。
Netty 的内部实现时很复杂的,但是 Netty 供了简单易用的 api 从网络处理代码中解耦业务逻辑。Netty 是完全基于 NIO 实现的,所以整个 Netty 都是异步的。
2.Netty 优点:
Netty作为业界最流行的nio框架之一,它的健壮性、功能、性能、可定制性、可扩展性都是首屈一指的,有点
总结如下:
1,API使用简单,开发门槛低。
2,功能强大,预置了多种编解码功能,支持多种主流协议。
3,定制能力强,通过channelHandler对通信框架进行灵活扩展。
4,性能高。
5,成熟,稳定,修复了所有的jdk nio bug。
6,社区活跃。
7,经历了大规模的商业应用考验,质量得到验证。
3.关于BIO和NIO的简单理解:
BIO:同步阻塞式IO,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
NIO:同步非阻塞式IO,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
AIO(NIO.2):异步非阻塞式IO,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。
阻塞IO的通信方式
非阻塞IO的通信方式
从这两图可以看出,NIO的单线程能处理连接的数量比BIO要高出很多,而为什么单线程能处理更多的连接呢?原因就是图二中出现的Selector。
当一个连接建立之后,他有两个步骤要做,第一步是接收完客户端发过来的全部数据,第二步是服务端处理完请求业务之后返回response给客户端。NIO和BIO的区别主要是在第一步。
在BIO中,等待客户端发数据这个过程是阻塞的,这样就造成了一个线程只能处理一个请求的情况,而机器能支持的最大线程数是有限的,这就是为什么BIO不能支持高并发的原因。
而NIO中,当一个Socket建立好之后,Thread并不会阻塞去接受这个Socket,而是将这个请求交给Selector,Selector会不断的去遍历所有的Socket,一旦有一个Socket建立完成,他会通知Thread,然后Thread处理完数据再返回给客户端——这个过程是阻塞的,这样就能让一个Thread处理更多的请求了。
NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
BIO与NIO一个比较重要的不同,是我们使用BIO的时候往往会引入多线程,每个连接一个单独的线程;而NIO则是使用单线程或者只使用少量的多线程,每个连接共用一个线程。
下面两张图是基于BIO的处理流程和netty的处理流程,辅助你理解两种方式的差别:
BIO的处理流程
NIO的处理流程
各自应用场景
(1)NIO适合处理连接数目特别多,但是连接比较短(轻操作)的场景,比如聊天服务器,并发局限于应用中,编程比较复杂,Jetty,ZooKeeper等都是基于java nio实现。
(2)BIO方式适用于连接数目比较小且固定的场景,这种方式对服务器资源要求比较高,并发局限于应用中。
(3)AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂
4.netty创建启动服务器:
通过创建 ServerBootstrap 对象来启动服务器,然后配置这个对象的相关选项,如端口、线程模式、事件循环,并且添加逻辑处理程序用来处理业务逻
启动服务器应先创建一个 ServerBootstrap 对象,因为使用 NIO,所以指定 NioEventLoopGroup 来接受和处理新连接,指定通道类型为 NioServerSocketChannel,设置InetSocketAddress 让服务器监听某个端口已等待客户端连接。
接下来,调用 childHandler 放来指定连接后调用的 ChannelHandler,这个方法传 ChannelInitializer 类型的参数,ChannelInitializer 是个抽象类,所以需要实现 initChannel 方法,这个方法就是用来设置 ChannelHandler。最后绑定服务器等待直到绑定完成,调用 sync()方法会阻塞直到服务器完成绑定,然后服务器等待通道关闭,因为使用 sync(),所以关闭操作也会被阻塞。现在你可以关闭 EventLoopGroup 和释放所有资源,包括创建的线程。
• 创建 ServerBootstrap 实例来引导绑定和启动服务器
• 创建 NioEventLoopGroup 对象来处理事件,如接受新连接、接收数据、写数据等等
• 指定 InetSocketAddress,服务器监听此端口
• 设置 childHandler 执行所有的连接请求
• 都设置完毕了,最后调用 ServerBootstrap.bind() 方法来绑定服务器
4.1实现服务器业务逻辑
Netty 使用 futures 和回调概念,它的设计允许你处理不同的事件类型,你的 channel handler 必须继承 ChannelInboundHandlerAdapter 并且重写 channelRead 方法,这个方法在任何时候都会被调用来接收数据,
Netty 使用多个 Channel Handler 来达到对事件处理的分离,因为可以很容的添加、更新、删除业务逻辑处理 handler。Handler 很简单,它的每个方法都可以被重写,它的所有的方法中只有 channelRead 方法是必须要重写的。
4.2捕获异常
重写 ChannelHandler 的 exceptionCaught 方法可以捕获服务器的异常,比如客户端连接服务器后强制关闭,
服务器会抛出"客户端主机强制关闭错误",通过重写 exceptionCaught 方法就可以处理异常,比如发生异常后关闭ChannelHandlerContext。
5 编写应答程序的客户端:
创建启动一个客户端包含下面几步:
• 创建 Bootstrap 对象用来引导启动客户端
• 创建 EventLoopGroup 对象并设置到 Bootstrap 中,EventLoopGroup 可以理解为是一个线程池,这个线程池用来处理连接、接受数据、发送数据
• 创建 InetSocketAddress 并设置到 Bootstrap 中,InetSocketAddress 是指定连接的服务器地址
• 添加一个 ChannelHandler,客户端成功连接服务器后就会被执行
• 调用 Bootstrap.connect()来连接服务器
• 最后关闭 EventLoopGroup 来释放资源
5.1实现客户端的业务逻辑:
客户端的业务逻辑的实现依然很简单,更复杂的用法将在后面章节详细介绍。和编写服务器的ChannelHandler 一样,在这里将自定义一个继承 SimpleChannelInboundHandler 的 ChannelHandler 来处理业务;
通过重写父类的三个方法来处理感兴趣的事件:
• channelActive():客户端连接服务器后被调用
• channelRead0():从服务器接收到数据后调用
• exceptionCaught():发生异常时被调用