大致走了一遍Netty启动过程,分析了一波加深印象,也学会了NIO设计思路,希望能给各位提供一些帮助,也希望大家能多多指教,后面还要更加深入学习Netty这个框架,内容很多
Netty源码分析
一、启动时所有方法依次分析:
1、new NioEventLoopGroup():
NioEventLoopGroup --> MultithreadEventLoopGroup --> MultithreadEventExecutorGroup
每个类重点
MultithreadEventLoopGroup中确定了EventLoopGroup中线程数是多少,默认是CPU * 2
MultithreadEventExecutorGroup中创建了Children数组,里面是NioEventLoop,然后对Children初始化(创建NioEventLoop)
2、new NioEventLoop():
NioEventLoop --> SingleThreadEventLoop --> SingleThreadEventExecutor
每个类重点
NioEventLoop中创建并open了Selector,并且将Selector中的SeletionKey由原来的HashSet换成了Netty自己实现的数组Set
SingleThreadEventExecutor中有一个taskQueue用来存放此线程的任务,还有一个Thread/Executor用来保存当前EventLoop的线程
2.5、new NioSocketChannel():
NioSocketChannel --> AbstractNioByteChannel --> AbstractNioChannel
每个类重点
NioSocketChannel中创建并open了java原生的socketchannel
AbstractNioChannel中将java原生的socketchannel设置为非阻塞模式,并且设置了类变量readInterestOp为SelectionKey.OP_READ
AbstractChannel中给每个channel分配了唯一id,创建了一个Unsafe实例以及创建了一个新的Pipeline(DefaultChannelPipeline)也就是说,创建了NioSocketChannel后,类中已经将NioSocketChannel和原生socketchannel相关联
3、boostrap.channel(NioSocketChannel.class) /boostrap.channel(NioServerSocketChannel.class)
往boostrap中的channelFactory注册对应SocketChannel的ReflectiveChannelFactory,ReflectiveChannelFactory中的newChannel会返回对应的实例对象
4、connect()
connect() --> doResolveAndConnect()
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
①final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
if (regFuture.isDone()) {
if (!regFuture.isSuccess()) {
return regFuture;
}
return ②doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise());
} else {
......
}
}
doResolveAndConnect()有两个重要方法
(1).initAndRegister()中重要方法
channel = channelFactory.newChannel()
此方法调用了上面注册的Factory.newChannel, 因此返回了一个NioSocketChannel
init(channel)
此方法调用了boostrap中的init方法,将在创建boostrap过程中在handler中设置的ChannelInitializer注册到channel上,此channel 为上一步新建的NioSocketChannel,此时channel中pipeline为 : head --- ChannelInitializer --- tail 然后设置channel为创建boostrap过程中设置的Option属性。
config().group().register(channel)
group为上面新建的NioEventLoopGroup,register方法从Group中选择一个NioEventLoop执行register(),最后会执行 channel.unsafe().register(this, promise); 这里的channel是上面新建的channel。在这个方法中,他会将调用register方法的 EventLoop赋值给当前channel(新建的NioSocketChannel)的EventLoop属性,完成socketchannel和eventloop的绑定。
具体方法为: AbstractChannel.this.eventLoop = eventLoop; 然后会判断此线程是否是eventLoop线程,如果不是则将注册方法register0(promise)加入到eventLoop任务队列中,因此接下来的 register0方法会在eventLoop线程中运行,发生线程切换,主线程在此处返回promise,并调用promise.cause()阻塞等待结果。
register0() --> AbstractChannel方法
执行此方法的线程的eventLoop线程,首先执行doRegister()-->AbstractNioChannel方法,此方法执行selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);即将java原生socketchannel注册到selector上(此selector来自当前eventLoop线程,在上面注册过程中已经open,并且channel上已经绑定了eventLoop,所以可以从channel中获取eventLoop,进而获取selector。
此处register的attachment参数为channel本身,于是便将java原生nio socketchannel和NioSocketChannel在Selector上关联上。
然后执行pipeline.invokeHandlerAddedIfNeeded(), 此方法会将上面注册的ChannelInitializer中添加的handler全部添加到pipeline中,并将ChannelInitializer移除。
然后执行safeSetSuccess(promise),告知主线程注册方法已经结束,可以结束阻塞拿到结果。 然后执行pipeline.fireChannelRegistered(),凡是fireChannelXXX()方法都是往channel的pipeline中激活对应事件的handler执行对应方法,像此处的fireChannelRegistered(),从head到tail每一个inboundHandler都执行channelRegistered()方法。然后执行pipeline.fireChannelActive(),原理同上
总结:initAndRegister()将java原生的niochannel注册到selector上,并对pipeline初始化
经过上面所有操作,initAndRegister()方法已经完成,此时主线程已经结束等待并获得成功结果拿到创建的channel对象,并准备好执行接下来的方法了
(2).doResolveAndConnect0():
此方法中会真正调用java底层的socketchannel的connect方法
1.doConnect()方法:
private static void doConnect(
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) {
// This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
final Channel channel = connectPromise.channel();
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (localAddress == null) {
channel.connect(remoteAddress, connectPromise);
} else {
channel.connect(remoteAddress, localAddress, connectPromise);
}
connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
}
});
}
此方法中的channelPromise是由上面新建的NioSocketChannel调用newPromise()创建,因此能拿到当前新建的channel,并从channel中获得此channel绑定的eventLoop,并且在eventLoop中添加了新的任务,此时再次线程切换来执行channel.connect()。
channel.connect()方法会在pipeline中所有handler依次执行connect方法,最后会在head(HeadContext)中执行connect方法,调用以下方法
@Override
public void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) {
unsafe.connect(remoteAddress, localAddress, promise);
}
在核心代码unsafe的connect方法中,会执行doConnect(remoteAddress, localAddress)方法,最后回到NioSocketChannel 执行doConnect()方法,此方法会执行
boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);
在此处完成原生nio socketchannel的connect方法,完成后把promise设置成功并返回
二、ChannelPipeline分析
每个Channel都会绑定一个ChannelPipeline,同时pipeline中也会绑定对应的channel。在上面已经说过创建Channel过程中会同时创建一个ChannelPipeline。因此从channel中可以获得pipeline,从pipeline中也可以获得channel
ChannelHandler 是消息的具体处理器,主要负责处理客户端/服务端接收和发送的数据。
ChannelPipeline 则是包含了一个或多个 ChannelHandler 的链表。
有关ChannelPipeline的类分析
ChannelHandler --> ChannelInboundHandler/ChannelOutboundHandler --> ChannelInboundHandlerAdapter/ChannelOutboundHandlerAdapter
ChannelHandlerContext --> AbstractChannelHandlerContext(Pipeline中实际存在的每个结点)
ChannelHandlerContext和ChannelHandler的关系,可以从DefaultChannelHandlerContext的构造方法中得到:
DefaultChannelHandlerContext(
DefaultChannelPipeline pipeline, EventExecutor executor, String name, ChannelHandler handler) {
super(pipeline, executor, name, handler.getClass());
this.handler = handler;
}
因此,可以认为ChannelHandlerContext是对ChannelHandler做了一层封装。再看AbstractChannelHandlerContext构造方法:
AbstractChannelHandlerContext(DefaultChannelPipeline pipeline, EventExecutor executor,
String name, Class<? extends ChannelHandler> handlerClass) {
this.name = ObjectUtil.checkNotNull(name, "name");
this.pipeline = pipeline;
this.executor = executor;
this.executionMask = mask(handlerClass);
// Its ordered if its driven by the EventLoop or the given Executor is an instanceof OrderedEventExecutor.
ordered = executor == null || executor instanceof OrderedEventExecutor;
}
因此每一个Context都会绑定一个executor(其实就是一个EventLoop),一个pipeline(通过此pipeline可以获得这个pipeline所在的channel,进而获得这个pipeline对应的EventLoop)
首先先从ChannelPipeline的addLast方法进行分析:
addLast(EventExecutorGroup group, ChannelHandler handler)
方法中,首先new了一个DefalutChannelHandlerContext(handler),同时从EventExecutorGroup(其实就是EventLoopGroup)中获取一个eventloop,并赋值到new出来的context的executor上, 完成EventLoopGroup和DefalutChannelHandlerContext的绑定,也就是可以选择不同的EventLoopGroup(中的一个eventloop)来执行对应的handler。最后将此ChannelHandlerContext加入到pipeline中
接下来研究DefaultChannelPipeline中的管道执行方法,选择研究fireChannelRead,其余fire方法同理(fireChannelXXX方法是使用pipeline处理对应动作的入口)
public final ChannelPipeline fireChannelRead(Object msg) {
AbstractChannelHandlerContext.invokeChannelRead(head, msg);
return this;
}
因此可以看出,fireChannelRead是先从头结点开始执行,msg为接收到的数据
查看AbstractChannelHandlerContext的类方法invokeChannelRead:
static void invokeChannelRead(final AbstractChannelHandlerContext next, Object msg) {
final Object m = next.pipeline.touch(ObjectUtil.checkNotNull(msg, "msg"), next);
//executor方法会返回此channel对应的eventLoop,说明对于head来说,它的方法必定会在自己的evenloop内执行
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeChannelRead(m);
} else {
executor.execute(new Runnable() {
@Override
public void run() {
next.invokeChannelRead(m);
}
});
}
}
注:AbstractChannelHandlerContext对象的executor方法,由此可见如果没有给结点指定对应的eventloop,那么就会用channel自己的eventloop执行
public EventExecutor executor() {
if (executor == null) {
return channel().eventLoop();
} else {
return executor;
}
}
查看AbstractChannelHandlerContext对象方法next.invokeChannelRead(m)方法
private void invokeChannelRead(Object msg) {
if (invokeHandler()) {
try {
((ChannelInboundHandler) handler()).channelRead(this, msg);
} catch (Throwable t) {
notifyHandlerException(t);
}
} else {
fireChannelRead(msg);
}
}
handler()方法会返回绑定在此DefalutChannelHandlerContext上的ChannelHandler,并调用其上的channelRead方法。选择ChannelInboundHandlerAdapter查看,其最后会调用
ctx.fireChannelRead(msg)
此处的ctx为head(ChannelHandlerContext)于是跟进去查看,此方法在AbstractChannelHandlerContext中实现,是对象方法,与Pipeline中的fireChannelRead方法不同
public ChannelHandlerContext fireChannelRead(final Object msg) {
invokeChannelRead(findContextInbound(MASK_CHANNEL_READ), msg);
return this;
}
又返回去调用类invokeChannelRead方法,findContextInbound方法是根据当前handler查找下一个对应handler的方法,因此又一次循环 进入上一个过程,直到尾结点tail的ChannelRead方法调用,由此分析完ChannelPipeline的过程。 (此处提示我们,如果想要这个msg传入下一个handler,需要调用ctx.firechannelRead)
三、Future、Promise分析
Future(Java原生) --> Future(netty) --> ChannelFuture --> ChannelPromise
Future(Java原生) --> Future(netty) --> Promise --> ChannelPromise
从上可知,无论是Future(netty)、还是Promise,都是在java原生Future的基础上增强的,以下分析DefaultPromise,其余类则基本一致。接下来从DefaultPromise的主要方法进行分析
1.sync()/await():
sync()方法里面直接调用了await(),因此分析await(),直接看代码:
@Override
public Promise<V> await() throws InterruptedException {
if (isDone()) {
return this;
}
if (Thread.interrupted()) {
throw new InterruptedException(toString());
}
checkDeadLock();
synchronized (this) {
while (!isDone()) {
incWaiters();
try {
wait();
} finally {
decWaiters();
}
}
}
return this;
}
可知,当调用了await方法,直接进入synchronized(锁住的对象为promise)后在调用Object类的wait()方法进入等待。
2.setSuccess(value)/setFailure(value) 方法
进入setValue0()方法
private boolean setValue0(Object objResult) {
if (RESULT_UPDATER.compareAndSet(this, null, objResult) ||
RESULT_UPDATER.compareAndSet(this, UNCANCELLABLE, objResult)) {
if (checkNotifyWaiters()) {
notifyListeners();
}
return true;
}
return false;
}
在checkNotifyWaiters()(方法是被synchronized修饰的)中可以看见直接调用了notifyAll(),因此调用setSuccess(value)后会唤醒调用了await()/sync()方法的线程。接下来看notifyListeners()方法
private void notifyListeners() {
EventExecutor executor = executor();
if (executor.inEventLoop()) {
final InternalThreadLocalMap threadLocals = InternalThreadLocalMap.get();
final int stackDepth = threadLocals.futureListenerStackDepth();
if (stackDepth < MAX_LISTENER_STACK_DEPTH) {
threadLocals.setFutureListenerStackDepth(stackDepth + 1);
try {
notifyListenersNow();
} finally {
threadLocals.setFutureListenerStackDepth(stackDepth);
}
return;
}
}
safeExecute(executor, new Runnable() {
@Override
public void run() {
notifyListenersNow();
}
});
}
从中可以看出,Promise会在对应的EventLoop中调用notifyListenersNow()方法,跟进去后会调用以下方法
private void notifyListeners0(DefaultFutureListeners listeners) {
GenericFutureListener<?>[] a = listeners.listeners();
int size = listeners.size();
for (int i = 0; i < size; i ++) {
notifyListener0(this, a[i]);
}
}
private static void notifyListener0(Future future, GenericFutureListener l) {
try {
l.operationComplete(future);
} catch (Throwable t) {
if (logger.isWarnEnabled()) {
logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationComplete()", t);
}
}
}
因此,可以看到在调用setSuccess后,紧接着会在当前EventLoop中执行回调函数GenericFutureListener中的operationComplete方法。这个接口是由我们自己来实现的。至此,分析完Promise的异步过程,用一个例子结束:
public static void main(String[] args) {
// 构造线程池
EventExecutor executor = new DefaultEventExecutor();
// 创建 DefaultPromise 实例
Promise promise = new DefaultPromise(executor);
// 下面给这个 promise 添加两个 listener
promise.addListener(new GenericFutureListener<Future<Integer>>() {
@Override
public void operationComplete(Future future) throws Exception {
if (future.isSuccess()) {
System.out.println("任务结束,结果:" + future.get());
} else {
System.out.println("任务失败,异常:" + future.cause());
}
}
}).addListener(new GenericFutureListener<Future<Integer>>() {
@Override
public void operationComplete(Future future) throws Exception {
System.out.println("任务结束,balabala...");
}
});
// 提交任务到线程池,五秒后执行结束,设置执行 promise 的结果
executor.submit(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
// 设置 promise 的结果
// promise.setFailure(new RuntimeException());
promise.setSuccess(123456);
}
});
// main 线程阻塞等待执行结果
try {
promise.sync();
} catch (InterruptedException e) {
}
}
最后分析一下java原生Future的使用,加深印象。主要分析FutureTask,因为他是线程池中submit默认的Future实现
FutureTask <-- RunnableFuture <-- Future、Runnable
首先从构造方法分析:
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
Callable的实现类RunnableAdapter在Excutors类中实现,构造方法如下
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
因此,Callable实际上是Runnable + result的组合,其中含有runnable任务,而FutureTask中的变量有Callable,因此也存在runnable任务
又因为FutureTask实现了Runnable(因为其实现了RunnableFuture),所以其里面含有run()方法,因此可以作为线程池execute(Runnable task)方法的参数。下面分析FutureTask的run方法:
public void run() {
if (state != NEW ||
!RUNNER.compareAndSet(this, null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
/**
在RunnableAdapter中,call方法的实现为
public T call() {
task.run();
return result;
}
因此在这里执行了在一开始submit(Runnable task, T result)传进来的 Runnable 方法,并把传进来的result返回
**/
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
接下来分析set(result)方法,set中设置了outcome属性为result, 把STATE状态CAS成了COMPLETING,并调用了finishCompletion(),此方法如下
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (WAITERS.weakCompareAndSet(this, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
可以看出和AQS中同步队列十分相似,就是找到头结点并把后一个结点unpark掉,因此我们去看get()方法:
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
get方法就是判断一下state状态是否是COMPLETING,由上可知只有调用了run方法后(也就是进入线程池执行Execute方法后)才会设置,因此会进入awaitDone方法,这部分代码十分长,但原理和AQS同步队列相似,都是用一个链表来存储线程结点,并调用LockSupport.park()让线程进入等待。
主要完成的事情如下:
private int awaitDone(boolean timed, long nanos)
该方法用了一个死循环,首先判断此FutureTask是否已经完成(状态STATE是否为COMPLETING),如果是则直接Thread.yield(), 然后判断线程 是否被打断,如果打断了就从队列中移除并直接抛异常 如果都没有,则CAS到队尾中调用LockSupport.parkNanos(this, parkNanos);阻塞parkNanos后继续重复上面动作
四、NioEventLoopGroup/NioEventLoop 分析
NioEventLoopGroup <-- MultithreadEventLoopGroup <-- MultithreadEventExecutorGroup <-- AbstractEventExecutorGroup
首先分析NioEventLoopGroup,可以认为这个类就是一个线程池在NioEventLoopGroup的构造方法中,比较值得注意的部分有:
public NioEventLoopGroup(int nThreads, Executor executor, EventExecutorChooserFactory chooserFactory,
final SelectorProvider selectorProvider,
final SelectStrategyFactory selectStrategyFactory,
final RejectedExecutionHandler rejectedExecutionHandler,
final EventLoopTaskQueueFactory taskQueueFactory) {
super(nThreads, executor, chooserFactory, selectorProvider, selectStrategyFactory,
rejectedExecutionHandler, taskQueueFactory);
}
executor默认值为空
selectorProvider默认值是由SelectorProvider.provider()生成的,即Java原生Selector
SelectStrategyFactory默认值是DefaultSelectStrategyFactory.INSTANCE
RejectedExecutionHandler默认值为RejectedExecutionHandlers.reject(),这个默认的拒绝策略是直接抛出异常
值得注意的是,以上三个参数都不是给EventLoopGroup构造时使用的,而是在接下来生成的EventLoop中使用的。接着分析MultithreadEventLoopGroup
protected MultithreadEventLoopGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, threadFactory, args);
}
比较重要的部分,设置了默认的线程池线程数,如果没有设置默认是CPU数*2。然后是MultithreadEventExecutorGroup构造方法
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
EventExecutorChooserFactory chooserFactory, Object... args) {
if (nThreads <= 0) {
throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
}
if (executor == null) {
executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
}
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
boolean success = false;
try {
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 {
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;
}
}
}
}
}
}
Executor默认值为ThreadPerTaskExecutor
EventExecutorChooserFactory默认值为DefaultEventExecutorChooserFactory.INSTANCE
关键部分为:
children = new EventExecutor[nThreads];
children[i] = newChild(executor, args);
此方法由NioEventLoopGroup实现:
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
EventLoopTaskQueueFactory queueFactory = args.length == 4 ? (EventLoopTaskQueueFactory) args[3] : null;
return new NioEventLoop(this, executor, (SelectorProvider) args[0],
((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2], queueFactory);
}
由此可见,NioEventLoopGroup就是一个线程池,而每一个NioEventLoop就是线程池中的一个线程。接下来看NioEventLoop:
NioEventLoop <-- SingleThreadEventLoop <-- SingleThreadEventExecutor <-- AbstractScheduledEventExecutor <-- AbstractEventExecutor
接下来一个一个分析
1、NioEventLoop
selectorProvider为NioEventLoopGroup中的SelectorProvider.provider() SelectStrategy为NioEventLoopGroup中的DefaultSelectStrategyFactory.INSTANCE。
这里主要做了openSelector();这个方法,让每一个EventLoop自己开了一个selector。
2、SingleThreadEventExecutor
这里有一个属性TaskQueue,是每个提交到此线程的任务的存放位置,实现为LinkedBlockingQueue,最大容量为16。
executor属性为上面的ThreadPerTaskExecutor
rejectedExecutionHandler为NioEventLoopGroup中的RejectedExecutionHandlers.reject()
到此,NioEventLoop中的线程还没有启动,此时还只是一个类 分析execute方法,这个方法第一次被执行的时候是在initAndRegister()方法中,也就是每个NioSocketChannel执行register方法的初始化的时候第一次执行
public void execute(Runnable task) {
if (task == null) {
throw new NullPointerException("task");
}
boolean inEventLoop = inEventLoop();
addTask(task);
if (!inEventLoop) {
startThread();
if (isShutdown()) {
boolean reject = false;
try {
if (removeTask(task)) {
reject = true;
}
} catch (UnsupportedOperationException e) {
// The task queue does not support removal so the best thing we can do is to just move on and
// hope we will be able to pick-up the task before its completely terminated.
// In worst case we will log on termination.
}
if (reject) {
reject();
}
}
}
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
可以看到,在execute方法中,如果执行execute方法的线程不是在EventLoop线程中,那么会进行inEventLoop()判断,并且调用startThread()方法开启线程并进入doStartThread()方法中,可以看见:
executor.execute(new Runnable() {
...
});
executor为ThreadPerTaskExecutor,查看execute方法:
@Override
public void execute(Runnable command) {
threadFactory.newThread(command).start();
}
因此可以看见,调用此方法后会创建一个新的线程,并且执行传进来的任务:具体任务为SingleThreadEventExecutor.this.run(),这个方法在NioEventLoop中实现:
protected void run() {
for (;;) {
try {
try {
//在此处会进行判断,如果TaskQueue中有任务,那么会调用selectNow()方法,否则会进入SelectStrategy.SELECT部分(调用select())
switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
case SelectStrategy.CONTINUE:
continue;
case SelectStrategy.BUSY_WAIT:
case SelectStrategy.SELECT:
select(wakenUp.getAndSet(false));
if (wakenUp.get()) {
selector.wakeup();
}
// fall through
default:
}
} catch (IOException e) {
rebuildSelector0();
handleLoopException(e);
continue;
}
cancelledKeys = 0;
needsToSelectAgain = false;
// ioRatio为IO任务和普通任务处理时间占比
final int ioRatio = this.ioRatio;
if (ioRatio == 100) {
try {
//在此处处理IO事件
processSelectedKeys();
} finally {
// 在此处处理TaskQueue中的任务
runAllTasks();
}
} else {
final long ioStartTime = System.nanoTime();
try {
processSelectedKeys();
} finally {
// Ensure we always run tasks.
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}
}
} catch (Throwable t) {
handleLoopException(t);
}
// Always handle shutdown even if the loop processing threw an exception.
try {
if (isShuttingDown()) {
closeAll();
if (confirmShutdown()) {
return;
}
}
} catch (Throwable t) {
handleLoopException(t);
}
}
}
上面的代码有三个部分需要细看:
select(wakenUp.getAndSet(false));
processSelectedKeys();
runAllTasks();
processSelectedKeys()方法中主要调用了private void processSelectedKey(SelectionKey k, AbstractNioChannel ch)方法其中k是select出来的有事件的socket,AbstractNioChannel则是从SelectedKeys中获取的attachment(上面说过,每一个java原生SocketChannel通过Selector的attachment关联着一个Netty的NioSocketChannel)
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
if (!k.isValid()) {
final EventLoop eventLoop;
try {
eventLoop = ch.eventLoop();
} catch (Throwable ignored) {
// If the channel implementation throws an exception because there is no event loop, we ignore this
// because we are only trying to determine if ch is registered to this event loop and thus has authority
// to close ch.
return;
}
// Only close ch if ch is still registered to this EventLoop. ch could have deregistered from the event loop
// and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is
// still healthy and should not be closed.
// See https://github.com/netty/netty/issues/5125
if (eventLoop != this || eventLoop == null) {
return;
}
// close the channel if the key is not valid anymore
unsafe.close(unsafe.voidPromise());
return;
}
try {
int readyOps = k.readyOps();
// We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise
// the NIO JDK channel implementation may throw a NotYetConnectedException.
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
// remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking
// See https://github.com/netty/netty/issues/924
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// Process OP_WRITE first as we may be able to write some queued buffers and so free memory.
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
// Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write
ch.unsafe().forceFlush();
}
// Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead
// to a spin loop
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
}
}
可以看出,此方法调用了每个channel自己的unsafe方法处理了对应的Socket事件,例如SelectionKey.OP_WRITE,SelectionKey.OP_READ等
runAllTasks()比较简单
protected final boolean runAllTasksFrom(Queue<Runnable> taskQueue) {
Runnable task = pollTaskFrom(taskQueue);
if (task == null) {
return false;
}
for (;;) {
safeExecute(task);
task = pollTaskFrom(taskQueue);
if (task == null) {
return true;
}
}
}
protected static void safeExecute(Runnable task) {
try {
task.run();
} catch (Throwable t) {
logger.warn("A task raised an exception. Task: {}", task, t);
}
}
由上可见,就是在此线程中依次执行taskQueue中的所有任务,到此,线程已启动,NioEventLoop已经开始运行,不断循环运行着run方法,直到关闭
现在回来说说,每个Channel在Register过程中是怎么做到切换线程执行register0的 在initAndRegister()方法中 ChannelFuture regFuture = config().group().register(channel); 这个方法拿到了NioEventLoopGroup,并调用了NioEventLoopGroup的register,实际上就是选择一个NioEventLoop并调用registerSingleThreadEventLoop类的方法
@Override
public ChannelFuture register(final ChannelPromise promise) {
ObjectUtil.checkNotNull(promise, "promise");
promise.channel().unsafe().register(this, promise);
return promise;
}
这个ChannelPromise是new DefaultChannelPromise(channel, this),最后调用了新建的channel的unsafe的register方法
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
......
AbstractChannel.this.eventLoop = eventLoop;
if (eventLoop.inEventLoop()) {
register0(promise);
} else {
try {
eventLoop.execute(new Runnable() {
@Override
public void run() {
register0(promise);
}
});
}
.....
}
.....
}
这部分是连接SingleThreadEventExecutor的execute方法
public void execute(Runnable task) {
...
boolean inEventLoop = inEventLoop();
addTask(task);
if (!inEventLoop) {
startThread();
}
...
if (!addTaskWakesUp && wakesUpForTask(task)) {
wakeup(inEventLoop);
}
}
由这段代码分析:当第一次register调用时选择了一个新建的NioEventLoop来执行execute时,会先把任务加入NioEventLoop的taskQueue中,此时因为NioEventLoop中的thread为null,在inEventLoop()方法中返回false,因此会进入startThread(),又因为thread为null,因此会创建一个线程,最后执行wakeup(inEventLoop)唤醒当前NioEventLoop的线程来执行新进来的任务。
当第一次register调用时选择了一个已经建好的的NioEventLoop来执行execute时,会先把任务加入NioEventLoop的taskQueue中,此时因为NioEventLoop中的thread已经有值并且不为主线程,在inEventLoop()方法中返回false,因此会进入startThread(),又因为thread有值,因此会跳过此方法,最后执行wakeup(inEventLoop)唤醒当前NioEventLoop的线程来执行新进来的任务
五、netty bytebuf内存池
可以带着以下问题去看Netty内存池源码:
内存池管理算法是怎么做到申请效率,怎么减少内存碎片
高负载下内存池不断扩展,如何做到内存回收
对象池是如何实现的,这个不是关键路径,可以当成黑盒处理
内存池跟对象池作为全局数据,在多线程环境下如何减少锁竞争
池化后内存的申请跟释放必然是成对出现的,那么如何做内存泄漏检测,特别是跨线程之间的申请跟释放是如何处理的。
(待更新)