Thursday, November 19, 2015

Classes related to Netty Transport - Part2

1. 相关流程

下面的相关流程特指NioServerSocketChannel/NioSocketChannel

1.1. bind流程

  • 新建一个Channel
  • 注册到boss group,注册时触发事件
    • fireChannelRegistered
    • fireChannelActive(如果是第一次注册,例如unregister后再register没有这个事件)
  • 调用Java NIO bind到端口

Classes related to Netty Transport - Part1

1. EventLoop

EventLoop

1.1. 相关类

  • 方框内是interface,方框外是class
  • 浅蓝色是java.util.concurrent包中的executor
  • 绿色是 io.netty.util.concurrent包中的EventExecutor
  • 黄色是 io.netty.util.concurrent包中的EventLoop
Executor相关类主要负责(立即或者定时)执行Runnable/Callable;EventExecutor类增加了判断当前线程是否在EventLoop中的方法;EventLoop相关类提供接口将Channel register/unregister到一个EventLoop,另外返回一个返回ChannelHandlerInvoker用于在EventLoop触发register/active/write等行为或事件
提示
EventLoopGroup
EventLoop集合;register channel; 返回EventExecutor(next函数)
Executor
执行Runnable
ExecutorService
执行Callable; shutdown
ScheduledExecutorService
定时执行Runnable和Callable
EventExecutor
判断当前线程是否在本executor:inEventLoop
EventExecutorGroup
shudown; EventExector集合
EventLoop
返回ChannelHandlerInvoker,用于在EventLoop触发register,active, write等行为或事件

1.2. NioEventLoop相关实现

类NioEventLoop实现了EventLoop,但其具体的实现通过上层的类提供: AbstractScheduledEventExecutor和SingleThreadEventExecutor和SingleThreadEventLoop。
AbstractScheduledEventExecutor
  • 实现schedule Runnable/Callable功能;
  • 有一个scheduledTaskQueue,用PriorityQueue实现,头部是到期时间最早的task
SingleThreadEventExecutor
基于AbstractScheduledEventExecutor提供了addTask和pollTask的功能。
拥有两个queue:
  • 本身有一个taskQueue,用来保存马上需要执行的任务
  • 继承自AbstractScheduledEventExecutor的scheduledTaskQueue
函数takeTask用来获取一个需要马上执行的runnable,函数的实现如下:
  • 查看scheduledTaskQueue头,如果没有任何scheduled task,则从taskQueue中take(无限等待)。
可能会感觉这里有bug,即在无限等待过程中,新加入scheduled tastk queue的task不会被执行。事实是在向其提交ScheduledTask时,会做以下操作:
 <V> ScheduledFuture<V> schedule(final ScheduledFutureTask<V> task) {
        if (inEventLoop()) { // (1)
            scheduledTaskQueue().add(task);
        } else { // (2)
            execute(new OneTimeTask() {
                @Override
                public void run() {
                    scheduledTaskQueue().add(task);
                }
            });
        }

        return task;
    }
  • 在(1)情况下,下一个循环又会先检查scheduledTaskQueue,所以会被执行
  • 在(2)情况下,线程会先被唤醒执行OneTimeTask,下一个循环还是会去先scheduledTaskQueue
因此是没有bug的
  • 如果有scheduled task,则检查dueTime。
    • 如果已经到期,则返回。
    • 如果没有到期,从等待taskQueue,一直到dueTime。
      • 如果dueTime到了taskQueue还没有东西,则返回scheduled task。
      • 否则返回taskQueue里的东西。
boolean runAllTasks(long timeoutNanos) 会被子类用到,执行task直到timeout。
SingleThreadEventLoop
这个类没啥东西
NioEventLoop本身
依靠Java NIO(如Selector),提供了register channel的相关功能(中间需要调用Channel.register)。同时,override SingleThreadEventExecutor中的run函数,从而定义线程主循环。
主循环逻辑如下:
  • select并等待唤醒(这里逻辑复杂,看不懂)
  • 根据设置的io/task执行时间比例,按比例执select到的任务和task任务
select到的任务主要指socket的read/write,task任务主要指提交的Runnable和Callable

2. Channel

2.1. 相关类

netty channel.png

2.2. NioServerSocketChannel

在使用ServerBootStrap时,需要提供一个Channel类,用来处理server socket的read操作(即accept)。使用NIO + TCP时,一般使用NioServerSocketChannel。
很奇怪的是,该类继承AbstractNioMessageChannel,这是因为AbstractNioMessageChannel实现了read函数(确切的说是实现了AbstractNioUnsafe子类),并会要求子类实现一个readMessage。而NioServerSocketChannel.readMessage则通过调用accept返回一个NioSocketChannel对象。

2.3. AbstractChannel

  • 包含了register,write,bind等实现框架
  • 需要子类去实现doRegister等函数,例如AbstractNioChannel实现了doRegister, NioSocketChannel实现了doWrite等
netty AbstractChannel do.png

3. Pipeline

netty pipeline
  • DefaultChannelPipeLien包含了以AbstractChannelHandlerContext为元素的双线链表。该双线链表的Head和Tail都是内建不能更改的。当我们向pipeline增加ChannelHandler时(如调用addLast),实际上是创建一个AbstractChannelHandlerContext并添加到这个双向链表中。
  • event类消息,从head开始向下遍历,比如fireChannelRegistered,fireChannelActive,fireChannelRead等
  • action类消息,从tail开始遍历,比如write,read,connect,close等
  • 内建的HeadContext会处理connect,write等调用,并调用AbstractChannel中相应的函数。具体的处理细节根据action和channel的类型都会不同。
  • 内建的TailContext没什么特别处理,只对没人处理的ByteBuf做释放操作

4. AddressResolver

Netty中的AddressResolver负责将名字翻译成SocketAddress。为了提高速度(Or somthing else),Netty定义了AddressResolverGroup,每个EventExecutor关联一个Resolver
Netty有三种AddressResolverGroup实现:
  • DefaultAddressResolverGroup: 通过Java自带的InetAddress.getByName来解析
  • DnsAddressResolverGroup:Netty自己实现的一套DNS解析,有Cache功能
  • NoopAddressResolverGroup: 啥都不干
在调用BootStrap.connect()时,会在当前线程调用AddressResolver.resolve(remoteAddress),而默认的实现DefaultAddressResolverGroup这个操作是 阻塞 的!
DnsAddressResolverGroup实现没看(貌似是4.1新出的功能),不过应该不会是阻塞的(但是也会在Channel的EventGroup上执行)。构造的时候需要:
  • EventLoop
  • name server list