暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Netty源码解析(上)

IT路人乙 2020-02-18
607

在梳理实现流程之前,需要首先扫盲下Netty抽象的几类关键概念

  • Channel 一个到网络套接字的连接

  • ChannelEvent 一个I/O事件或一个关联到Channel的I/O请求

  • ChannelPipeline 一个ChannelHandler的列表,是一个ChannelHandlerContext的双向链表

  • ChannelHandler 处理或拦截ChannelEvent,发送一个ChannelEvent到下一个ChannelPipeline中的Handler

  • ChannelSink 接受或处理最终向下游的ChannelEvent

  • ChannelHandlerContext 是一个ChannelHandler能够和所属ChannelPipeline和其他ChannelHandler互动

  • ChannelFuture 一个异步I/O操作的结果

  • ChannelConfig  一组Channel的配置属性

  • WorkerPool 工作线程池

除了上面几类关键概念之外,Netty还抽象了下面几类辅助概念

  • Boss 服务器端监听客户端连接的线程

  • Worker 负责指派I/O操作的工作线程

  • RegisterTask 注册任务

  • ThreadRenamingRunnable 改变当前线程名称的线程

  • DeadLockProofWorker 启动Runable的辅助类

关键抽象

Channel

Channel是一个到网络套接字的连接,提供如下功能

  • Channel当前状态,如通道是打开的么?通道已经被连接么?

  • Channel的配置参数,如接受缓存的大小

  • 通道支持的I/O操作,如读、写、连接和绑定

  • 处理这个通道所有I/O事件的ChannelPipeine

Channel的类继承图如下所示

NioServerSocketChannel : 1) 关联sink到pipeline;2) 打开服务端socket连接通道;3) 设置服务端socket通道为非阻塞模式;4) 发送通道状态为OPEN的UpstreamChannelStateEvent。

NioAcceptedSocketChannel : 1) 设置通道连接状态为ST_CONNECTED;2) 发送通道状态为OPEN的UpstreamChannelStateEvent。

ChannelEvent

ChannelEvent代表一个I/O事件或一个关联到Channel的I/O请求。在ChannelPipeline中,一个ChannelEvent会被一系列ChannelHandler处理。每个事件要么是upstream事件,要么是downstream事件。在ChannelPipeline中,假如一个事件从第一个handler流转到最后一个handler,我们叫它为一个upstream事件,并且说"一个事件溯流而上"。假如一个事件从最后一个handler回流到第一个handler,我们叫为一个downstream时间,并且说"一个时间顺流而下"。

当你的服务器接收到一个来自客户端的消息,这个关联到接收消息的事件是一个upstream事件。当你的服务器发送消息或者回复客户端,这个关联到写请求的事件是一个downstream事件。相同的规则使用于客户端。

Upstream events

事件名称事件类型和条件备注
messageReceivedMessageEvent一个来自远端的消息对象被接收
exceptionCaughtExceptionEvent一个异常被I/O线程或ChannelHandler抛出
channelOpenChannelStateEvent(state=OPEN,value=true)一个Channel是打开的,但是没有绑定不能连接
channelClosedChannelStateEvent(state=OPEN,value=false)Channel被关闭,通道关联的所有资源都被释放
channelBoundChannelStateEvent(state=BOUND,value=SocketAddress)Channel是打开的,绑定到一个本地地址上,但是不能连接
channelUnboundChannelStateEvent(state=BOUND,value=null)当前本地地址没有被绑定
channelConnectedChannelStateEvent(state=CONNECTED,value=SocketAddress)通道是打开的,绑定到一个本地地址,被连接到一个远程地址
writeCompleteWriteCompletionEvent消息被写到远端
channelDisconnectedChannelStateEvent(state=CONNECTED,value=null)远程客户端断开连接
channelInterestChangedChannelStateEvent通道的interestOps被改变

Downstream events

事件名称事件类型和条件备注
writeMessageEvent发送一个消息到Channel
bindChannelStateEvent(state=BOUND,value=SocketAddress)绑定通道指定的本地地址
unbindChannelStateEvent(state=BOUND,value=null)解除通道本地地址的绑定
connectChannelStateEvent(state=CONNECTED,value=SocketAddress)连接通道到指定的远程地址
disconnectChannelStateEvent(state=CONNECTED,value=null)断开通道到远程地址的连接
closeChannelStateEvent(state=OPEN,value=false)关闭通道

通道事件的类继承图如下

ChannelPipeline

ChannelPipeline定义了ChannelHandler双向链表的相关接口;此外,ChannelPipeline还负责发送一个指定事件到第一个ChannelUpstreamHandler
,和最后一个ChannelDownstreamHandler
。类继承图如下

DefaultChannelPipeline是ChannelHandler双向链表的缺省实现。结点为ChannelHandler的包装结点DefaultChannelHandlerContext。DefaultChannelHandlerContext除了包装ChannelHandler之后,还维护了先前结点和后置结点,确保链表的双向性;也维护了ChannelHandler的是Upstream的handler还是Downstream的Handler的标志。

ChannelHandler

ChannelHandler负责处理通道事件,也是Netty的扩展点。类继承图如下所示

OneToOneEncoder : 转换一个写请求到另一个写请求。这个Handler常用于响应数据类型格式化。

StringEncoder : 编码请求的字符串到ChannelBuffer中。配置了之后,即可在编程中直接操作字符串。

ProtobufEncoder : 编码请求的Google Protocol Buffers消息和MessageLite到ChannelBuffer中。对于TCP/IP,典型的设置是:

ChannelPipeline pipeline = ...;
// Decoders
pipeline.addLast("frameDecoder",
new LengthFieldBasedFrameDecoder(1048576, 0, 4, 0, 4));
pipeline.addLast("protobufDecoder",
new ProtobufDecoder(MyMessage.getDefaultInstance()));
// Encoder
pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
pipeline.addLast("protobufEncoder", new ProtobufEncoder());

然后你可以使用MyMessage
替代ChannelBuffer
作为消息传输:

void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
MyMessage req = (MyMessage) e.getMessage();
MyMessage res = MyMessage.newBuilder().setText(
"Did you say '" + req.getText() + "'?").build();
ch.write(res);
}

FrameDecoder : 解码接收到的ChannelBuffer到一个有意义的帧对象。在像TCP/IP这样基于流传输的协议中,数据被封装成帧。假如你接收到了三个数据包:

+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+

由于数据包被分片的原因,服务器接收到的数据可以变成这样

+----+-------+---+---+
| AB | CDEFG | H | I |
+----+-------+---+---+

FrameDecoder
帮助拆解接收到的数据包为一个或更多的有意义的应用逻辑更容易理解的数据帧。在上面的例子中,FrameDecoder
实现能够接接收到的数据为

+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+

DelimiterBasedFrameDecoder : 使用一个或多个分隔符拆分ChannelBuffer的解码器。对于解码以NUL结尾或者以换行符结尾的数据帧,它是非常有用的。

FixedLengthFrameDecoder : 以固定字节长度拆分ChannelBuffer的解码器。例如,你接收像下面这样的数据包

+---+----+------+----+
| A | BC | DEFG | HI |
+---+----+------+----+

FixedLengthFrameDecoder(3)
会将它们解码为固定长度的三个包

+-----+-----+-----+
| ABC | DEF | GHI |
+-----+-----+-----+

LengthFieldBasedFrameDecoder : 以消息中长度域的值动态拆分ChannelBuffer的解码器。它对于那种有一个整型头域代表消息体长度或者整个消息长度的二进制消息特别有用。LengthFieldBasedFrameDecoder
有许多配置参数以至于它能够解码任何有长度域的消息。

ChannelSink

ChannelSink接收和处理终端的downstream通道事件。类继承图如下

NioServerSocketPipelineSink : 负责处理服务端出口通道事件。接收到ChannelStateEvent.BOUND事件时,将服务端套接字绑定到主机地址和端口,并启动服务端Boss线程。

NioClientSocketPipelineSink : 负责处理客户端出口通道事件。接收到ChannelState.CONNECTED事件时,启动客户端Boss线程。

ChannelHandlerContext

ChannelHandlerContext使ChannelHandler能够影响它所属的ChannelPipeline和其他handlers。一个handler能够发送一个upstream的ChannelEvent或downstream的ChannelEvent,并且动态修改他所属的ChannelPipeline。

在相同的ChannelPipeline中,你能够通过调用sendUpstream(ChannelEvent)
sendDownstream(ChannelEvent)
发送或转发一个ChannelEvent到最近的Handler。通过调用getPipeline()
方法,你能够得到Handler所属的ChannelPipeline。

ChannelHandlerContext的类集成图如下

DefaultChannelHandlerContext
类包装了ChannelHandler,并且维护了Handler是否能处理upstream事件或downstream事件的自认证关系;此外,它还维护先前结点和后继结点的引用,使其形成了ChannelPipleline的链表的结点。

ChannelFuture

ChannelFuture是一个异步Chanel I/O操作的结果。类继承如下

DefaultChannelFuture
是缺省的ChannelFuture实现。推荐使用Channels#future(Channel)
Channels#future(Channel, boolean)
来创建一个新的ChannelFuture,而不是调用构造函数。

ChannelRunnableWrapper
将ChannelFuture包装成一个线程,执行指定任务并设置执行结果。

ChannelConfig

ChannelConfig是一组Channel配置的属性的集合。在Sink中获取PipelineFactory实现系统Pipeline和用户Pipeline的切换。类继承图如下

DefaultNioSocketChannelConfig
客户端套接字连接通道的缺省配置实现。

DefaultServerSocketChannelConfig
服务端套接字连接通道的缺省配置实现。

WorkerPool

工作线程池,管理工作线程。类继承图如下

NioWorkerPool
处理NioWorker线程池的默认实现。主要负责创建NioWorker。

文章转载自IT路人乙,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论