Netty系列-核心組件

待完善

Channel、EventLoop和ChannelFuture

ChannelEventLoopChannelFuture這些類組合在一起,可以被認為是Netty網絡抽象的代表:

  • Channel—Socket;
  • EventLoop—控制流、多線程處理、并發;
  • ChannelFuture—異步通知

Channel

暫略

EventLoop

EventLoop定義了Netty的核心抽象,用于處理連接的生命周期中所發生的事件。

  • 一個EventLoopGroup包含一個或者多個EventLoop

  • 一個EventLoop在它的生命周期內只和一個Thread綁定;

  • 所有由EventLoop處理的I/O事件都將在它專有的Thread上被處理;

  • 一個Channel在它的生命周期內只注冊于一個EventLoop

  • 一個EventLoop可能會被分配給一個或多個Channel

注意,在這種設計中,一定程度上消除了對于同步的需要。

ChannelFuture

暫略

ChannelHandler和ChannelPipeline

ChannelHandler

Handles an I/O event or intercepts an I/O operation, and forwards it to its next handler in its ChannelPipeline.

  1. ChannelHandler

    public interface ChannelHandler {
        // ChannelHandler添加到ChannelPipeline中時被調用
        void handlerAdded(ChannelHandlerContext ctx) throws Exception;
        // ChannelHandler從ChannelPipeline中移除時被調用
        void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
        // 處理過程中在ChannelPipeline中有錯誤產生時被調用
        void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
    }
    
  2. ChannelHandler層次結構如下圖所示

    圖片.png

    ChannelHandler itself does not provide many methods, but you usually have to implement one of its subtypes:

    • ChannelInboundHandler to handle inbound I/O events, and
    • ChannelOutboundHandler to handle outbound I/O operations.

    Alternatively, the following adapter classes are provided for your convenience:

    • ChannelInboundHandlerAdapter to handle inbound I/O events,
    • ChannelOutboundHandlerAdapter to handle outbound I/O operations, and
    • ChannelDuplexHandler to handle both inbound and outbound events
A ChannelHandler often needs to store some stateful information?
  1. ChannelInboundHandler

    public interface ChannelInboundHandler extends ChannelHandler {
    
        void channelRegistered(ChannelHandlerContext ctx) throws Exception;
        
        void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
    
        void channelActive(ChannelHandlerContext ctx) throws Exception;
        
        void channelInactive(ChannelHandlerContext ctx) throws Exception;
    
        void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
    
        void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
    
        void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
    
        void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
    
        void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
    }
    
  2. ChannelInboundHandlerAdapter

    public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler {
    
        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelRegistered();
        }
    
        @Override
        public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelUnregistered();
        }
    
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelActive();
        }
    
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelInactive();
        }
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ctx.fireChannelRead(msg);
        }
    
        @Override
        public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelReadComplete();
        }
    
        @Override
        public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
            ctx.fireUserEventTriggered(evt);
        }
    
        @Override
        public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception {
            ctx.fireChannelWritabilityChanged();
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
                throws Exception {
            ctx.fireExceptionCaught(cause);
        }
    }
    

    ChannelInboundHandlerAdapterchannelRead方法處理完消息后不會自動釋放消息,若想自動釋放收到的消息,可以使用SimpleChannelInboundHandler

    Usually, channelRead() handler method is implemented like the following:

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        try {
            // Do something with msg
        } finally {
            ReferenceCountUtil.release(msg);
        }
    }
    
  1. ChannelOutboundHandler

    public interface ChannelOutboundHandler extends ChannelHandler {
    
        void bind(ChannelHandlerContext ctx, SocketAddress localAddress, 
            ChannelPromise promise) throws Exception;
    
        void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception;
    
        void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    
        void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
        
        void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception;
    
        void read(ChannelHandlerContext ctx) throws Exception;
    
        void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception;
    
        void flush(ChannelHandlerContext ctx) throws Exception;
    }
    
  2. ChannelOutboundHandlerAdapter

```java
public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler {

    @Override
    public void bind(ChannelHandlerContext ctx, SocketAddress localAddress,
            ChannelPromise promise) throws Exception {
        ctx.bind(localAddress, promise);
    }

    @Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress,
            SocketAddress localAddress, ChannelPromise promise) throws Exception {
        ctx.connect(remoteAddress, localAddress, promise);
    }

    @Override
    public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
        ctx.disconnect(promise);
    }

    @Override
    public void close(ChannelHandlerContext ctx, ChannelPromise promise)
            throws Exception {
        ctx.close(promise);
    }

    @Override
    public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
        ctx.deregister(promise);
    }

    @Override
    public void read(ChannelHandlerContext ctx) throws Exception {
        ctx.read();
    }

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

    @Override
    public void flush(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}
```

ChannelHandlerContext

使ChannelHandler能夠與其ChannelPipeline和其他處理程序進行交互。除其他事項外,處理程序可以通知ChannelPipeline中的下一個ChannelHandler,也可以動態修改它所屬的ChannelPipeline

  1. Notify

    You can notify the closest handler in the same ChannelPipeline by calling one of the various methods provided here.

  2. Modifying a pipeline

    You can get the ChannelPipeline your handler belongs to by calling pipeline(). A non-trivial application could insert, remove, or replace handlers in the pipeline dynamically at runtime.

  3. Retrieving for later use

    You can keep the ChannelHandlerContext for later use, such as triggering an event outside the handler methods, even from a different thread.

    public class MyHandler extends ChannelDuplexHandler {
      
           private ChannelHandlerContext ctx;
      
           public void beforeAdd(ChannelHandlerContext ctx) {
               this.ctx = ctx;
           }
      
           public void login(String username, password) {
               ctx.write(new LoginMessage(username, password));
           }
           ...
    }
    
  4. Storing stateful information

    attr(AttributeKey) allow you to store and access stateful information that is related with a handler and its context. Please refer to ChannelHandler to learn various recommended ways to manage stateful information.

  5. A handler can have more than one context

    Please note that a ChannelHandler instance can be added to more than one ChannelPipeline. It means a single ChannelHandler instance can have more than one ChannelHandlerContext and therefore the single instance can be invoked with different ChannelHandlerContexts if it is added to one or more ChannelPipelines more than once.

    For example, the following handler will have as many independent AttributeKeys as how many times it is added to pipelines, regardless if it is added to the same pipeline multiple times or added to different pipelines multiple times:

    public class FactorialHandler extends ChannelInboundHandlerAdapter {
      
         private final AttributeKey<Integer> counter = AttributeKey.valueOf("counter");
      
         // This handler will receive a sequence of increasing integers starting
         // from 1.
          @Override
         public void channelRead(ChannelHandlerContext ctx, Object msg) {
           Integer a = ctx.attr(counter).get();
      
           if (a == null) {
             a = 1;
           }
      
           attr.set(a * (Integer) msg);
         }
       }
      
       // Different context objects are given to "f1", "f2", "f3", and "f4" even if
       // they refer to the same handler instance.  Because the FactorialHandler
       // stores its state in a context object (using an AttributeKey), the factorial is
       // calculated correctly 4 times once the two pipelines (p1 and p2) are active.
       FactorialHandler fh = new FactorialHandler();
      
       ChannelPipeline p1 = Channels.pipeline();
       p1.addLast("f1", fh);
       p1.addLast("f2", fh);
      
       ChannelPipeline p2 = Channels.pipeline();
       p2.addLast("f3", fh);
       p2.addLast("f4", fh);
    
    

ChannelPipeline

ChannelPipeline提供了ChannelHandler鏈的容器,并定義了用于在該鏈上傳播入站和出站事件流的API。當Channel被創建時,它會被自動地分配到它專屬的ChannelPipeline

  1. Creation of a pipeline

    ==Each channel has its own pipeline and it is created automatically== when a new channel is created.

  2. How an event flows in a pipeline

    The following diagram describes how I/O events are processed by ChannelHandlers in a ChannelPipeline typically. An I/O event is handled by either a ChannelInboundHandler or a ChannelOutboundHandler and be forwarded to its closest handler by calling the event propagation methods defined in ChannelHandlerContext, such as ChannelHandlerContext.fireChannelRead(Object) and ChannelHandlerContext.write(Object).

                                        I/O Request  via Channel or
                                              ChannelHandlerContext
        +---------------------------------------------------+---------------+
        |                           ChannelPipeline         |               |
        |                                                  \|/              |
        |    +---------------------+            +-----------+----------+    |
        |    | Inbound Handler  N  |            | Outbound Handler  1  |    |
        |    +----------+----------+            +-----------+----------+    |
        |              /|\                                  |               |
        |               |                                  \|/              |
        |    +----------+----------+            +-----------+----------+    |
        |    | Inbound Handler N-1 |            | Outbound Handler  2  |    |
        |    +----------+----------+            +-----------+----------+    |
        |              /|\                                  .               |
        |               .                                   .               |
        | ChannelHandlerContext.fireIN_EVT() ChannelHandlerContext.OUT_EVT()|
        |        [ method call]                       [method call]         |
        |               .                                   .               |
        |               .                                  \|/              |
        |    +----------+----------+            +-----------+----------+    |
        |    | Inbound Handler  2  |            | Outbound Handler M-1 |    |
        |    +----------+----------+            +-----------+----------+    |
        |              /|\                                  |               |
        |               |                                  \|/              |
        |    +----------+----------+            +-----------+----------+    |
        |    | Inbound Handler  1  |            | Outbound Handler  M  |    |
        |    +----------+----------+            +-----------+----------+    |
        |              /|\                                  |               |
        +---------------+-----------------------------------+---------------+
                        |                                  \|/
        +---------------+-----------------------------------+---------------+
        |               |                                   |               |
        |       [ Socket.read() ]                    [ Socket.write() ]     |
        |                                                                   |
        |  Netty Internal I/O Threads (Transport Implementation)            |
        +-------------------------------------------------------------------+
    

    An inbound event is handled by the inbound handlers in the ==bottom-up direction== as shown on the left side of the diagram. An inbound handler usually handles the inbound data generated by the I/O thread on the bottom of the diagram. The inbound data is often read from a remote peer via the actual input operation such as SocketChannel.read(ByteBuffer). If an inbound event goes beyond the top inbound handler, it is discarded silently, or logged if it needs your attention.

    An outbound event is handled by the outbound handler in the ==top-down direction== as shown on the right side of the diagram. An outbound handler usually generates or transforms the outbound traffic such as write requests. If an outbound event goes beyond the bottom outbound handler, it is handled by an I/O thread associated with the Channel. The I/O thread often performs the actual output operation such as SocketChannel.write(ByteBuffer).

  3. Forwarding an event to the next handler

    a handler has to invoke the event propagation methods in ChannelHandlerContext to forward an event to its next handler. Those methods include:

    • Inbound event propagation methods:

      • ChannelHandlerContext.fireChannelRegistered()
      • ChannelHandlerContext.fireChannelActive()
      • ChannelHandlerContext.fireChannelRead(Object)
      • ChannelHandlerContext.fireChannelReadComplete()
      • ChannelHandlerContext.fireExceptionCaught(Throwable)
      • ChannelHandlerContext.fireUserEventTriggered(Object)
      • ChannelHandlerContext.fireChannelWritabilityChanged()
      • ChannelHandlerContext.fireChannelInactive()
      • ChannelHandlerContext.fireChannelUnregistered()
    • Outbound event propagation methods:

      • ChannelHandlerContext.bind(SocketAddress, ChannelPromise)
      • ChannelHandlerContext.connect(SocketAddress, SocketAddress, ChannelPromise)
      • ChannelHandlerContext.write(Object, ChannelPromise)
      • ChannelHandlerContext.flush()
      • ChannelHandlerContext.read()
      • ChannelHandlerContext.disconnect(ChannelPromise)
      • ChannelHandlerContext.close(ChannelPromise)
      • ChannelHandlerContext.deregister(ChannelPromise)
  4. Building a pipeline

    A user is supposed to have one or more ChannelHandlers in a pipeline to receive I/O events (e.g. read) and to request I/O operations (e.g. write and close). For example, a typical server will have the following handlers in each channel's pipeline, but your mileage may vary depending on the complexity and characteristics of the protocol and business logic:

    • Protocol Decoder - translates binary data (e.g. ByteBuf) into a Java object.
    • Protocol Encoder - translates a Java object into binary data.
    • Business Logic Handler - performs the actual business logic (e.g. database access).

    and it could be represented as shown in the following example:

    static final EventExecutorGroup group = new DefaultEventExecutorGroup(16);
    ...
    
    ChannelPipeline pipeline = ch.pipeline();
    
    pipeline.addLast("decoder", new MyProtocolDecoder());
    pipeline.addLast("encoder", new MyProtocolEncoder());
    
    // Tell the pipeline to run MyBusinessLogicHandler's event handler methods
    // in a different thread than an I/O thread so that the I/O thread is not blocked by
    // a time-consuming task.
    // If your business logic is fully asynchronous or finished very quickly, you don't
    // need to specify a group.
    pipeline.addLast(group, "handler", new MyBusinessLogicHandler());
    
  5. Thread safety

    A ChannelHandler can be added or removed at any time because a ChannelPipeline is thread safe. For example, you can insert an encryption handler when sensitive information is about to be exchanged, and remove it after the exchange.

ServerBootstrap和Bootstrap

在深入地學習了ChannelPipelineChannelHandlerEventLoop之后,你接下來 的問題可能是:“如何將這些部分組織起來,成為一個可實際運行的應用程序呢?”

答案是?==“引導”(Bootstrapping)==。簡單來說,引導一個應用程序是指對它進行配置,并使它運行起來的過程—盡管該過程的具體細節可能并不如它的定義那樣簡單,尤其是對于一個網絡應用程序來說。

引導類層次結構

圖片.png

ServerBootstrapBootstrap分別作用于==服務器==和==客戶端==。ServerBootstrap致力于使用一個==父Channel==來接受來自客戶端的連接,并創建==子Channel==以用于它們之間的通信;而客戶端只需要==一個單獨的、沒有父Channel的Channel==來用于所有的網絡交互(這也適用于無連接的傳輸協議,如UDP,因為它們并不是每個連接都需要一個單獨的Channel)。

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, 
                    C extends Channel>  implements Cloneable

子類型B是其父類型的一個類型參數,因此可以返回到運行時實例的引用以 支持方法的鏈式調用(也就是所謂的流式語法)

為什么引導類是Cloneable

你有時可能會需要創建多個具有類似配置或者完全相同配置的 Channel 。為了支持這種模式而又不 需 要 為 每 個 Channel 都 創 建 并 配 置 一 個 新 的 引 導 類 實 例 , AbstractBootstrap 被 標 記 為 了 Cloneable 。在一個已經配置完成的引導類實例上調用 clone() 方法將返回另一個可以立即使用的引 導類實例。

注意,這種方式只會創建引導類實例的 EventLoopGroup 的一個淺拷貝,所以,后者 將在所有克 隆的 Channel 實例之間共享。這是可以接受的,因為通常這些克隆的 Channel 的生命周期都很短暫,一 個典型的場景是——創建一個 Channel 以進行一次HTTP請求。

Bootstrap

Bootstrap類被用于客戶端或者使用了無連接協議的應用程序中。Bootstrap類的API如下:

  • Bootstrap group(EventLoopGroup)

    設置用于處理Channel所有事件的EventLoopGroup

  • Bootstrap channel( Class<? extends C>)

  • Bootstrap channelFactory(ChannelFactory<? extends C>)

    channel()方法指定了Channel的實現類。如果該實現類沒提供默認的構造函數 , 可以通過調用channelFactory()方法來指定一個工廠類,它將會被bind()方法調用。

  • Bootstrap localAddress(SocketAddress)

    指定Channel應該綁定到的本地地址。如果沒有指定,則將由操作系統創建一個隨機的地址。或者,也可以通過bind()或者connect()方法指定localAddress。

  • <T> Bootstrap option(ChannelOption<T> option, T value)

    設置ChannelOption, 其將被應用到每個新創建的Channel的ChannelConfig。 這些選項將會通過bind()或者connect()方法設置到Channel ,不管哪個先被調用。這個方法在Channel已經被創建后再調用將不會有任何的效果。支持的ChannelOption取決于使用的 Channel類型。

  • <T> Bootstrap attr( Attribute<T> key, T value)

    指定新創建的Channel的屬性值。這些屬性值是通過bind()或者connect()方法設置到Channel的,具體取決于誰最先被調用。這個方法在Channel被創建后將不會有任何的效果。

  • Bootstrap handler(ChannelHandler)

    設置將被添加到ChannelPipeline以接收事件通知的ChannelHandler

  • Bootstrap clone()

    創建一個當前Bootstrap的克隆,其具有和原始的Bootstrap相同的設置信息。

  • Bootstrap remoteAddress(SocketAddress)

    設置遠程地址。或者,也可以通過connect()方法來指定它。

  • ChannelFuture connect()

    連接到遠程節點并返回一個ChannelFuture,其將會在連接操作完成后接收到通知。

  • ChannelFuture bind()

    綁定Channel并返回一個ChannelFuture,其將會在綁定操作完成后接收到通知,在那之后必須調用Channel.connect()方法來建立連接。

Bootstrap類負責為客戶端和使用無連接協議的應用程序創建Channel,如圖所示:

圖片.png

在引導的過程中,在調用bind()或者connect()方法之前,必須調用以下方法來設置所需的組件:

  • group();
  • channel()或者channelFactory();
  • handler()

如果不這樣做,則將會導致IllegalStateException 。對handler()方法的調用尤其重要,因為它需要配置好ChannelPipeline 。

ServerBootstrap

ServerBootstrap的API如下:

  • group

    設置ServerBootstrap要用的EventLoopGroup。這個EventLoopGroup將用于ServerChannel和被接受的子Channel的I/O處理。

  • channel

    設置將要被實例化的ServerChannel類。

  • channelFactory

    如果不能通過默認的構造函數創建Channel,那么可以提供一個ChannelFactory。

  • localAddress

    指定ServerChannel應該綁定到的本地地址。如果沒有指定,則將由操作系統使用一個隨機地址。或者,可以通過bind()方法來指定該localAddress。

  • option

    指定要應用到新創建的ServerChannel的ChannelConfig的ChannelOption。這些選項將會通過bind()方法設置到Channel。在bind()方法被調用之后,設置或者改變 ChannelOption都不會有任何的效果。所支持的ChannelOption取決于所使用的Channel類型。

  • childOption

    指定當子Channel被接受時,應用到==子Channel的ChannelConfig==的ChannelOption 。所支持的ChannelOption取決于所使用的Channel的類型。

  • attr

    指定ServerChannel上的屬性,屬性將會通過bind()方法設置給Channel。在調用 bind()方法之后改變它們將不會有任何的效果

  • childAttr

    將屬性設置給已經被接受的子Channel

  • handler

    設置被添加到ServerChannel的ChannelPipeline中的ChannelHandler。

  • childHandler

    設置將被添加到已被接受的子Channel的ChannelPipeline中的ChannelHandler。 handler()方法和childHandler()方法之間的區別是:前者所添加的ChannelHandler由接受子Channel的ServerChannel處理,而childHandler()方法所添加ChannelHandler將由已被接受的子Channel處理,其代表一個綁定到遠程節點的套接字。

  • clone

    克隆一個設置和原始的ServerBootstrap相同的ServerBootstrap。

  • bind

    綁定ServerChannel并且返回一個ChannelFuture成后收到通知(帶著成功或者失敗的結果)

ServerBootstrap在bind()方法被調用時創建了一個ServerChannel,并且該ServerChannel管理了多個子Channel

圖片.png

從Channel引導客戶端?

ChannelInitializer

在引導的過程中調用了handler()或者childHandler()方法來添加單個的ChannelHandler。對于簡單的應用程序來說可能已經足夠了,但是它不能滿足更加復雜的需求。

可以通過在ChannelPipeline中將它們鏈接在一起來部署盡可能多的ChannelHandler,Netty提供了一個特殊的ChannelInitializer類。

A special ChannelInboundHandler which offers an easy way to initialize a Channel once it was registered to its EventLoop. Implementations are most often used in the context of Bootstrap.handler(ChannelHandler), ServerBootstrap.handler(ChannelHandler) and ServerBootstrap.childHandler(ChannelHandler) to setup the ChannelPipeline of a Channel.

public abstract class ChannelInitializer<C extends Channel> 
                                extends ChannelInboundHandlerAdapter

它定義了下面的方法:

protected abstract void initChannel(C ch) throws Exception;

這個方法提供了一種將多個ChannelHandler添加到一個ChannelPipeline中的簡便方法。只需要簡單地向BootstrapServerBootstrap的實例提供你的ChannelInitializer實現即可,并且一旦Channel被注冊到了它的EventLoop之后,就會調用你的initChannel方法。在該方法返回之后,ChannelInitializer的實例將會從ChannelPipeline中移除它自己。

示例代碼如下:

public class MyChannelInitializer extends ChannelInitializer {
    public void initChannel(Channel channel) {
        channel.pipeline().addLast("myHandler", new MyHandler());
    }
}

ServerBootstrap bootstrap = ...;
...
bootstrap.childHandler(new MyChannelInitializer());
...

ChannelOption和屬性

使用option()方法可以將ChannelOption應用到引導,你所提供的值將會被自動應用到引導所創建的所有Channel(這樣就可以不用在每個Channel創建時都手動配置它。)。可用的ChannelOption包括了底層連接的詳細信息,如keep-alive或者超時屬性以及緩存區設置。

Netty應用程序通常與組織的專有軟件集成在一起,而像Channel這樣的組件可能甚至會在正常的Netty生命周期之外被使用。 在某些常用的屬性和數據不可用時, Netty提供了 AttributeMap抽象(一個由Channel和引導類提供的集合)以及AttributeKey<T>(一個用于插入和獲取屬性值的泛型類)。使用這些工具,便可以安全地將任何類型的數據項與客戶端和服務器Channel(包含ServerChannel的子Channel)相關聯了。

有點重要?

優雅關閉

優雅是指干凈地釋放資源。關閉Netty應用程序并沒有太多的魔法,最重要的是需要關閉EventLoopGroup,它將處理任何掛起的事件和任務,并且隨后釋放所有活動的線程。

// 創建處理 I/O 的 EventLoopGroup
EventLoopGroup group = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap();
// 創建一個 Bootstrap 類的實例并配置它
bootstrap.group(group)
         .channel(NioSocketChannel.class);
...
Future<?> future = group.shutdownGracefully();
// block until the group has shutdown
future.syncUninterruptibly();

資源管理

每當通過調用 ChannelInboundHandler.channelRead()或者 ChannelOutbound- Handler.write()方法來處理數據時,你都需要確保沒有任何的資源泄漏。

為了幫助你診斷潛在的(資源泄漏)問題,Netty提供了class ResourceLeakDetector1, 它將對你應用程序的緩沖區分配做大約 1%的采樣來檢測內存泄露。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容