netty-eventloop

DiscardServer

 1 package io.netty.example.discard;
 2     
 3 import io.netty.bootstrap.ServerBootstrap;
 4 
 5 import io.netty.channel.ChannelFuture;
 6 import io.netty.channel.ChannelInitializer;
 7 import io.netty.channel.ChannelOption;
 8 import io.netty.channel.EventLoopGroup;
 9 import io.netty.channel.nio.NioEventLoopGroup;
10 import io.netty.channel.socket.SocketChannel;
11 import io.netty.channel.socket.nio.NioServerSocketChannel;
12     
13 /**
14  * Discards any incoming data.
15  */
16 public class DiscardServer {
17     
18     private int port;
19     
20     public DiscardServer(int port) {
21         this.port = port;
22     }
23     
24     public void run() throws Exception {
25         EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
26         EventLoopGroup workerGroup = new NioEventLoopGroup();
27         try {
28             ServerBootstrap b = new ServerBootstrap(); // (2)
29             b.group(bossGroup, workerGroup)
30              .channel(NioServerSocketChannel.class) // (3)
31              .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
32                  @Override
33                  public void initChannel(SocketChannel ch) throws Exception {
34                      ch.pipeline().addLast(new DiscardServerHandler());
35                  }
36              })
37              .option(ChannelOption.SO_BACKLOG, 128)          // (5)
38              .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)
39     
40             // Bind and start to accept incoming connections.
41             ChannelFuture f = b.bind(port).sync(); // (7)
42     
43             // Wait until the server socket is closed.
44             // In this example, this does not happen, but you can do that to gracefully
45             // shut down your server.
46             f.channel().closeFuture().sync();
47         } finally {
48             workerGroup.shutdownGracefully();
49             bossGroup.shutdownGracefully();
50         }
51     }
52     
53     public static void main(String[] args) throws Exception {
54         int port;
55         if (args.length > 0) {
56             port = Integer.parseInt(args[0]);
57         } else {
58             port = 8080;
59         }
60         new DiscardServer(port).run();
61     }
62 }
View Code

 

构造Bootstrap时,有一个很重要的元素就是eventLoopGroup(分为bossGroup和workerGroup,bossGroup用来accept,workerGroup用来处理socket IO),eventLoopGroup继承了eventExecutorGroup,eventExecutorGroup可以认为是eventExecutor的容器,可以通过next()获取到eventExecutor。由下面的类图可知eventExecutorGroup继承了executorService,其具有executorService的特性,比如执行提交的任务(通过next()委托给具体的eventExecutor)并获得代表任务执行进度的Future、关闭执行器(shutdown)等。

eventLoopGroup在eventExecutorGroup的基础上提供了register方法,即可以将channel注册在eventLoop上(eventLoopGroup的register内部仍然会调用next(),将channel注册到具体的eventLoop上)。所谓注册就是将eventLoop与channel绑定,对于nio来说,一个eventLoop(NioEventLoop)会绑定多个channel(还会将channel注册在与eventLoop绑定的selector上),对于oio来说,一个eventLoop(ThreadPerChannelEventLoop)只能绑定一个channel。

 下面是EventLoop的类图,EventLoop继承了EventLoopGroup,EventLoop额外提供了inEventLoop()方法来判断当前线程是否在EventLoop中。

我们知道ExecutorService屏蔽了任务如何执行的具体机制,但通常Executor都会持有一个线程池来执行任务,并通过一个阻塞队列来缓存待执行的任务,那EventLoopGroup是怎样的呢?

 

这里以Nio为例,serverSocketChannel在accept后会将socketChannel注册在EventLoop上,并在EventLoop上execute完成注册操作

//childGroup即在ServerBootstrap中配置的childGroup(EventLoopGroup)
childGroup.register(child)

 

NioEventLoop的execute方法

 1     public void execute(Runnable task) {
 2         if (task == null) {
 3             throw new NullPointerException("task");
 4         }
 5 
 6         boolean inEventLoop = inEventLoop();
 7         if (inEventLoop) {
 8             addTask(task);
 9         } else {
10             //startThread()会调用executor的execute方法,这个executor就是一个java.util.concurrent.Executor
11             startThread();
12             addTask(task);
13             if (isShutdown() && removeTask(task)) {
14                 reject();
15             }
16         }
17 
18         if (!addTaskWakesUp && wakesUpForTask(task)) {
19             wakeup(inEventLoop);
20         }
21     }

 

EventLoop的execute最终会调用EventLoop中executor的execute,这个executor是哪儿来的呢?

 1 public class NioEventLoopGroup extends MultithreadEventLoopGroup {
 2     //......
 3     
 4     //threadFactory会被包装为一个executor,
 5     //该executor的execute方法就是threadFactory.newThread(command).start();
 6     //threadFactory的newThread一般都会新建thread,因为thread实例只能start一次。
 7     public NioEventLoopGroup(int nThreads, ThreadFactory threadFactory) {
 8       this(nThreads, threadFactory, SelectorProvider.provider());
 9     }
10     
11     //这里传入的executor就是EventLoop中的executor,
12     //nThreads表示EventExecutor(EventLoop)的个数,这些eventExecutor会持有同一个executor,
13     //Group会持有这些eventExecutor,即children,但children还未启动。
14     //children = new EventExecutor[nThreads];
15     //迭代children[i] = newChild(executor, args);
16     public NioEventLoopGroup(int nThreads, Executor executor) {
17       this(nThreads, executor, SelectorProvider.provider());
18     }
19     
20     //......
21 }

 

由上分析可知,netty中EventLoop的核心在于executor,executor提供了真正的执行能力(即线程),下面就是NioEventLoop在executor中的执行的任务(这里的run并不是Runnable的run),可以看出,所谓EventLoop就是一个循环器,该循环器不停的在处理channel的IO事件。

 1 protected void run() {
 2   for (;;) {
 3     try {
 4       switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
 5         case SelectStrategy.CONTINUE:
 6           continue;
 7         case SelectStrategy.SELECT:
 8           select(wakenUp.getAndSet(false));
 9           if (wakenUp.get()) {
10               selector.wakeup();
11           }
12         default:
13           // fallthrough
14       }
15 
16       cancelledKeys = 0;
17       needsToSelectAgain = false;
18       final int ioRatio = this.ioRatio;
19       if (ioRatio == 100) {
20         try {
21           processSelectedKeys();
22         } finally {
23           // Ensure we always run tasks.
24           runAllTasks();
25         }
26       } else {
27         final long ioStartTime = System.nanoTime();
28         try {
29             processSelectedKeys();
30         } finally {
31             // Ensure we always run tasks.
32             final long ioTime = System.nanoTime() - ioStartTime;
33             runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
34         }
35       }
36     } catch (Throwable t) {
37       handleLoopException(t);
38     }
39     // Always handle shutdown even if the loop processing threw an exception.
40     try {
41       if (isShuttingDown()) {
42         closeAll();
43         if (confirmShutdown()) {
44           return;
45         }
46       }
47     } catch (Throwable t) {
48       handleLoopException(t);
49     }
50   }
51 }
View Code

 

posted @ 2017-08-26 15:45  holoyong  阅读(504)  评论(0编辑  收藏  举报