Netty是什么
Netty是一个异步事件驱动的网络应用程序框架,用于快速开发可维护的、高性能的、可伸缩的协议服务器和客户端。Netty是完全基于NIO实现的,其内部实现非常复杂,但是它为我们提供了简单易用的API,我们不必再去编写复杂的逻辑代码实现通信,也不再需要去考虑编码问题,半包读写问题。下图展示了netty的总体架构:
安装
netty不强制依赖特定的外部环境,只需要jdk版本在1.5以上就可以,使用前,我们只需要在pom文件中添加相应版本的netty依赖。
具体的版本可以在https://bintray.com/netty/downloads/netty/中找到本文使用的版本为1.1.19,pom文件中依赖如下:
第一个netty应用
还是老套路,先演示个小demo,再讲原理。接下来,我们将写一个Print Server,即:客户端发送什么内容,服务器就在控制台打印什么内容。
首先,我们来实现一个消息处理的Handler,姑且叫做PrintServerHandler,代码如下:
在这里,我们继承了一个类ChannelInboundHandlerAdapter,可以看看它的源码:
这个类是ChannelInboundHandler的实现,ChannelInboundHandler接口定义了channel的各种事件处理方法。ChannelInboundHandlerAdapter对各个方法的默认实现就是简单触发ChannelHandlerContext中各个事件的回调方法。
在这里我们只需要简单打印内容,而不需要实现所有接口方法。因此,继承ChannelInboundHandlerAdapter实现我们所关心的方法就足够了。
channelRead():接收到消息时,就会调用此方法
exceptionCaught():当处理事件或者IO发生异常时,调用此方法
ByteBuf:netty自定义的字节缓冲数据结构,类比于Java的ByteBuffer,不同于我们平时基于HTTP的应用,netty是直接操作TCP或者UDP传输的数据包,所有的数据最终都是以字节流的方式传输,开发者需要自定义数据编码解码协议,如我们之前介绍的Protobuf就可以作为一种编码解码协议。
ReferenceCountUtil.release(msg):netty中对象的生命周期由它们的引用计数来管理,当引用计数为零时,netty会将资源归还到对象池(netty通过对象池来给对象分配内存),引用计数的对象需要自己在最后一次使用后释放,否则当引用计数对象不可达导致JVM将它们GC掉,而被GC后就不能再访问,也就不能释放引用,最终无法归还到对象池,导致内存泄露。
写完PrintServerHandler后,接下来我们就需要实现服务器代码,并将PrintServerHandler添加到Channel的处理链中。代码如下:
NioEventLoopGroup处理IO操作的事件循环,对于不同的IO类型,netty提供了不同的事件循环实现。
ServerBootstrap启动服务器的helper类。
NioServerSocketChannel指定通道类型,这个类指定NIO通道,相应的还有阻塞式IO通道类型 OioServerSocketChannel和epoll类型通道EpollServerSocketChannel等。
ChannelInitializer配置通道的类,例如这个例子中,我们通过ch.pipeline().addLast()向通道的处理链中添加Handler,我们可以为一个通道添加一系列的Handler(类似于tomcat中的阀)
option(ChannelOption.SO_BACKLOG, 128)配置NioServerSocketChannel的连接队列大小为128.
childOption(ChannelOption.SO_KEEPALIVE, true)设置子通道的连接属性,当设置该选项以后,如果在两小时内没有数据的通信时,TCP会自动发送一个活动探测数据报文.
bind()绑定端口,该方法是异步的,它会立即返回的一个Future,但是绑定不一定已经完成,此处我们绑定8090端口。
讲到这里,可能大家还是雾里看花,作者当时也是,官网文档实在是太简洁了。netty是典型的Reactor结构,为了方便理解,我们可以看看下面这种Reactor实现方式。
这种结构,将Reactor分为两个部分,mainReactor负责监听server socket,accept新连接,并将建立的socket分派给subReactor,subReactor负责多路分离已连接的socket,读写网络数据。netty NIO的默认模式就是相当于这种Reactor结构去掉线程池的变种。bossGroup相当于mainReactor,workerGroup相当于subReactor。
最后,我们在main方法里面启动这个服务器就可以了。
下面,我们来测试下服务器,作者在控制台使用telnet来测试我们Print Server。命令如下:
windows下telnet功能默认没有开启,可以通过控制面板->程序和功能->打开或关闭Windows功能->Telnet客户端 勾选上并确定即可开启。
如果控制台的输入都是横线,可以通过ctrl+]切换到人类可以理解的自然语言的控制台。
例如:我们通过sen命令发送:hello i am netty
服务器端输出如下:
是不是很简单。
好了,今天的介绍到这里就结束了,demo总是那么简单。下一期我们将深入介绍netty的原理。