Netty 4.x 用戶指導

問題

現在我們使用通用的應用或者庫跟其他人進行通信。例如, 我們經常使用 HTTP 客戶端的庫從網站服務中獲取數據和通過網站服務調用遠程程序。然而, 一個通用的協議它有時實現起來不是很好。就像我們不使用通用的 HTTP 服務獲取大文件, 電子郵件消息, 和諸如財務信息和多人遊戲數據的實時消息。必須有一個高度優化的協議致力於一個特別的目的。例如, 你可能想實現一個基於 AJAX 優化的一個聊天應用, 流媒體,大的文件傳輸的 HTTP 服務。你甚至可以根據你需求設計和實現一個全新的協議。另外一個不可避免的情況是你必須處理一個已有的協議以確保在老系統中的互通性。在這種情況下,重要的是我們如何快速地實現該協議,同時不犧牲所得到的應用的穩定性和性能。

解決方案

Netty是一個努力提供異步事件驅動可維護的高效快速發展, 高擴展性協議服務器和客戶端的網絡應用框架和工具。

換句話說, Netty 是一個能夠快速和輕鬆開發的如協議服務器和客戶端的網絡應用 NIO 客戶端服務端框架。它大大簡化了網絡編程,如TCP和UDP套接字服務器的開發。

‘快速和簡單’不意味着應用會遭受可維護性和性能產生的問題。 Netty 已被精心設計通過大量如FTP、SMTP、HTTP以及各種二進制和文本協議中的經驗。因此, Netty 已經成功地找到一種方法沒有妥協的來實現容易開發,性能,穩定,靈活。

寫一個 Discard 服務

在世界上最簡單的協議不是’Hello, World!’ 而是DISCARD。它是一個丟棄任何接收到數據沒有任何反應的協議。

爲了實現 DISCARD 的協議, 你只需要做的一件事情是忽略所有接收到的數據。讓我們從 Netty 的 handler 直接開始 實現 I/O 事件。

package io.netty.example.discard;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

/**
 * Handles a server-side channel.
 */
public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
        // Discard the received data silently.
        ((ByteBuf) msg).release(); // (3)
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
        // Close the connection when an exception is raised.
        cause.printStackTrace();
        ctx.close();
    }
}
  1. DiscardServerHandler 繼承於 ChannelInboundHandlerAdapter,這是一個 ChannelInboundHandler 的實現類。ChannelInboundHandler 提供了各種你可以重寫的方法。目前, 只需要繼承於ChannelInboundHandlerAdapter而不是實現這個接口。

  2. 我們在這裏重寫 channelRead() 方法。這個方法被用來接收消息, 無論何時來自客戶端的新數據都被接收。在這個例子, 接收到的消息類型是ByteBuf

  3. 爲了實現 DISCARD 協議, 處理機已經忽略接收到的消息。ByteBuf 是一個必須通過 release() 方法顯式釋放的引用計數對象。請記住通過處理機釋放任何引用的對象是處理機的職責。通常, channelRead() 方法的實現如下:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) {
    try {
        // Do something with msg
    } finally {
        ReferenceCountUtil.release(msg);
    }
}
  1. Netty 由於一個 I/O 錯誤或者由於處理機實現類處理時拋出異常引起一個異常會調用 exceptionCaught() 方法。在大多數情況下, 這個被捕獲的異常應該被記錄下來並且在這裏關閉相關的通道, 雖然這種方法的實現可以是不同的,這取決於你想做什麼來處理一個特殊的情況。例如, 你可能想在錯誤代碼關閉連接之前發送一條響應的消息。

到目前爲止一切順利。我們一開始已經實現了半個 DISCARD 服務器。 剩下的就是寫 main() 方法開啓 DiscardServerHandler 這個服務。

package io.netty.example.discard;

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

/**
 * Discards any incoming data.
 */
public class DiscardServer {

    private int port;

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

    public void run() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap(); // (2)
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class) // (3)
             .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                 @Override
                 public void initChannel(SocketChannel ch) throws Exception {
                     ch.pipeline().addLast(new DiscardServerHandler());
                 }
             })
             .option(ChannelOption.SO_BACKLOG, 128)          // (5)
             .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

            // Bind and start to accept incoming connections.
            ChannelFuture f = b.bind(port).sync(); // (7)

            // Wait until the server socket is closed.
            // In this example, this does not happen, but you can do that to gracefully
            // shut down your server.
            f.channel().closeFuture().sync();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    public static void main(String[] args) throws Exception {
        int port;
        if (args.length > 0) {
            port = Integer.parseInt(args[0]);
        } else {
            port = 8080;
        }
        new DiscardServer(port).run();
    }
}
  1. NioEventLoopGroup 是一個多線程的處理機 I/O 操作事件輪詢。Netty 提供各種傳輸不同種類的EventLoopGroup實現類。我們實現一個服務端應用的例子, 因此兩個 NioEventLoopGroup 將被使用。第一個, 通常被稱爲 ‘boss’,
    接受傳入的連接。第二個,通常被稱爲 ‘worker’, 處理所接受的連接的流量,一旦 boss 接受連接,並註冊接受的連接到 worker。多少線程被使用, 它們怎樣被映射到創建 Channels ,取決於 EventLoopGroup 的實現,甚至可通過構造器進行配置。

  2. ServerBootstrap 是一個設置服務器的輔助類。你可以直接使用 Channel 來設置服務器。但是, 請注意一個繁瑣的過程,大多數情況下你不需要這樣做。

  3. 這裏, 我們指定使用被實例化一個新的 Channel 來接受傳入的連接的 NioServerSocketChannel 類。

  4. 這裏指定的處理機將一直被一個新的 Channel 處理。這個 ChannelInitializer 是一個特別有意幫助用戶配置新 Channel 的 handler。

原文來自:https://github.com/netty/netty/wiki/User-guide-for-4.x

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章