任务调度
偶尔,需要调度一个任务以便稍后(延迟)执行或者周期性地执行。例如,你可能想要注册一个在客户端已经连接了5分钟之后才触发的任务。一个常见的用例,发送心跳消息到远程节点,以检查连接是否仍然还活着。如果没有响应,你便知道可以关闭该Channel了。
使用ScheduledExecutorService调度任务
|
|
|
|
使用EventLoop调度任务
Netty使用Channel的EventLoop实现任务调度解决以下问题:ScheduledExecutorService的实现具有局限性,例如,作为线程池管理的一部分,将会有额外的线程创建。如果有大量的任务被紧凑地调度,那么这将会成为一个瓶颈。
使用EventLoop调度周期性的任务和取消任务
|
|
实现细节
线程管理
Netty线程模型的卓越性能取决于对当前执行的Thread的身份的确定,也就是,确定它是否分配给当前Channel以及它的EventLoop的那一个线程。每一个EventLoop将负责一个Channel的整个生命周期内的所有事件。
如果调用线程正是支撑EventLoop的线程,那么所提交的代码块将会被直接执行。否则,EventLoop将调度该任务以便稍后执行,并将它放入到内部队列中。当EventLoop下次处理它的事件时,它会执行队列中的那儿任务/事件。这也就解释了任何的Thread是如何与Channel直接交互而无需在ChannelHandler中进行额外同步的。
每个EventLoop都有它自己的任务队列,独立于任何其他的EventLoop。下图展示了EventLoop用于调度任务的执行逻辑。
EventLoop/线程的分配
异步传输
异步传输实现只使用了少量的EventLoop(以及和它们相关联的Thread),而且在当前的线程模型中,它们可能会被多个Channel所共享。这使得可以通过尽量少的Thread的支撑大量的Channel,而不是每个Channel分配一个Thread。
线程安全:一旦一个Channel被分配给一个EventLoop,它将会在它的整个生命周期都使用这个EventLoop(以及相关联的Thread)。
阻塞传输
得到的保证,每个Channel的I/O事件都将会被一个Thread(用于支撑该Channel的EventLoop的那个Thread处理)。