线程模型
传统的线程池模型:jDK1.5之后的Executor的API,表现入下:
总结
传统的这种池化以及可重用提升了线程的创建于销毁,但是并没有排除线程上下文的切换
EventLoop 接口
当连接发生的整个生命周期,运行一个任务处理事件,是一个基础的网络框架的功能。相应的架构通常是作为一个事件循环,netty是使用EventLoop这个接口实现的,如下图:
一个EventLoop确定绑定在一个Thread上并且不会改变,一个EventLoop会被分配多个Channels
通常Events以及任务是执行在一个FIFO序列中,这个排除数据出错的可能性,并且保证任务处理的正确顺序
I/O 以及事件处理在netty4中
所有的I/O操作以及事件处理都通过线程被指派在EventLoop上
I/O 以及事件处理在netty3中
所有的入站都由EventLoop执行,但是出站的操作是由调用它的线程处理,这个导致了一个问题,可能会导致异步操作,例如同时发生了两个出站的写操作,在不同的线程
另一个消极的方面是当一个入站事件产生了出站事件,但是出站的时候发生了异常,在netty3中,你需要调用线程来处理,增加了线程切换
在netty4中,解决了这个问题,提供了简单的执行架构,并且排除了在ChannelHandler上进行同步操作
任务调度
偶尔你需要延迟(推迟)执行一个任务,或者周期性的执行,你可能需要注册一个任务例如5分钟连接一次。一个通用的用例是心跳消息对远端执行。如果没有响应,你知道将要关闭这个channel
JDK的任务调度
在JDk1.5之前,通常使用java.util.Timer,随后JDk1.5之后使用java.util.concurrent,定义了一个ScheduledExecutorService,
1 | ScheduledExecutorService executor = |
netty的任务调度使用的是EventLoop
1 | Channel ch = ... |
1 | Channel ch = ... |
实现细节
这一节主要讲解重要元素线程模型以及调度实现的细节
线程管理
事件循环与线程的分配
异步实现仅使用一点EventLoops,在单签的线程模型中,可能持有多个Channels,这个允许多个Channels可以用一个很小数量的线程,而不是每个线程都有一个channel
如下:
一旦一个Channel已经被分配到了一个EventLoop,它将会在整个生命周期中使用EventLooop响应的线程
一个EventLoop需要使用ThreadLocal来保证线程安全,因为一个EvnetLoop通常持有多各Channel
OIO