本文主要内容:
- Netty的技术和体系结构方面的内容
- Channel、EventLoop和ChannelFuture
- ChannelHandler和ChannelPipeline
- 引导
学习Netty是因为它是基于Java NIO 的异步和事件驱动的实现,保证了高负载下应用程序性能的最大化和可伸缩性;同时它包含了一组设计模式,实现了应用程序逻辑从网络层解耦,简化了开发过程,最大限度地提高了可测试性、模块化以及代码的可重用性。
1. Channel、EventLoop和ChannelFuture
Channel:Socket;
EventLoop:控制流、多线程处理、并发;
ChannelFuture:异步通知
Channel接口
基本的I/O操作(bind、connect、read、write)都依赖于底层网络传送所提供的原语,也就是Socket,而Netty中的Channel就是简化Socket编程的复杂性。
EventLoop接口
用于处理连接的生命周期中所发生的事件。
其关系是:
- 一个EventLoopGroup包含一个或多个EventLoop;
- 一个EventLoop在它的生命周期内只和一个Thread绑定;
- 所有由EventLoop处理的I/O事件都将在它专有的Thread上被处理(消除了对同步的需求)
- 一个Channel在它的生命周期内只注册于一个EventLoop;
- 一个EventLoop可能会被分配给一个或多个Channel。
ChannelFuture
正因为Netty中所有的I/O操作都是异步的,其执行结果不会立即返回,所以Netty提供了ChannelFuture接口,其addListener()方法注册了一个ChannelFutureListener,以便在某个操作完成时(无论是否成功)都能得到通知。
2. ChannelHandler和ChannelPipeline
管理数据流以及执行应用程序处理逻辑的组件
ChannelHandler接口
充当了所有处理入站和出站数据的应用程序逻辑的容器,因为ChannelHandler
的方法是由网络事件触发的。
ChannelPipeline接口
提供了ChannelHandler链的容器,并定义了用于在该链上传播入站和出站事件流的API,当Channel被创建时,他会被自动的分配到它专属的ChannelPipeline。
//在创建Channel时向ChannelPipeline中添加一个EchoClientHandler实例
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline().addLast(new EchoClientHandler());
}
});
执行顺序由添加顺序所决定,ChannelHandler处理完后将数据传递给链中的下一个ChannelHandler,该ChanelHandler不一定会去修改数据,如果数据到达ChannelPipeline一端,也就说明处理结束。
当ChannelHandler被添加到ChannelPipeline时,它将会被分配一个ChannelHandlerContext,其代表了ChannelHandler和ChannelPipeline之间的绑定,主要用于写出站数据。
在Netty中有两种发送消息的方式,直接写到Channel中,也可以写到ChannelHandlerContext对象中,前一种方式将会导致消息从ChannelPipeline的尾端开始流动,后者将导致消息从ChannelPipeline中的下一个ChannelHandler开始流动。
编码器和解码器
因为网络数据总是一系列的字节,而我们写的代码是一些对象
所有由Netty提供的编码器/解码器适配器类都实现了ChannelInboundHandler接口或者ChannelOutboundHandler接口。
抽象类SimpleChannelInboundHandler
当我们利用一个ChannelHandler来接收解码消息,并对该数据应用业务逻辑,基本上只需要扩展基类SimpleChannelInboundHandler
@ChannelHandler.Sharable
public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {
//这里的ByteBuf应该是跟服务端代码没有关系的,根据我们自身的业务需求来定的
@Override
protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf) throws Exception {
System.out.println("Client received: "+ byteBuf.toString(CharsetUtil.UTF_8));
}
引导
Netty的引导类为应用程序的网络层配置提供了容器,有两种类型的引导:一种用于客户端,另一种用于服务器,区别如下:
区别 | BootStrap | ServerBootStrap |
---|---|---|
网络编程中的作用 | 连接到远程主机和端口 | 绑定到一个本地端口 |
EventLoopGroup的数目 | 1 | 2 |
为什么ServerBootStrap需要两个EventLoopGroup呢?
因为服务器需要两组不同的Channel,第一组将只包含一个ServerChannel,代表服务器自身的已绑定到某个本地端口的正在监听的套接字,而第二组将包含所有已创建的用来处理传入客户端连接的Channel。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!