最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Netty分布式Future與Promise執(zhí)行回調相關邏輯剖析

 更新時間:2022年03月29日 15:08:50   作者:向南是個萬人迷  
這篇文章主要為大家介紹了Netty分布式Future與Promise執(zhí)行回調相關邏輯剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步

Future和Promise執(zhí)行回調

Netty中的Future, 其實類似于jdk的Future, 用于異步獲取執(zhí)行結果

Promise則相當于一個被觀察者, 其中promise對象會一直跟隨著channel的讀寫事件, 并跟蹤著事件狀態(tài), 然后執(zhí)行相應的回調

這種設計思路也就是java設計模式的觀察者模式

首先我們看一段寫在handler中的業(yè)務代碼

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ChannelFuture future = ctx.writeAndFlush("test data");
    future.addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()){
                System.out.println("寫出成功");
            }else{
                System.out.println("寫出失敗");
            }
        }
    });
}

熟悉netty的小伙伴估計對這段代碼并不陌生, 首先調用writeAndFlush方法將數(shù)據(jù)寫出, 然后返回的future進行添加Listener, 并且重寫回調函數(shù)

這里舉一個最簡單的示例, 在回調函數(shù)中判斷future的狀態(tài)成功與否, 成功的話就打印"寫出成功", 否則節(jié)打印"寫出失敗"

這里如果寫在handler中通常是NioEventLoop線程執(zhí)行的, 在future返回之后才會執(zhí)行添加listener的操作, 如果在用戶線程中writeAndFlush是異步執(zhí)行的, 在添加監(jiān)聽的時候有可能寫出操作沒有執(zhí)行完畢, 等寫出操作執(zhí)行完畢之后才會執(zhí)行回調

以上邏輯在代碼中如何體現(xiàn)的呢?我們首先跟到writeAndFlush的方法中去

這里會走到AbstractChannelHandlerContext中的writeAndFlush方法中:

public ChannelFuture writeAndFlush(Object msg) {
    return writeAndFlush(msg, newPromise());
}

這里的邏輯之前剖析過, 想必大家并不陌生

這里關注newPromise()方法, 跟進去

public ChannelPromise newPromise() {
    return new DefaultChannelPromise(channel(), executor());
}

這里直接創(chuàng)建了DefaultChannelPromise這個對象并傳入了當前channel和當前channel綁定NioEventLoop對象

在DefaultChannelPromise構造方法中, 也會將channel和NioEventLoop對象綁定在自身成員變量中

回到writeAndFlush方法繼續(xù)跟

public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
    if (msg == null) {
        throw new NullPointerException("msg");
    }
    if (!validatePromise(promise, true)) {
        ReferenceCountUtil.release(msg);
        return promise;
    }
    write(msg, true, promise);
    return promise;
}

這里的邏輯也不陌生, 注意這里最后返回了promise, 其實就是我們上一步創(chuàng)建DefaultChannelPromise對象

DefaultChannelPromise實現(xiàn)了ChannelFuture接口, 所以方法如果返回該對象可以被ChannelFuture類型接收

我們繼續(xù)跟write方法

private void write(Object msg, boolean flush, ChannelPromise promise) {
    AbstractChannelHandlerContext next = findContextOutbound();
    final Object m = pipeline.touch(msg, next);
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
        if (flush) {
            next.invokeWriteAndFlush(m, promise);
        } else {
            next.invokeWrite(m, promise);
        }
    } else {
        AbstractWriteTask task;
        if (flush) {
            task = WriteAndFlushTask.newInstance(next, m, promise);
        }  else {
            task = WriteTask.newInstance(next, m, promise);
        }
        safeExecute(executor, task, promise, m);
    }
}

這里的邏輯我們同樣不陌生, 如果nioEventLoop線程, 我們繼續(xù)調invokeWriteAndFlush方法, 如果不是nioEventLoop線程則將writeAndFlush事件封裝成task, 交給eventLoop線程異步

這里如果是異步執(zhí)行, 則到這一步之后, 我們的業(yè)務代碼中, writeAndFlush就會返回并添加監(jiān)聽, 有關添加監(jiān)聽的邏輯稍后分析

走到這里, 無論同步異步, 都會執(zhí)行到invokeWriteAndFlush方法:

private void invokeWriteAndFlush(Object msg, ChannelPromise promise) {
    if (invokeHandler()) { 
        invokeWrite0(msg, promise);
        invokeFlush0();
    } else {
        writeAndFlush(msg, promise);
    }
}

這里也是我們熟悉的邏輯, 我們看到在invokeWrite0方法中傳入了我們剛才創(chuàng)建的DefaultChannelPromise

后續(xù)邏輯想必大家都比較熟悉, 通過事件傳播, 最終會調用head節(jié)點的write方法:

public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    unsafe.write(msg, promise);
}

這里最終調用unsafe的write方法, 并傳入了promise對象

跟到AbstractUnsafe的write方法中:

public final void write(Object msg, ChannelPromise promise) {
    assertEventLoop();
    //負責緩沖寫進來的byteBuf
    ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
    if (outboundBuffer == null) {
        safeSetFailure(promise, WRITE_CLOSED_CHANNEL_EXCEPTION);
        ReferenceCountUtil.release(msg);
        return;
    }
    int size;
    try { 
        msg = filterOutboundMessage(msg);
        size = pipeline.estimatorHandle().size(msg);
        if (size < 0) {
            size = 0;
        }
    } catch (Throwable t) {
        safeSetFailure(promise, t);
        ReferenceCountUtil.release(msg);
        return;
    }
    //插入寫隊列
    outboundBuffer.addMessage(msg, size, promise);
}

這里的邏輯之前小節(jié)也剖析過, 這里我們首先關注兩個部分, 首先看在catch中safeSetFailure這步

因為是catch塊, 說明發(fā)生了異常, 寫到緩沖區(qū)不成功, safeSetFailure就是設置寫出失敗的狀態(tài)

我們跟到safeSetFailure方法中:

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

這里看if判斷, 首先我們的promise是DefaultChannelPromise, 所以!(promise instanceof VoidChannelPromise)為true

重點分析promise.tryFailure(cause), 這里是設置失敗狀態(tài), 這里會調用DefaultPromise的tryFailure方法

跟進tryFailure方法

public boolean tryFailure(Throwable cause) {
    if (setFailure0(cause)) {
        notifyListeners();
        return true;
    }
    return false;
}

再跟到setFailure0(cause)中:

private boolean setValue0(Object objResult) {
    if (RESULT_UPDATER.compareAndSet(this, null, objResult) ||
        RESULT_UPDATER.compareAndSet(this, UNCANCELLABLE, objResult)) {
        checkNotifyWaiters();
        return true;
    }
    return false;
}

這里在if塊中的cas操作, 會將參數(shù)objResult的值設置到DefaultPromise的成員變量result中, 表示當前操作為異常狀態(tài)

回到tryFailure方法:

這里關注notifyListeners()這個方法, 這個方法是執(zhí)行添加監(jiān)聽的回調函數(shù), 當writeAndFlush和addListener是異步執(zhí)行的時候, 這里有可能添加已經(jīng)添加, 所以通過這個方法可以調用添加監(jiān)聽后的回調

如果writeAndFlush和addListener是同步執(zhí)行的時候, 也就是都在NioEventLoop線程中執(zhí)行的時候, 那么走到這里addListener還沒執(zhí)行, 所以這里不能回調添加監(jiān)聽的回調函數(shù), 那么回調是什么時候執(zhí)行的呢?我們在剖析addListener步驟的時候會給大家分析

具體執(zhí)行回調我們再講解添加監(jiān)聽的時候進行剖析

以上就是記錄異常狀態(tài)的大概邏輯

回到AbstractUnsafe的write方法:

我們再關注這一步:

outboundBuffer.addMessage(msg, size, promise);

跟到addMessage方法中

public void addMessage(Object msg, int size, ChannelPromise promise) {
    Entry entry = Entry.newInstance(msg, size, total(msg), promise);
    //代碼省略
}

我們只需要關注包裝Entry的newInstance方法, 該方法傳入promise對象

跟到newInstance中:

static Entry newInstance(Object msg, int size, long total, ChannelPromise promise) {
    Entry entry = RECYCLER.get();
    entry.msg = msg;
    entry.pendingSize = size;
    entry.total = total;
    entry.promise = promise;
    return entry;
}

這里將promise設置到Entry的成員變量中了, 也就是說, 每個Entry都關聯(lián)了唯一的一個promise

我們回到AbstractChannelHandlerContext的invokeWriteAndFlush方法中:

private void invokeWriteAndFlush(Object msg, ChannelPromise promise) {
    if (invokeHandler()) { 
        invokeWrite0(msg, promise);
        invokeFlush0();
    } else {
        writeAndFlush(msg, promise);
    }
}

我們剛才分析了write操作中promise的傳遞以及狀態(tài)設置的大概過程, 我們繼續(xù)看在flush中promise的操作過程

這里invokeFlush0()并沒有傳入promise對象, 是因為我們剛才分析過, promise對象會綁定在緩沖區(qū)中entry的成員變量中, 可以通過其成員變量拿到promise對象

invokeFlush0()我們之前也分析過, 通過事件傳遞, 最終會調用HeadContext的flush方法:

public void flush(ChannelHandlerContext ctx) throws Exception {
    unsafe.flush();
}

最后跟到AbstractUnsafe的flush方法

public final void flush() {
    assertEventLoop();
    ChannelOutboundBuffer outboundBuffer = this.outboundBuffer;
    if (outboundBuffer == null) {
        return;
    }
    outboundBuffer.addFlush();
    flush0();
}

這塊邏輯之前已分析過, 繼續(xù)看flush0方法:

protected void flush0() {
    //代碼省略
    try {
        doWrite(outboundBuffer);
    } catch (Throwable t) {
        //代碼省略
    } finally {
        inFlush0 = false;
    }
}

篇幅原因我們省略大段代碼

我們繼續(xù)跟進doWrite方法:

protected void doWrite(ChannelOutboundBuffer in) throws Exception {
    int writeSpinCount = -1;
    boolean setOpWrite = false;
    for (;;) {
        Object msg = in.current();
        if (msg == null) {
            clearOpWrite();
            return;
        }
        if (msg instanceof ByteBuf) {
            //代碼省略
            boolean done = false;
            //代碼省略
            if (done) {
                //移除當前對象
                in.remove();
            } else {
                break;
            }
        } else if (msg instanceof FileRegion) {
            //代碼省略
        } else {
            throw new Error();
        }
    }
    incompleteWrite(setOpWrite);
}

這里也省略了大段代碼, 我們重點關注in.remove()這里, 之前介紹過, 如果done為true, 說明刷新事件已完成, 則移除當前entry節(jié)點

我們跟到remove()方法中

public boolean remove() {
    Entry e = flushedEntry;
    if (e == null) {
        clearNioBuffers();
        return false;
    }
    Object msg = e.msg;
    ChannelPromise promise = e.promise;
    int size = e.pendingSize;
    removeEntry(e);
    if (!e.cancelled) {
        ReferenceCountUtil.safeRelease(msg);
        safeSuccess(promise);
        decrementPendingOutboundBytes(size, false, true);
    }
    e.recycle();
    return true;
}

這里我們看這一步:

ChannelPromise promise = e.promise;

之前我們剖析promise對象會綁定在entry中, 而這步就是從entry中獲取promise對象

等remove操作完成, 會執(zhí)行到這一步:

safeSuccess(promise);

這一步正好和我們剛才分析的safeSetFailure相反, 這里是設置成功狀態(tài)

跟到safeSuccess方法中:

private static void safeSuccess(ChannelPromise promise) {
    if (!(promise instanceof VoidChannelPromise)) {
        PromiseNotificationUtil.trySuccess(promise, null, logger);
    }
}

再跟到trySuccess方法中

public static &lt;V&gt; void trySuccess(Promise&lt;? super V&gt; p, V result, InternalLogger logger) {
    if (!p.trySuccess(result) &amp;&amp; logger != null) {
        //代碼省略
    }
}

這里再繼續(xù)跟if中的trySuccess方法, 最后會走到DefaultPromise的trySuccess方法:

public boolean trySuccess(V result) {
    if (setSuccess0(result)) {
        notifyListeners();
        return true;
    }
    return false;
}

這里跟到setSuccess0方法中:

private boolean setSuccess0(V result) {
    return setValue0(result == null ? SUCCESS : result);
}

這里的邏輯我們剛才剖析過了, 這里參數(shù)傳入一個信號SUCCESS, 表示設置成功狀

再繼續(xù)跟setValue方法:

private boolean setValue0(Object objResult) {
    if (RESULT_UPDATER.compareAndSet(this, null, objResult) ||
        RESULT_UPDATER.compareAndSet(this, UNCANCELLABLE, objResult)) {
        checkNotifyWaiters();
        return true;
    }
    return false;
}

同樣, 在if判斷中, 通過cas操作將參數(shù)傳入的SUCCESS對象賦值到DefaultPromise的屬性result中, 我們看這個屬性:

 private volatile Object result; 

這里是Object類型, 也就是可以賦值成任何類型

SUCCESS是一個Signal類型的對象, 這里我們可以簡單理解成一種狀態(tài), SUCCESS表示一種成功的狀態(tài)

通過上述cas操作, result的值將賦值成SUCCESS

我們回到trySuccess方法:

public boolean trySuccess(V result) {
    if (setSuccess0(result)) {
        notifyListeners();
        return true;
    }
    return false;
}

設置完成功狀態(tài)之后, 則會通過notifyListeners()執(zhí)行監(jiān)聽中的回調

我們看用戶代碼

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    ChannelFuture future = ctx.writeAndFlush("test data");
    future.addListener(new ChannelFutureListener() {
        @Override
        public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()){
                System.out.println("寫出成功");
            }else{
                System.out.println("寫出失敗");
            }
        }
    });
}

在回調中會判斷future.isSuccess(), promise設置為成功狀態(tài)這里會返回true, 從而打印寫出成功"

跟到isSuccess方法中, 這里會調用DefaultPromise的isSuccess方法:

public boolean isSuccess() {
    Object result = this.result;
    return result != null &amp;&amp; result != UNCANCELLABLE &amp;&amp; !(result instanceof CauseHolder);
}

我們看到首先會拿到result對象, 然后判斷result不為空, 并且不是UNCANCELLABLE, 并且不屬于CauseHolder對象

我們剛才分析如果promise設置為成功裝載, 則result為SUCCESS, 所以這里條件成立, 可以執(zhí)行 if (future.isSuccess()) 中if塊的邏輯

和設置錯誤狀態(tài)的邏輯一樣, 這里也有同樣的問題, 如果writeAndFlush是和addListener是異步操作, 那么執(zhí)行到回調的時候, 可能addListener已經(jīng)添加完成, 所以可以正常的執(zhí)行回調

那么如果writeAndFlush是和addListener是同步操作, writeAndFlush在執(zhí)行回調的時候, addListener并沒有執(zhí)行, 所以無法執(zhí)行回調方法, 那么回調方法是如何執(zhí)行的呢, 我們看addListener這個方法:

addListener傳入ChannelFutureListener對象, 并重寫了operationComplete方法, 也就是執(zhí)行回調的方法

這里會執(zhí)行到DefaultChannelPromise的addListener方法, 跟進去

public ChannelPromise addListener(GenericFutureListener&lt;? extends Future&lt;? super Void&gt;&gt; listener) {
    super.addListener(listener);
    return this;
}

跟到父類的addListener中:

public Promise&lt;V&gt; addListener(GenericFutureListener&lt;? extends Future&lt;? super V&gt;&gt; listener) {
    checkNotNull(listener, "listener");
    synchronized (this) {
        addListener0(listener);
    }
    if (isDone()) {
        notifyListeners();
    }
    return this;
}

這里通過addListener0方法添加listener, 因為添加listener有可能會在不同的線程中操作, 比如用戶線程和NioEventLoop線程, 為了防止并發(fā)問題, 這里簡單粗暴的加了個synchronized關鍵字

跟到addListener0方法中

private void addListener0(GenericFutureListener&lt;? extends Future&lt;? super V&gt;&gt; listener) {
    if (listeners == null) {
        listeners = listener;
    } else if (listeners instanceof DefaultFutureListeners) {
        ((DefaultFutureListeners) listeners).add(listener);
    } else {
        listeners = new DefaultFutureListeners((GenericFutureListener&lt;? extends Future&lt;V&gt;&gt;) listeners, listener);
    }
}

如果是第一次添加listener, 則成員變量listeners為null, 這樣就把參數(shù)傳入的GenericFutureListener賦值到成員變量listeners

如果是第二次添加listener, listeners不為空, 會走到else if判斷, 因為第一次添加的listener是GenericFutureListener類型, 并不是DefaultFutureListeners類型, 所以else if判斷返回false, 進入到else塊中

else塊中, 通過new的方式創(chuàng)建一個DefaultFutureListeners對象并賦值到成員變量listeners中

DefaultFutureListeners的構造方法中, 第一個參數(shù)傳入DefaultPromise中的成員變量listeners, 也就是第一次添加的GenericFutureListener對象, 第二個參數(shù)為第二次添加的GenericFutureListener對象, 這里通過兩個GenericFutureListener對象包裝成一個DefaultFutureListeners對象

我們看listeners的定義:

private Object listeners;

這里是個Object類型, 所以可以保存任何類型的對象

再看DefaultFutureListeners的構造方法:

DefaultFutureListeners(
        GenericFutureListener&lt;? extends Future&lt;?&gt;&gt; first, GenericFutureListener&lt;? extends Future&lt;?&gt;&gt; second) {
    listeners = new GenericFutureListener[2];
    //第0個
    listeners[0] = first;
    //第1個
    listeners[1] = second;
    size = 2;
    //代碼省略
}

在DefaultFutureListeners類中也定義了一個成員變量listeners, 類型為GenericFutureListener數(shù)組

構造方法中初始化listeners這個數(shù)組, 并且數(shù)組中第一個值賦值為我們第一次添加的GenericFutureListener, 第二個賦值為我們第二次添加的GenericFutureListener

回到addListener0方法中

private void addListener0(GenericFutureListener&lt;? extends Future&lt;? super V&gt;&gt; listener) {
    if (listeners == null) {
        listeners = listener;
    } else if (listeners instanceof DefaultFutureListeners) {
        ((DefaultFutureListeners) listeners).add(listener);
    } else {
        listeners = new DefaultFutureListeners((GenericFutureListener&lt;? extends Future&lt;V&gt;&gt;) listeners, listener);
    }
}

經(jīng)過兩次添加listener, 屬性listeners的值就變成了DefaultFutureListeners類型的對象, 如果第三次添加listener, 則會走到else if塊中, DefaultFutureListeners對象通過調用add方法繼續(xù)添加listener

跟到add方法中:

public void add(GenericFutureListener&lt;? extends Future&lt;?&gt;&gt; l) {
    GenericFutureListener&lt;? extends Future&lt;?&gt;&gt;[] listeners = this.listeners;
    final int size = this.size;
    if (size == listeners.length) {
        this.listeners = listeners = Arrays.copyOf(listeners, size &lt;&lt; 1);
    }
    listeners[size] = l;
    this.size = size + 1;
    //代碼省略
}

這里的邏輯也比較簡單, 就是為當前的數(shù)組對象listeners中追加新的GenericFutureListener對象, 如果listeners容量不足則進行擴容操作

根據(jù)以上邏輯, 就完成了listener的添加邏輯

那么再看我們剛才遺留的問題, 如果writeAndFlush和addListener是同步進行的, writeAndFlush執(zhí)行回調時還沒有addListener還沒有執(zhí)行回調, 那么回調是如何執(zhí)行的呢?

回到DefaultPromise的addListener中:

public Promise&lt;V&gt; addListener(GenericFutureListener&lt;? extends Future&lt;? super V&gt;&gt; listener) {
    checkNotNull(listener, "listener");
    synchronized (this) {
        addListener0(listener);
    }
    if (isDone()) {
        notifyListeners();
    }
    return this;
}

我們分析完了addListener0方法, 再往下看

這個會有if判斷isDone(), isDone方法, 就是程序執(zhí)行到這一步的時候, 判斷刷新事件是否執(zhí)行完成

跟到isDone方法中

public boolean isDone() {
    return isDone0(result);
}

繼續(xù)跟isDone0, 這里傳入了成員變量result

private static boolean isDone0(Object result) {
    return result != null &amp;&amp; result != UNCANCELLABLE;
}

這里判斷result不為null并且不為UNCANCELLABLE, 則就表示完成

因為成功的狀態(tài)是SUCCESS, 所以flush成功這里會返回true

回到 addListener中:

如果執(zhí)行完成, 就通過notifyListeners()方法執(zhí)行回調, 這也解釋剛才的問題, 在同步操作中, writeAndFlush在執(zhí)行回調時并沒有添加listener, 所以添加listener的時候會判斷writeAndFlush的執(zhí)行狀態(tài), 如果狀態(tài)時完成, 則會這里執(zhí)行回調

同樣, 在異步操作中, 走到這里writeAndFlush可能還沒完成, 所以這里不會執(zhí)行回調, 由writeAndFlush執(zhí)行回調

所以, 無論writeAndFlush和addListener誰先完成, 都可以執(zhí)行到回調方法

跟到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();
        }
    });
}

這里首先判斷是否是eventLoop線程, 如果是eventLoop線程則執(zhí)行if塊中的邏輯, 如果不是eventLoop線程, 則把執(zhí)行回調的邏輯封裝成task丟到EventLoop的任務隊列中異步執(zhí)行

我們重點關注notifyListenersNow()方法, 跟進去:

private void notifyListenersNow() {
    Object listeners;
    synchronized (this) {
        if (notifyingListeners || this.listeners == null) {
            return;
        }
        notifyingListeners = true;
        listeners = this.listeners;
        this.listeners = null;
    }
    for (;;) {
        if (listeners instanceof DefaultFutureListeners) {
            notifyListeners0((DefaultFutureListeners) listeners);
        } else {
            notifyListener0(this, (GenericFutureListener&lt;? extends Future&lt;V&gt;&gt;) listeners);
        }
        //代碼省略
    }
}

在無限for循環(huán)中, 首先首先判斷l(xiāng)isteners是不是DefaultFutureListeners類型, 根據(jù)我們之前的邏輯, 如果只添加了一個listener, 則listeners是GenericFutureListener類型

通常在添加的時候只會添加一個listener, 所以我們跟到else塊中的notifyListener0方法:

private static void notifyListener0(Future future, GenericFutureListener l) {
    try {
        l.operationComplete(future);
    } catch (Throwable t) {
        logger.warn("An exception was thrown by " + l.getClass().getName() + ".operationComplete()", t);
    }
}

我們看到, 這里執(zhí)行了GenericFutureListener的中我們重寫的回調函數(shù)operationComplete

以上就是執(zhí)行回調的相關邏輯

章節(jié)小結

        這一章講解了有關write和flush的相關邏輯, 并分析了有關添加監(jiān)聽和異步寫數(shù)據(jù)的相關步驟

        經(jīng)過學習, 同學們應該掌握如下知識:

        write操作是如何將ByteBuf添加到發(fā)送緩沖區(qū)的

        flush操作是如何將ByteBuf寫出到chanel中的

        抽象編碼器MessageToByteEncoder中如何定義了編碼器的骨架邏輯

        writeAndFlush和addListener在同步和異步操作中是如何執(zhí)行回調的

更多關于Netty分布式Future和Promise執(zhí)行回調的資料請關注腳本之家其它相關文章!

相關文章

  • Java 同步鎖(synchronized)詳解及實例

    Java 同步鎖(synchronized)詳解及實例

    這篇文章主要介紹了Java 同步鎖(synchronized)詳解及實例的相關資料,需要的朋友可以參考下
    2017-03-03
  • SpringBoot實現(xiàn)項目文件上傳的方法詳解

    SpringBoot實現(xiàn)項目文件上傳的方法詳解

    這篇文章主要為大家詳細介紹了SpringBoot中實現(xiàn)項目文件上傳的相關資料,文中的示例代碼講解詳細,具有一定的借鑒價值,感興趣的小伙伴可以了解一下
    2022-11-11
  • Java線程池的幾種實現(xiàn)方法和區(qū)別介紹實例詳解

    Java線程池的幾種實現(xiàn)方法和區(qū)別介紹實例詳解

    本篇文章主要介紹了Java線程池的幾種實現(xiàn)方法和區(qū)別,需要的朋友可以參考
    2017-04-04
  • 詳解maven依賴沖突以及解決方法

    詳解maven依賴沖突以及解決方法

    這篇文章主要介紹了maven依賴沖突以及解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-06-06
  • Cookie在Java中的使用

    Cookie在Java中的使用

    Cookie又稱“小甜餅”,類型為“小型文本文件”,指某些網(wǎng)站為了辨別用戶身份而儲存在用戶本地終端(Client Side)上的數(shù)據(jù)(通常經(jīng)過加密)。由用戶客戶端計算機暫時或永久保存的信息。本文將講解Cookie在Java中的使用,感興趣的朋友可以了解下
    2021-05-05
  • Java設計模式開發(fā)中使用觀察者模式的實例教程

    Java設計模式開發(fā)中使用觀察者模式的實例教程

    這篇文章主要介紹了Java設計模式開發(fā)中使用觀察者模式的實例教程,松耦合和邏輯清晰的消息監(jiān)聽是觀察者模式的大特色,需要的朋友可以參考下
    2016-04-04
  • SpringBoot+logback默認日志的配置和使用方式

    SpringBoot+logback默認日志的配置和使用方式

    這篇文章主要介紹了SpringBoot+logback默認日志的配置和使用方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-05-05
  • SpringBoot程序打包失敗(.jar中沒有主清單屬性)

    SpringBoot程序打包失敗(.jar中沒有主清單屬性)

    在學習SpringBoot,打包SpringBoot程序后,在cmd運行出現(xiàn)了 某某某.jar中沒有注清單屬性,本文就來介紹一下原因以及解決方法,感興趣的可以了解一下
    2023-06-06
  • MybatisPlus之時間處理問題

    MybatisPlus之時間處理問題

    在數(shù)據(jù)庫設計時,阿里巴巴編碼規(guī)約建議使用gmt_create和gmt_modified命名時間字段,并設置為datetime類型,本文介紹了兩種自動填充時間字段的實現(xiàn)方式:SQL級別和代碼級別(使用MyBatis?Plus),SQL級別通過設置默認值和更新值為CURRENT_TIMESTAMP
    2024-09-09
  • 詳解Spring boot Admin 使用eureka監(jiān)控服務

    詳解Spring boot Admin 使用eureka監(jiān)控服務

    本篇文章主要介紹了詳解Spring boot Admin 使用eureka監(jiān)控服務,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12

最新評論

三穗县| 洛扎县| 天长市| 蒙山县| 高邮市| 温州市| 沽源县| 星座| 宁阳县| 体育| 合水县| 定远县| 磐安县| 吉林省| 海兴县| 华池县| 宾川县| 边坝县| 肇东市| 神木县| 长顺县| 海盐县| 台中市| 延川县| 云龙县| 伊吾县| 盐边县| 锡林浩特市| 洛隆县| 长宁区| 溆浦县| 太和县| 墨竹工卡县| 井研县| 石柱| 嘉祥县| 绍兴市| 东明县| 新绛县| 晋江市| 遂平县|