前置芝士:
需要知道Executor执行器的一些操作。(转载请联系)😉
文章目录
Demo
public class NettyServer {
int port;
public NettyServer(int port) {
this.port = port;
}
public void start() {
ServerBootstrap bootstrap = new ServerBootstrap();
EventLoopGroup boss = new NioEventLoopGroup(1);
EventLoopGroup work = new NioEventLoopGroup();
try {
bootstrap.group(boss, work)
.handler(new LoggingHandler(LogLevel.DEBUG))
.channel(NioServerSocketChannel.class)
.childHandler(new ChatRoomServerInitializer());
ChannelFuture f = bootstrap.bind(new InetSocketAddress(port)).sync();
System.out.println("http server started. port : " + port);
f.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
boss.shutdownGracefully();
work.shutdownGracefully();
}
}
public static void main(String[] args) {
NettyServer server = new NettyServer(8080);// 8080为启动端口
server.start();
}
}
上面是一段比较简单的Netty服务端的代码,我们主要关注:
EventLoopGroup boss = new NioEventLoopGroup(1); // 用于新连接接入的Group,初始化为1
EventLoopGroup work = new NioEventLoopGroup(); // 用于处理channel中的io事件以及任务的group
NioEventLoopGroup初始化过程
跟进到上述构造函数中,最后会来到MultithreadEventLoopGroup
类中的构造函数:
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}
传入的参数就是指定Group的大小,默认大小 DEFAULT_EVENT_LOOP_THREADS
是 Runtime.getRuntime().availableProcessors() * 2
也就是两倍的CPU数。
继续跟会来到:
# MultithreadEventExecutorGroup.java
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
// 这个executor是group所包含的executor,其将来会为其所包含的每个eventLoop创建一个线程
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
// 创建eventLoop
children[i] = newChild(executor, args);
success = true;
} catch (Exception e) {
// TODO: Think about if this is a good exception type
throw new IllegalStateException("failed to create a child event loop", e);
} finally {
// 有创建失败的eventLoop就关闭所有之前创建的
if (!success) {
for (int j = 0; j < i; j ++) {
children[j].shutdownGracefully();
}
for (int j = 0; j < i; j ++) {
EventExecutor e = children[j];
try {
while (!e.isTerminated()) {
e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
}
} catch (InterruptedException interrupted) {
// Let the caller handle the interruption.
Thread.currentThread().interrupt();
break;
}
}
}
}
}
// 创建选择器
chooser = chooserFactory.newChooser(children);
final FutureListener<Object> terminationListener = new FutureListener<Object>() {
@Override
public void operationComplete(Future<Object> future) throws Exception {
if (terminatedChildren.incrementAndGet() == children.length) {
terminationFuture.setSuccess(null);
}
}
};
for (EventExecutor e: children) {
e.terminationFuture().addListener(terminationListener);
}
Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
Collections.addAll(childrenSet, children);
readonlyChildren = Collections.unmodifiableSet(childrenSet);
}
从类名就可以知道这是多线程的线程组,这里主要完成几件事情:
new ThreadPerTaskExecutor()
[线程创建器]for(){ new Child() }
[构造NioEventLoop]chooserFactory.newChooser()
[线程选择器]
线程创建器
先看看名字,是给每个任务创建一个线程的线程创建器,其保存在NioEventGroup
中的executor
中。主要是为每一个NioEventLoop
创建一个对应的线程,1:1。
# ThreadPerTaskExecutor.java
public final class ThreadPerTaskExecutor implements Executor {
private final ThreadFactory threadFactory;
public ThreadPerTaskExecutor(ThreadFactory threadFactory) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
// 传入一个线程工厂
this.threadFactory = threadFactory;
}
@Override
public void execute(Runnable command) {
// 在执行exector的execute()方法时,调用线程工厂创建线程,并start()
threadFactory.newThread(command).start();
}
}
上述代码其实就是初始化NioEventGroup
中的executor
为一个线程工厂,通过之后调用execute()
方法为将来的NioEventLoop
创建线程来一一对应。
打住,先来看看NioEventLoop的继承关系:
可知NioEventLoop
本身就是一个单线程的EventExecutor
,因此有下面创建线程组数组
children = new EventExecutor[nThreads];
而实例化创建EventLoop
在函数newChild()
中。
构造NioEventLoop
我们跟进到构造NioEventLoop
的函数newChild()
:
# NioEventLoopGroup.java
@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}
继续跟来到NioEventLoop
的构造函数:
# NioEventLoop.java
NioEventLoop(NioEventLoopGroup parent, Executor executor, SelectorProvider selectorProvider,
SelectStrategy strategy, RejectedExecutionHandler rejectedExecutionHandler) {
// 父类单线程的构造方法,传入的参数executor是group中的executor
super(parent, executor, false, DEFAULT_MAX_PENDING_TASKS, rejectedExecutionHandler);
if (selectorProvider == null) {
throw new NullPointerException("selectorProvider");
}
if (strategy == null) {
throw new NullPointerException("selectStrategy");
}
provider = selectorProvider;
selector = openSelector(); // 创建selector事件轮询器到NioEventLoop上
selectStrategy = strategy;
}
先跟进父类的构造方法:
# SingleThreadEventLoop.java
protected SingleThreadEventLoop(EventLoopGroup parent, Execut