javadoc notes point
The core idea of the observer is that at the appropriate time specified action callback function observer
We know that when you create a channel using netty, usually this channel is set to non-blocking mode, what does it mean? Io means that all operations once called immediately returned
It makes netty has dramatically enhance the throughput io, but compared with the traditional asynchronous programming serialized programming mode, the control it can be too much trouble
jdk provides Futrue native interface, which means the next task, in fact, is to encapsulate the task to execute new thread, the thread during this mission, our main thread can make time to do something else
The following example code netty given, we can see that the task thread has returned a Futrue object that encapsulates the case of task execution
* * void showSearch(final String target)
* * throws InterruptedException {
* * Future<String> future
* * = executor.submit(new Callable<String>() {
* * public String call() {
* * return searcher.search(target);
* * }});
* * displayOtherThings(); // do other things while searching
* * try {
* * displayText(future.get()); // use future
* * } catch (ExecutionException ex) { cleanup(); return; }
* * }
*
Although jdk native Futrue can submit asynchronous tasks, and returned Futrue task execution information, but there is a fatal flaw, get task implementation method from futrue, it is blocked , it is not allowed, because in the netty, a channel relationship with thousands of possible links to the client, which blocked a client led to thousands of clients that are not available are not allowed, Netty's Future is designed to inherit jdk native future, and extended as follows
// todo 这个接口继承了 java并发包总的Futrue , 并在其基础上增加了很多方法
// todo Future 表示对未来任务的封装
public interface Future<V> extends java.util.concurrent.Future<V> {
// todo 判断IO是否成功返回
boolean isSuccess();
// todo 判断是否是 cancel()方法取消
boolean isCancellable();
// todo 返回IO 操作失败的原因
Throwable cause();
/**
* todo 使用了观察者设计模式, 给这个future添加监听器, 一旦Future 完成, listenner 立即被通知
*/
Future<V> addListener(GenericFutureListener<? extends Future<? super V>> listener);
// todo 添加多个listenner
Future<V> addListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
Future<V> removeListener(GenericFutureListener<? extends Future<? super V>> listener);
// todo 移除多个 listenner
Future<V> removeListeners(GenericFutureListener<? extends Future<? super V>>... listeners);
// todo sync(同步) 等待着 future 的完成, 并且,一旦future失败了,就会抛出 future 失败的原因
// todo bind()是个异步操作,我们需要同步等待他执行成功
Future<V> sync() throws InterruptedException;
// todo 不会被中断的 sync等待
Future<V> syncUninterruptibly();
// todo 等待
Future<V> await() throws InterruptedException;
Future<V> awaitUninterruptibly();
// todo 无阻塞的返回Future对象, 如果没有,返回null
// todo 有时 future成功执行后返回值为null, 这是null就是成功的标识, 如 Runable就没有返回值, 因此文档建议还要 通过isDone() 判断一下真的完成了吗
V getNow();
@Override
boolean cancel(boolean mayInterruptIfRunning);
...
netty observer mode
The most commonly used method on asynchronous execution writeAndFlush()
is a typical observer implementation in netty, the IO operation when a beginning, a ChannelFutrue
target will be created out of this time, the object of this futrue neither success nor failure , but not canceled, because the IO operation is not over yet
If we want to perform immediately other operations after the IO operation, netty recommend we use the addListenner()
method to add listeners instead of using the await () blocking wait, use the listener, we do not have the relationship exactly when the end of the IO operation, only You need to provide a callback method can, after the end of the IO operation, the method will automatically be called back
In netty, one IO operation state is organized as follows
* +---------------------------+
* | Completed successfully |
* +---------------------------+
* +----> isDone() = true |
* +--------- -----------------+ | | isSuccess() = true |
* | Uncompleted | | +===========================+
* +--------------------------+ | | Completed with failure |
* | isDone() = false | | +---------------------------+
* | isSuccess() = false |----+----> isDone() = true |
* | isCancelled() = false | | | cause() = non-null 非空|
* | cause() = null | | +===========================+
* +--------------------------+ | | Completed by cancellation |
* | +---------------------------+
* +----> isDone() = true |
* | isCancelled() = true |
* +---------------------------+
Source Track
The use of writeAndFlush
ChannelFuture channelFuture = ctx.writeAndFlush("from client : " + UUID.randomUUID());
channelFuture.addListener(future->{
if(future.isSuccess()){
todo
}else{
todo
}
});
Note the point: We use the writeAndFlush()
program returns immediately, then we use the object returned by adding listener, add a callback, this time writeAndFlush()
there may have been done, there may not be completed, it is uncertain what
First, we know, writeAndFlush()
is an action out of the station, belonging channelOutboundHandler
, and he is from the end of the pipeline began to spread, the source code is as follows:
@Override
public final ChannelFuture writeAndFlush(Object msg) {
return tail.writeAndFlush(msg);
}
End node data AbstractChannelHandlerContext
classes, view the source code to follow up as follows:
@Override
public final ChannelFuture writeAndFlush(Object msg) {
return tail.writeAndFlush(msg);
}
@Override
public ChannelPromise newPromise() {
return new DefaultChannelPromise(channel(), executor());
}
Quietly made a very important thing, to create Promise
, this DefaultChannelPromise
is to be an observer, after a while it completed by the callback method
Continue to follow up writeAndFlush()
, as the source, we can see that promise
is returned, DefaultChannelPromise
it is ChannelPromise
the implementation class, and ChannelPromise
has inherited ChannelFuture
, which is why every time obviously writeAndFlush()
return are ChannelFuture
we here yet returnedDafaultChannelPromise
// todo 调用本类的 write(msg, true, promise)
@Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
if (msg == null) {
throw new NullPointerException("msg");
}
if (isNotValidPromise(promise, true)) {
ReferenceCountUtil.release(msg);
return promise;
}
write(msg, true, promise);
return promise;
Before going to the destination, look at addListenner()
what did we enter the DefaultChannelPromise
source code as follows:
@Override
public ChannelPromise addListener(GenericFutureListener<? extends Future<? super Void>> listener) {
super.addListener(listener);
return this;
}
随机进入它的父类 DefaultChannelPromise中
@Override
public Promise<V> addListener(GenericFutureListener<? extends Future<? super V>> listener) {
checkNotNull(listener, "listener");
synchronized (this) {
addListener0(listener);
}
if (isDone()) {
notifyListeners();
}
return this;
}
This function is performed in two steps
Step one: Why add an event listener methods need to be synchronized?
In the case of concurrent execution of multiple threads of this, the addListener0(listener);
arbitrary one thread can be used, as the only binding presence synchronization add this action, unlike the channel and EventLoop do, there is no need to use inEventloop()
to judge in which thread, direct use of synchronous
Then enteraddListener0(listener)
private void addListener0(GenericFutureListener<? extends Future<? super V>> listener) {
if (listeners == null) {
listeners = listener; // todo 第一次添加直接在这里赋值
} else if (listeners instanceof DefaultFutureListeners) {
// todo 第三次添加调用这里
((DefaultFutureListeners) listeners).add(listener);
} else {
// todo 第二次添加来这里复制, 由这个 DefaultFutureListeners 存放观察者
listeners = new DefaultFutureListeners((GenericFutureListener<?>) listeners, listener);
}
}
Step two: Why then judgeisDone()
writeAndFlush()
Is asynchronous, and before we add listeners operation has started, so after adding the listener immediately verify one, there is no success
Thinking wave:
Recalling the writeAndFlush()
calling sequence, from the tail began to spread two-wave event, the first wave of write, followed by a second wave flush , has spread to the header, into the unsafe class, he completed the data is written by the native jdk ByteBuffer
operation, it stands to reason we are listenner add callback is done in unsafe header in, it is our goal to
Any callback method is designed well in advance, as a method of pipeline in the handler callback, is achieved through the internal pipeline traversing the linked list, where the notification observer, the observer is actually a method call, and he it must be observed using the parent class and the methods to achieve the above referenced callback
Returning to our writeAndFlush()
this method, passing in the second wave of the transaction is complete, the data is actually written to the native jdk ByteBuffer
before all callback settings are only carried out a failed state, until after the issue of data security operation may be callback success
In addition, you want to operate a callback, you have to have a reference to the object being observed, so for a while I went back to see, Promise
all the way to be passed on
We enter the unsafe write()
you can see the operations related to the callback safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
, the source code is as follows
@Override
public final void write(Object msg, ChannelPromise promise) {
assertEventLoop();
ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
if (outboundBuffer == null) { // todo 缓存 写进来的 buffer
safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
ReferenceCountUtil.release(msg);
return;
}
We continue to follow up this class method safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
, source code is as follows:
protected final void safeSetFailure(ChannelPromise promise, Throwable cause) {
if (!(promise instanceof VoidChannelPromise) && !promise.tryFailure(cause)) {
logger.warn("Failed to mark a promise as failure because it's done already: {}", promise, cause);
}
}
One important way is the callback to be the observer tryFailure(cause)
, this is the type of observer is ChannelPromise
, we look at its implementation, the source code is as follows
@Override
public boolean tryFailure(Throwable cause) {
if (setFailure0(cause)) {
notifyListeners();
return true;
}
return false;
}
This method calls the classnotifyListeners()
Continue to follow up this class methodnotifyListenersNow();
Then follow the method of the present classnotifyListener0(this, (GenericFutureListener<?>) listeners);
Continue l.operationComplete(future);
finally saw the finished listener calls, in fact, the user's callback method, although it is completed, but failed
Here we go flush()
in to see the success of the notification callback procedure, method calling sequence is as follows
flush();
flush0();
doWrite(outboundBuffer);
In doWrite () method will be used to try to spin the way data is written out, data is written out, there is a logo done = true, proved successful write, followed by that of the current bloom ByteBuf removing the entry from the list, the next source
if (done) {
// todo 跟进去
in.remove();
} else {
We continue to follow up remove()
, finally we found a sign of success callback, in remove()
the bottom of the safeSuccess (promise) ;, the next step is to add a listener callback user operation is complete, and completion status is Success
success
public boolean remove() {
// todo 获取当前的 Entry
Entry e = flushedEntry;
if (e == null) {
clearNioBuffers();
return false;
}
Object msg = e.msg;
ChannelPromise promise = e.promise;
int size = e.pendingSize;
// todo 将当前的Entry进行移除
removeEntry(e);
if (!e.cancelled) {
// only release message, notify and decrement if it was not canceled before.
ReferenceCountUtil.safeRelease(msg);
safeSuccess(promise);
decrementPendingOutboundBytes(size, false, true);
}