netty

  • Netty是一个高性能的网络编程框架。Netty是一个NIO客户端服务器框架,可以快速轻松地开发协议服务器和客户端等网络应用程序。它极大地简化并简化了TCP和UDP套接字服务器等网络编程。

优点

  • 在设计方面来说:

    1)统一的API,适用于不同的协议(阻塞和非阻塞)

    2)基于灵活、可扩展的事件驱动模型(SEDA)

    3)高度可定制的线程模型

    4)可靠的无连接数据Socket支持(UDP)

  • 在性能方面:

    1)更好的吞吐量,低延迟

    2)更省资源

    3)尽量减少不必要的内存拷贝(这也是快的原因之一)

  • 安全方面:

    完整的SSL/TLS和STARTTLS的支持

  • 在易用性方面:

    1)完善的Java doc,用户指南和样例(个人觉得并不是很完善,国内的书籍也只有一两本还可以)

    2)仅依赖于JDK1.6(netty 4.x)

netty的主要组件包括:

  • TransportChannel —-对应NIO中的channel
  • EventLoop—- 对应于NIO中的while循环
  • EventLoopGroup: 多个EventLoop,就是事件循环
  • ChannelHandler和ChannelPipeline—对应于NIO中的客户逻辑实现handleRead/handleWrite(interceptor pattern)
  • ByteBuf—- 对应于NIO 中的ByteBuffer
  • Bootstrap 和 ServerBootstrap —对应NIO中的Selector、ServerSocketChannel等的创建、配置、启动等

netty的主要流程:

设置服务端ServerBootStrap启动参数

1
2
3
4
5
6
7
1)group(boss,worker):

2)channel(NioServerSocketChannel):设置通道类型

3)handler():设置NioServerSocketChannel的ChannelHandlerPipeline

4)childHandler():设置NioSocketChannel的ChannelHandlerPipeline

通过ServerBootStrap的bind方法启动服务端,bind方法会在boss中注册NioServerScoketChannel,监听客户端的连接请求

1
1)会创建一个NioServerSocketChannel实例,并将其在boss中进行注册

netty eg:

  • netty的服务端编程要从EventLoopGroup开始,我们要创建两个EventLoopGroup,一个是boss专门用来接收连接,可以理解为处理accept事件,另一个是worker,可以关注除了accept之外的其它事件,处理子任务。

代码:

  • boss线程一般设置一个线程,设置多个也只会用到一个,而且多个目前没有应用场景(包括作者据说目前也没有找到。。。),worker线程通常要根据服务器调优,如果不写默认就是cpu的两倍。接下来服务端要启动,需要创建ServerBootStrap,在这里面netty把nio的模板式的代码都给封装好了:
  • 下一步就是配置ServerBootStrap,
  • 上面第一行配置了处理事件的线程,第二行配置了channel类型,第三行childHandler表示给worker那些线程配置了一个处理器,这个就是上面NIO中说的,把处理业务的具体逻辑抽象出来,放到Handler里面
  • 可以看到server绑定端口后直接启动,下面就是如何优雅的关闭了。服务端的启动代码很简单,比NIO少了很多。

再看看channel的处理配置类:

这个就是把ServerHandler加到了channel的pipeline里面,看一下具体的ServerHandler:

  • ServerHandler集成了ChannelInboundHandlerAdapter,这是netty中的一个事件处理器,netty中的处理器分为Inbound(进站)和Outbound(出站)处理器
1
上面的重定义了三个方法,channelRead方法表示读到消息以后如何处理,上面的处理方式就是把消息打印出来,ctx.write表示把消息再发送回客户端,但是仅仅是写到缓冲区,没有发送,flush才会真正写到网络上去。channelReadComplete方法表示消息读完了的处理,writeAndFlush方法表示写入并发送消息,这里的逻辑就是所有的消息读取完毕了,在统一写回到客户端。Unpooled.EMPTY_BUFFER表示空消息,addListener(ChannelFutureListener.CLOSE)表示写完后,就关闭连接。exceptionCaught方法就是发生异常的处理。

Netty客户端编程

  • 客户度很多组件也是类似的,事件循环客户度用的也是EventLoopGroup,而且客户端只是发起连接,不存在接收,所以使用一个EventLoopGroup就够了。服务端启动用的是ServerBootstrap,客户端用的就是Bootstrap。服务端的channel是NioServerSocketChannel,客户端的就是NioSocketChannel,代码:
  • 客户端的消息处理器:
  • 继承了一个SimpleChannelInboundHandler,和服务端的差别不大,这个Handler就是在消息不要的时候,可以把内存释放掉。再看上面的三个重写的方法,channelActive表示在通道在EventLoop上面注册完成,连接上服务器后调用,发送一个问候的消息。channelRead0方法会在read消息之后被调用,方法内的操作很简单,就是打印了一下从服务端接收到的内容。

结合前面的服务端代码,客户端启动并连接到服务端后,会发送一个问候消息,服务端收到后就直接返回这个消息,这时候客户端会把服务端返回的消息打印在控制台上,然后关闭连接。这就是上面写的netty程序做的事件,虽然代码和类很陌生,但是熟悉了NIO后,流程其实很简单。

  • netty执行的主要流程:

    Client发起连接CONNECT请求,boss中的NioEventLoop不断轮循是否有新的客户端请求,如果有,ACCEPT事件触发

    ACCEPT事件触发后,worker中NioEventLoop会通过NioServerSocketChannel获取到对应的代表客户端的NioSocketChannel,并将其注册到worker中

    worker中的NioEventLoop不断检测自己管理的NioSocketChannel是否有读写事件准备好,如果有的话,调用对应的ChannelHandler进行处理

    1
    ChannelInboundHandler按照注册的先后顺序执行;ChannelOutboundHandler按照注册的先后顺序逆序执行
  • 在使用Handler的过程中,需要注意:

1、ChannelInboundHandler之间的传递,通过调用 ctx.fireChannelRead(msg) 实现;调用ctx.write(msg) 将传递到ChannelOutboundHandler。

2、ctx.write()方法执行后,需要调用flush()方法才能令它立即执行。

3、ChannelOutboundHandler 在注册的时候需要放在最后一个ChannelInboundHandler之前,否则将无法传递到ChannelOutboundHandler。