Netty專題(四)-----Netty介紹、Netty組件介紹


Netty是什么?為什么要用Netty?

介紹

Netty是由JBOSS提供的一個java開源框架。 Netty提供異步的、事件驅動的網絡應用程序框架和工具, 用以快速開發高性能、高可靠性的網絡服務器和客戶端程序。

為什么要用Netty

1、雖然JAVA NIO框架提供了 多路復用IO的支持,但是並沒有提供上層“信息格式”的良好封裝。例如前兩者並沒有提供針對 Protocol Buffer、JSON這些信息格式的封裝,但是Netty框架提供了這些數據格式封裝(基於責任鏈模式的編碼和解碼功能);

2、直接使用NIO需要需要額外的技能,例如Java多線程,網絡編程;

3、要編寫一個可靠的、易維護的、高性能的NIO服務器應用。除了框架本身要兼容實現各類操作系統的實現外。更重要的是它應該還要處理很多上層特有服務,例如:客戶端的權限、還有上面提到的信息格式封裝、簡單的數據讀取,斷連重連,半包讀寫,心跳等等,這些Netty框架都提供了響應的支持。

4、JAVA NIO框架存在一個poll/epoll bug:Selector doesn’t block on Selector.select(timeout),不能block意味着CPU的使用率會變成100%(這是底層JNI的問題,上層要處理這個異常實際上也好辦)。當然這個bug只有在Linux內核上才能重現。這個問題在JDK 1.7版本中還沒有被完全解決,但是Netty已經將這個bug進行了處理。

這個Bug與操作系統機制有關系的,JDK雖然僅僅是一個兼容各個操作系統平台的軟件,但在JDK5和JDK6最初的版本中(嚴格意義上來將,JDK部分版本都是),這個問題並沒有解決,而將這個帽子拋給了操作系統方,這也就是這個bug最終一直到2013年才最終修復的原因(JDK7和JDK8之間)。

為什么不用Netty5

1. netty5 中使用了 ForkJoinPool,增加了代碼的復雜度,但是對性能的改善卻不明顯

2. 多個分支的代碼同步工作量很大

3. 作者覺得當下還不到發布一個新版本的時候

4. 在發布版本之前,還有更多問題需要調查一下,比如是否應該廢棄 exceptionCaught, 是否暴露EventExecutorChooser等等。

為什么Netty使用NIO而不是AIO?

Netty不看重Windows上的使用,在Linux系統上,AIO的底層實現仍使用EPOLL,沒有很好實現AIO,因此在性能上沒有明顯的優勢,而且被JDK封裝了一層不容易深度優化。AIO還有個缺點是接收數據需要預先分配緩存, 而不是NIO那種需要接收時才需要分配緩存, 所以對連接數量非常大但流量小的情況, 內存浪費很多。據說Linux上AIO不夠成熟,處理回調結果速度跟不上處理需求,有點像外賣員太少,顧客太多,供不應求,造成處理速度有瓶頸。

作者原話:

Not faster than NIO (epoll) on unix systems (which is true)   There is no daragram suppport   Unnecessary threading model (too much abstraction without usage)

翻譯一下:

在unix系統上不比NIO(epoll)快(這是事實)沒有數據包支持不必要的線程模型(過多的抽象而無需使用)

Hello Netty代碼示例 

1、服務端

EchoServer 

package cn.enjoyedu.ch02.echo;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

import java.net.InetSocketAddress;

public class EchoServer  {

    private final int port;

    public EchoServer(int port) {
        this.port = port;
    }

    public static void main(String[] args) throws InterruptedException {
        int port = 9999;
        EchoServer echoServer = new EchoServer(port);
        System.out.println("服務器即將啟動");
        echoServer.start();
        System.out.println("服務器關閉");
    }

    public void start() throws InterruptedException {
        final EchoServerHandler serverHandler = new EchoServerHandler();
        /*線程組*/
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            /*服務端啟動必備*/
            ServerBootstrap b = new ServerBootstrap();
            b.group(group)
                    /*指明使用NIO進行網絡通訊*/
                    .channel(NioServerSocketChannel.class)
                    /*指明服務器監聽端口*/
                    .localAddress(new InetSocketAddress(port))
                    /*接收到連接請求,新啟一個socket通信,也就是channel,每個channel
                    * 有自己的事件的handler*/
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
                            ch.pipeline().addLast(new EchoServerHandler());
                        }
                    });
            /*綁定到端口,阻塞等待直到連接完成*/
            ChannelFuture f = b.bind().sync();
            /*阻塞,直到channel關閉*/
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }
}

EchoServerHandler

package cn.enjoyedu.ch02.echo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

/**
 * 類說明:自己的業務處理
 */
/*指明我這個handler可以在多個channel之間共享,意味這個實現必須線程安全的。*/
@ChannelHandler.Sharable
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    /*** 服務端讀取到網絡數據后的處理*/
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf in = (ByteBuf)msg;/*netty實現的緩沖區*/
        System.out.println("Server Accept:"+in.toString(CharsetUtil.UTF_8));
        ctx.write(in);
    }

    /*** 服務端讀取完成網絡數據后的處理*/
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)/*flush掉所有的數據*/
                .addListener(ChannelFutureListener.CLOSE);/*當flush完成后,關閉連接*/
    }

    /*** 發生異常后的處理*/
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

2、客戶端

EchoClient

package cn.enjoyedu.ch02.echo;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;

import java.net.InetSocketAddress;

/**
 * 類說明:netty的客戶端
 */
public class EchoClient {

    private final int port;
    private final String host;

    public EchoClient(int port, String host) {
        this.port = port;
        this.host = host;
    }

    public void start() throws InterruptedException {
        /*線程組*/
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            /*客戶端啟動必備*/
            Bootstrap b = new Bootstrap();
            b.group(group)
                /*指明使用NIO進行網絡通訊*/
                .channel(NioSocketChannel.class)
                /*配置遠程服務器的地址*/
                .remoteAddress(new InetSocketAddress(host,port))
                .handler(new EchoClientHandler());
            /*連接到遠程節點,阻塞等待直到連接完成*/
            ChannelFuture f = b.connect().sync();
            /*阻塞,直到channel關閉*/
            f.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient(9999,"127.0.0.1").start();
    }
}

EchoClientHandler

package cn.enjoyedu.ch02.echo;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

public class EchoClientHandler extends SimpleChannelInboundHandler<ByteBuf> {

    /*客戶端讀取到數據后干什么*/
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg) throws Exception {
        System.out.println("Client accetp:"+msg.toString(CharsetUtil.UTF_8));
    }

    /*客戶端被通知channel活躍以后,做事*/
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //往服務器寫數據
        ctx.writeAndFlush(Unpooled.copiedBuffer("Hello,Netty",
                CharsetUtil.UTF_8));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

Netty核心組件

Channel

Channel 是Java NIO 的一個基本構造。它代表一個到實體(如一個硬件設備、一個文件、一個網絡套接字或者一個能夠執行一個或者多個不同的I/O操作的程序組件)的開放連接,如讀操作和寫操作。目前,可以把Channel 看作是傳入(入站)或者傳出(出站)數據的載體。因此,它可以被打開或者被關閉,連接或者斷開連接。

回調和Future

一個回調其實就是一個方法,一個指向已經被提供給另外一個方法的方法的引用。這使得后者可以在適當的時候調用前者。回調在廣泛的編程場景中都有應用,而且也是在操作完成后通知相關方最常見的方式之一。Netty 在內部使用了回調來處理事件;當一個回調被觸發時,相關的事件可以被一個interface-ChannelHandler 的實現處理。Future 提供了另一種在操作完成時通知應用程序的方式。這個對象可以看作是一個異步操作的結果的占位符;它將在未來的某個時刻完成,並提供對其結果的訪問。

JDK 預置了interface java.util.concurrent.Future,但是其所提供的實現,只允許手動檢查對應的操作是否已經完成,或者一直阻塞直到它完成。這是非常繁瑣的,所以Netty提供了它自己的實現——ChannelFuture,用於在執行異步操作的時候使用。

ChannelFuture提供了幾種額外的方法,這些方法使得我們能夠注冊一個或者多個ChannelFutureListener實例。監聽器的回調方法operationComplete(),將會在對應的操作完成時被調用。然后監聽器可以判斷該操作是成功地完成了還是出錯了。如果是后者,我們可以檢索產生的Throwable。簡而

言之,由ChannelFutureListener提供的通知機制消除了手動檢查對應的操作是否完成的必要。每個Netty 的出站I/O 操作都將返回一個ChannelFuture。

事件和ChannelHandler

Netty 使用不同的事件來通知我們狀態的改變或者是操作的狀態。這使得我們能夠基於已經發生的事件來觸發適當的動作。Netty事件是按照它們與入站或出站數據流的相關性進行分類的。

可能由入站數據或者相關的狀態更改而觸發的事件包括:

  • 1、連接已被激活或者連接失活;
  • 2、數據讀取;
  • 3、用戶事件;
  • 4、錯誤事件。

出站事件是未來將會觸發的某個動作的操作結果,這些動作包括:

  • 1、打開或者關閉到遠程節點的連接;
  • 2、將數據寫到或者沖刷到套接字。

每個事件都可以被分發給ChannelHandler 類中的某個用戶實現的方法。可以認為每個Channel-Handler 的實例都類似於一種為了響應特定事件而被執行的回調。Netty 提供了大量預定義的可以開箱即用的ChannelHandler 實現,包括用於各種協議(如HTTP 和SSL/TLS)的ChannelHandler。

組件詳細介紹

Channel、EventLoop 和ChannelFuture

Netty 網絡抽象的代表

Channel—Socket;

EventLoop—控制流、多線程處理、並發;

ChannelFuture—異步通知。

 

 

Channel 接口

基本的I/O 操作(bind()、connect()、read()和write())依賴於底層網絡傳輸所提供的原語。在基於Java 的網絡編程中,其基本的構造是class Socket。Netty 的Channel 接口所提供的API,被用於所有的I/O 操作。大大地降低了直接使用Socket 類的復雜性。此外,Channel 也是擁有許多預定義的、

專門化實現的廣泛類層次結構的根。

每個Channel 都將會被分配一個ChannelPipeline 和ChannelConfig。ChannelConfig 包含了該Channel 的所有配置設置,並且支持熱更新。由於Channel 是獨一無二的,所以為了保證順序將Channel 聲明為java.lang.Comparable 的一個子接口。因此,如果兩個不同的Channel 實例都返回了相同的

散列碼,那么AbstractChannel 中的compareTo()方法的實現將會拋出一個Error。

Channel 的生命周期狀態

ChannelUnregistered Channel 已經被創建,但還未注冊到EventLoop

ChannelRegistered Channel 已經被注冊到了EventLoop

ChannelActive Channel 處於活動狀態(已經連接到它的遠程節點)。它現在可以接收和發送數據了

ChannelInactive Channel 沒有連接到遠程節點

最重要Channel 的方法

eventLoop 返回分配給Channel 的EventLoop

pipeline 返回分配給Channel 的ChannelPipeline

isActive 如果Channel 是活動的,則返回true。活動的意義可能依賴於底層的傳輸。例如,一個Socket 傳輸一旦連接到了遠程節點便是活動的,而一個Datagram 傳輸一旦被打開便是活動的。

localAddress 返回本地的SokcetAddress

remoteAddress 返回遠程的SocketAddress

write 將數據寫到遠程節點。這個數據將被傳遞給ChannelPipeline,並且排隊直到它被沖刷

flush 將之前已寫的數據沖刷到底層傳輸,如一個Socket

writeAndFlush 一個簡便的方法,等同於調用write()並接着調用flush()

EventLoop和EventLoopGroup

 

EventLoop 定義了Netty 的核心抽象,用於處理連接的生命周期中所發生的事件。io.netty.util.concurrent 包構建在JDK 的java.util.concurrent 包上。,一個EventLoop 將由一個永遠都不會改變的Thread 驅動,同時任務(Runnable 或者Callable)可以直接提交給EventLoop 實現,以立即執行或者

調度執行。

根據配置和可用核心的不同,可能會創建多個EventLoop 實例用以優化資源的使用,並且單個EventLoop 可能會被指派用於服務多個Channel。

Netty的EventLoop在繼承了ScheduledExecutorService的同時,只定義了一個方法,parent()。在Netty 4 中,所有的I/O操作和事件都由已經被分配給了EventLoop的那個Thread來處理。

任務調度

偶爾,你將需要調度一個任務以便稍后(延遲)執行或者周期性地執行。例如,你可能想要注冊一個在客戶端已經連接了5 分鍾之后觸發的任務。一個常見的用例是,發送心跳消息到遠程節點,以檢查連接是否仍然還活着。如果沒有響應,你便知道可以關閉該Channel 了。

在內部,當提交任務到如果(當前)調用線程正是支撐EventLoop 的線程,那么所提交的代碼塊將會被(直接)執行。否則,EventLoop 將調度該任務以便稍后執行,並將它放入到內部隊列中。當EventLoop下次處理它的事件時,它會執行隊列中的那些任務/事件。

異步傳輸

異步傳輸實現只使用了少量的EventLoop(以及和它們相關聯的Thread),而且在當前的線程模型中,它們可能會被多個Channel 所共享。這使得可以通過盡可能少量的Thread 來支撐大量的Channel,而不是每個Channel 分配一個Thread。EventLoopGroup 負責為每個新創建的Channel 分配一個

EventLoop。在當前實現中,使用順序循環(round-robin)的方式進行分配以獲取一個均衡的分布,並且相同的EventLoop可能會被分配給多個Channel。

一旦一個Channel 被分配給一個EventLoop,它將在它的整個生命周期中都使用這個EventLoop(以及相關聯的Thread)。請牢記這一點,因為它可以使你從擔憂你的Channel-

Handler 實現中的線程安全和同步問題中解脫出來。

另外,需要注意的是,EventLoop 的分配方式對ThreadLocal 的使用的影響。因為一個EventLoop 通常會被用於支撐多個Channel,所以對於所有相關聯的Channel 來說,ThreadLocal 都將是一樣的。這使得它對於實現狀態追蹤等功能來說是個糟糕的選擇。然而,

在一些無狀態的上下文中,它仍然可以被用於在多個Channel 之間共享一些重度的或者代價昂貴的對象,甚至是事件。

ChannelFuture 接口

Netty 中所有的I/O 操作都是異步的。因為一個操作可能不會立即返回,所以我們需要一種用於在之后的某個時間點確定其結果的方法。為此,Netty 提供了ChannelFuture 接口,其addListener()方法注冊了一個ChannelFutureListener,以便在某個操作完成時(無論是否成功)得到通知。可以將

ChannelFuture 看作是將來要執行的操作的結果的占位符。它究竟什么時候被執行則可能取決於若干的因素,因此不可能准確地預測,但是可以肯定的是它將會被執行。

ChannelHandler、ChannelPipeline和ChannelHandlerContext 

ChannelHandler

從應用程序開發人員的角度來看,Netty 的主要組件是ChannelHandler,它充當了所有處理入站和出站數據的應用程序邏輯的容器。ChannelHandler 的方法是由網絡事件觸發的。事實上,ChannelHandler 可專門用於幾乎任何類型的動作,例如將數據從一種格式轉換為另外一種格式,例如各種編

解碼,或者處理轉換過程中所拋出的異常。

舉例來說,ChannelInboundHandler 是一個你將會經常實現的子接口。這種類型的ChannelHandler 接收入站事件和數據,這些數據隨后將會被你的應用程序的業務邏輯所處理。當你要給連接的客戶端發送響應時,也可以從ChannelInboundHandler 沖刷數據。你的應用程序的業務邏輯通常駐留在

一個或者多個ChannelInboundHandler 中。

這種類型的ChannelHandler 接收入站事件和數據,這些數據隨后將會被你的應用程序的業務邏輯所處理。

ChannelHandler 的生命周期

下面列出了interface ChannelHandler 定義的生命周期操作,在ChannelHandler被添加到ChannelPipeline 中或者被從ChannelPipeline 中移除時會調用這些操作。這些方法中的每一個都接受一個ChannelHandlerContext 參數。

handlerAdded 當把ChannelHandler 添加到ChannelPipeline 中時被調用

handlerRemoved 當從ChannelPipeline 中移除ChannelHandler 時被調用

exceptionCaught 當處理過程中在ChannelPipeline 中有錯誤產生時被調用

Netty 定義了下面兩個重要的ChannelHandler 子接口:

  ChannelInboundHandler——處理入站數據以及各種狀態變化;

  ChannelOutboundHandler——處理出站數據並且允許攔截所有的操作。

ChannelInboundHandler 接口

下面列出了interface ChannelInboundHandler 的生命周期方法。這些方法將會在數據被接收時或者與其對應的Channel 狀態發生改變時被調用。正如我們前面所提到的,這些方法和Channel 的生命周期密切相關。

channelRegistered 當Channel 已經注冊到它的EventLoop 並且能夠處理I/O 時被調用

channelUnregistered 當Channel 從它的EventLoop 注銷並且無法處理任何I/O 時被調用

channelActive 當Channel 處於活動狀態時被調用;Channel 已經連接/綁定並且已經就緒

channelInactive 當Channel 離開活動狀態並且不再連接它的遠程節點時被調用

channelReadComplete 當Channel上的一個讀操作完成時被調用

channelRead 當從Channel 讀取數據時被調用

ChannelWritability-Changed 當Channel 的可寫狀態發生改變時被調用。用戶可以確保寫操作不會完成得太快(以避免發生OutOfMemoryError)或者可以在Channel 變為再次可寫時恢復寫入。可以通過調用Channel 的isWritable()方法來檢測Channel 的可寫性。與可寫性相關的閾值可以通過

Channel.config().setWriteHighWaterMark()和Channel.config().setWriteLowWater-Mark()方法來設置

userEventTriggered 當ChannelnboundHandler.fireUserEventTriggered()方法被調用時被調用。

當某個ChannelInboundHandler 的實現重寫channelRead()方法時,它要負責顯式地釋放與池化的ByteBuf 實例相關的內存。Netty 為此提供了一個實用方法ReferenceCount-Util.release()。Netty 將使用WARN 級別的日志消息記錄未釋放的資源,使得可以非常簡單地在代碼中發現違規的實例。但是以這種方式管理資源可能很繁瑣。一個更加簡單的方式是使用Simple-ChannelInboundHandler,SimpleChannelInboundHandler 會自動釋放資源。

ChannelOutboundHandler 接口

出站操作和數據將由ChannelOutboundHandler 處理。它的方法將被Channel、Channel-Pipeline 以及ChannelHandlerContext 調用。

所有由ChannelOutboundHandler 本身所定義的方法:

bind(ChannelHandlerContext,SocketAddress,ChannelPromise) 當請求將Channel 綁定到本地地址時被調用

connect(ChannelHandlerContext,SocketAddress,SocketAddress,ChannelPromise) 當請求將Channel 連接到遠程節點時被調用

disconnect(ChannelHandlerContext,ChannelPromise) 當請求將Channel 從遠程節點斷開時被調用

close(ChannelHandlerContext,ChannelPromise) 當請求關閉Channel 時被調用

deregister(ChannelHandlerContext,ChannelPromise) 當請求將Channel 從它的EventLoop 注銷時被調用

read(ChannelHandlerContext) 當請求從Channel 讀取更多的數據時被調用

flush(ChannelHandlerContext) 當請求通過Channel 將入隊數據沖刷到遠程節點時被調用

write(ChannelHandlerContext,Object,ChannelPromise) 當請求通過Channel 將數據寫到遠程節點時被調用

ChannelHandler的適配器

有一些適配器類可以將編寫自定義的ChannelHandler 所需要的努力降到最低限度,因為它們提供了定義在對應接口中的所有方法的默認實現。因為你有時會忽略那些不感興趣的事件,所以Netty提供了抽象基類ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter。

你可以使用ChannelInboundHandlerAdapter 和ChannelOutboundHandlerAdapter類作為自己的ChannelHandler 的起始點。這兩個適配器分別提供了ChannelInboundHandler和ChannelOutboundHandler 的基本實現。通過擴展抽象類ChannelHandlerAdapter,它們獲得了它們共同的超接口

ChannelHandler 的方法。

ChannelHandlerAdapter 還提供了實用方法isSharable()。如果其對應的實現被標注為Sharable,那么這個方法將返回true,表示它可以被添加到多個ChannelPipeline。

ChannelPipeline

 

將圖 中的處理器(ChannelHandler)從左到右進行編號,那么第一個被入站事件看到的ChannelHandler 將是 1,而第一個被出站事件看到的ChannelHandler 將是5。

當Channel 被創建時,它會被自動地分配到它專屬的ChannelPipeline。每一個新創建的Channel 都將會被分配一個新的ChannelPipeline。這項關聯是永久性的;Channel 既不能附加另外一個ChannelPipeline,也不能分離其當前的。在Netty 組件的生命周期中,這是一項固定的操作,不需要開發

人員的任何干預。使得事件流經ChannelPipeline 是ChannelHandler 的工作,它們是在應用程序的初始化或者引導階段被安裝的。這些對象接收事件、執行它們所實現的處理邏輯,並將數據傳遞給鏈中的下一個ChannelHandler。它們的執行順序是由它們被添加的順序所決定的。入站和出站

ChannelHandler 可以被安裝到同一個ChannelPipeline中。如果一個消息或者任何其他的入站事件被讀取,那么它會從ChannelPipeline 的頭部開始流動,最終,數據將會到達ChannelPipeline 的尾端,屆時,所有處理就都結束了。數據的出站運動(即正在被寫的數據)在概念上也是一樣的。在這

種情況下,數據將從ChannelOutboundHandler 鏈的尾端開始流動,直到它到達鏈的頭部為止。在這之后,出站數據將會到達網絡傳輸層,這里顯示為Socket。通常情況下,這將觸發一個寫操作。如果將兩個類別的ChannelHandler都混合添加到同一個ChannelPipeline 中會發生什么。雖然

ChannelInboundHandle 和ChannelOutboundHandle 都擴展自ChannelHandler,但是Netty 能區分ChannelInboundHandler實現和ChannelOutboundHandler 實現,並確保數據只會在具有相同定向類型的

兩個ChannelHandler 之間傳遞。

ChannelPipeline上的方法

addFirst、addBefore、addAfter、addLast 將一個ChannelHandler 添加到ChannelPipeline 中

remove 將一個ChannelHandler 從ChannelPipeline 中移除

replace 將ChannelPipeline 中的一個ChannelHandler 替換為另一個ChannelHandler

get 通過類型或者名稱返回ChannelHandler

context 返回和ChannelHandler 綁定的ChannelHandlerContext

names 返回ChannelPipeline 中所有ChannelHandler 的名稱

ChannelPipeline 的API 公開了用於調用入站和出站操作的附加方法。

ChannelHandlerContext 

 

通過使用作為參數傳遞到每個方法的ChannelHandlerContext,事件可以被傳遞給當前ChannelHandler 鏈中的下一個ChannelHandler。雖然這個對象可以被用於獲取底層的Channel,但是它主要還是被用於寫出站數據。

ChannelHandlerContext 代表了ChannelHandler 和ChannelPipeline 之間的關聯,每當有ChannelHandler 添加到ChannelPipeline 中時,都會創建ChannelHandler-Context。ChannelHandlerContext 的主要功能是管理它所關聯的ChannelHandler 和在同一個ChannelPipeline 中的其他

ChannelHandler 之間的交互。ChannelHandlerContext 有很多的方法,其中一些方法也存在於Channel 和Channel-Pipeline 本身上,但是有一點重要的不同。如果調用Channel 或者ChannelPipeline 上的這些方法,它們將沿着整個ChannelPipeline 進行傳播。而調用位於ChannelHandlerContext上的相同方法,則將從當前所關聯的ChannelHandler 開始,並且只會傳播給位於該ChannelPipeline 中的下一個(入站下一個,出站上一個)能夠處理該事件的ChannelHandler。

ChannelHandlerContext 的API

alloc 返回和這個實例相關聯的Channel 所配置的ByteBufAllocator

bind 綁定到給定的SocketAddress,並返回ChannelFuture

channel 返回綁定到這個實例的Channel

close 關閉Channel,並返回ChannelFuture

connect 連接給定的SocketAddress,並返回ChannelFuture

deregister 從之前分配的EventExecutor 注銷,並返回ChannelFuture

disconnect 從遠程節點斷開,並返回ChannelFuture

executor 返回調度事件的EventExecutor

fireChannelActive 觸發對下一個ChannelInboundHandler 上的channelActive()方法(已連接)的調用

fireChannelInactive 觸發對下一個ChannelInboundHandler 上的channelInactive()方法(已關閉)的調用

fireChannelRead 觸發對下一個ChannelInboundHandler 上的channelRead()方法(已接收的消息)的調用

fireChannelReadComplete 觸發對下一個ChannelInboundHandler 上的channelReadComplete()方法的調用

fireChannelRegistered 觸發對下一個ChannelInboundHandler 上的fireChannelRegistered()方法的調用

fireChannelUnregistered 觸發對下一個ChannelInboundHandler 上的fireChannelUnregistered()方法的調用

fireChannelWritabilityChanged 觸發對下一個ChannelInboundHandler 上的fireChannelWritabilityChanged()方法的調用

fireExceptionCaught 觸發對下一個ChannelInboundHandler 上的fireExceptionCaught(Throwable)方法的調用

fireUserEventTriggered 觸發對下一個ChannelInboundHandler 上的fireUserEventTriggered(Object evt)方法的調用

handler 返回綁定到這個實例的ChannelHandler

isRemoved 如果所關聯的ChannelHandler 已經被從ChannelPipeline中移除則返回true

name 返回這個實例的唯一名稱

pipeline 返回這個實例所關聯的ChannelPipeline

read 將數據從Channel讀取到第一個入站緩沖區;如果讀取成功則觸發一個channelRead事件,並(在最后一個消息被讀取完成后)通知ChannelInboundHandler 的channelReadComplete(ChannelHandlerContext)方法

當使用ChannelHandlerContext 的API 的時候,有以下兩點:

  • ChannelHandlerContext 和ChannelHandler 之間的關聯(綁定)是永遠不會改變的,所以緩存對它的引用是安全的;
  • 如同我們在本節開頭所解釋的一樣,相對於其他類的同名方法,ChannelHandler Context的方法將產生更短的事件流,應該盡可能地利用這個特性來獲得最大的性能。

選擇合適的內置通信傳輸模式

NIO io.netty.channel.socket.nio 使用java.nio.channels 包作為基礎——基於選擇器的方式

Epoll io.netty.channel.epoll 由 JNI 驅動的 epoll()和非阻塞 IO。這個傳輸支持只有在Linux 上可用的多種特性,如SO_REUSEPORT,比NIO 傳輸更快,而且是完全非阻塞的。將NioEventLoopGroup替換為EpollEventLoopGroup , 並且將NioServerSocketChannel.class 替換為

EpollServerSocketChannel.class 即可。

OIO io.netty.channel.socket.oio 使用java.net 包作為基礎——使用阻塞流

Local io.netty.channel.local 可以在VM 內部通過管道進行通信的本地傳輸

Embedded io.netty.channel.embedded Embedded 傳輸,允許使用ChannelHandler 而又不需要一個真正的基於網絡的傳輸。在測試ChannelHandler 實現時非常有用

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM