Explore netty observer design pattern

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 ChannelFutruetarget 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 AbstractChannelHandlerContextclasses, 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 DefaultChannelPromiseis 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 promiseis returned, DefaultChannelPromiseit is ChannelPromisethe implementation class, and ChannelPromisehas inherited ChannelFuture, which is why every time obviously writeAndFlush()return are ChannelFuturewe 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 DefaultChannelPromisesource 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 ByteBufferoperation, 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 ByteBufferbefore 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, Promiseall 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 Successsuccess

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);
}

Guess you like

Origin www.cnblogs.com/ZhuChangwu/p/11246368.html