Netty 4.x 寫(xiě)個(gè)丟棄服務(wù)器

2018-10-25 14:46 更新

寫(xiě)個(gè)丟棄服務(wù)器

世上最簡(jiǎn)單的協(xié)議不是'Hello, World!' 而是 DISCARD(丟棄)。這個(gè)協(xié)議將會(huì)丟掉任何收到的數(shù)據(jù),而不響應(yīng)。

為了實(shí)現(xiàn) DISCARD 協(xié)議,你只需忽略所有收到的數(shù)據(jù)。讓我們從 handler (處理器)的實(shí)現(xiàn)開(kāi)始,handler 是由 Netty 生成用來(lái)處理 I/O 事件的。

    import io.netty.buffer.ByteBuf;

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

    /**
     * 處理服務(wù)端 channel.
     */
    public class DiscardServerHandler extends ChannelInboundHandlerAdapter { // (1)

        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) { // (2)
            // 默默地丟棄收到的數(shù)據(jù)
            ((ByteBuf) msg).release(); // (3)
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // (4)
            // 當(dāng)出現(xiàn)異常就關(guān)閉連接
            cause.printStackTrace();
            ctx.close();
        }
    }

1.DiscardServerHandler 繼承自 ChannelInboundHandlerAdapter,這個(gè)類實(shí)現(xiàn)了 ChannelInboundHandler接口,ChannelInboundHandler 提供了許多事件處理的接口方法,然后你可以覆蓋這些方法。現(xiàn)在僅僅只需要繼承 ChannelInboundHandlerAdapter 類而不是你自己去實(shí)現(xiàn)接口方法。

2.這里我們覆蓋了 chanelRead() 事件處理方法。每當(dāng)從客戶端收到新的數(shù)據(jù)時(shí),這個(gè)方法會(huì)在收到消息時(shí)被調(diào)用,這個(gè)例子中,收到的消息的類型是 ByteBuf

3.為了實(shí)現(xiàn) DISCARD 協(xié)議,處理器不得不忽略所有接受到的消息。ByteBuf 是一個(gè)引用計(jì)數(shù)對(duì)象,這個(gè)對(duì)象必須顯示地調(diào)用 release() 方法來(lái)釋放。請(qǐng)記住處理器的職責(zé)是釋放所有傳遞到處理器的引用計(jì)數(shù)對(duì)象。通常,channelRead() 方法的實(shí)現(xiàn)就像下面的這段代碼:


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

4.exceptionCaught() 事件處理方法是當(dāng)出現(xiàn) Throwable 對(duì)象才會(huì)被調(diào)用,即當(dāng) Netty 由于 IO 錯(cuò)誤或者處理器在處理事件時(shí)拋出的異常時(shí)。在大部分情況下,捕獲的異常應(yīng)該被記錄下來(lái)并且把關(guān)聯(lián)的 channel 給關(guān)閉掉。然而這個(gè)方法的處理方式會(huì)在遇到不同異常的情況下有不同的實(shí)現(xiàn),比如你可能想在關(guān)閉連接之前發(fā)送一個(gè)錯(cuò)誤碼的響應(yīng)消息。

目前為止一切都還不錯(cuò),我們已經(jīng)實(shí)現(xiàn)了 DISCARD 服務(wù)器的一半功能,剩下的需要編寫(xiě)一個(gè) main() 方法來(lái)啟動(dòng)服務(wù)端的 DiscardServerHandler。

    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;

    /**
     * 丟棄任何進(jìn)入的數(shù)據(jù)
     */
    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)

                // 綁定端口,開(kāi)始接收進(jìn)來(lái)的連接
                ChannelFuture f = b.bind(port).sync(); // (7)

                // 等待服務(wù)器  socket 關(guān)閉 。
                // 在這個(gè)例子中,這不會(huì)發(fā)生,但你可以優(yōu)雅地關(guān)閉你的服務(wù)器。
                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 是用來(lái)處理I/O操作的多線程事件循環(huán)器,Netty 提供了許多不同的 EventLoopGroup 的實(shí)現(xiàn)用來(lái)處理不同的傳輸。在這個(gè)例子中我們實(shí)現(xiàn)了一個(gè)服務(wù)端的應(yīng)用,因此會(huì)有2個(gè) NioEventLoopGroup 會(huì)被使用。第一個(gè)經(jīng)常被叫做‘boss’,用來(lái)接收進(jìn)來(lái)的連接。第二個(gè)經(jīng)常被叫做‘worker’,用來(lái)處理已經(jīng)被接收的連接,一旦‘boss’接收到連接,就會(huì)把連接信息注冊(cè)到‘worker’上。如何知道多少個(gè)線程已經(jīng)被使用,如何映射到已經(jīng)創(chuàng)建的 Channel上都需要依賴于 EventLoopGroup 的實(shí)現(xiàn),并且可以通過(guò)構(gòu)造函數(shù)來(lái)配置他們的關(guān)系。

2.ServerBootstrap 是一個(gè)啟動(dòng) NIO 服務(wù)的輔助啟動(dòng)類。你可以在這個(gè)服務(wù)中直接使用 Channel,但是這會(huì)是一個(gè)復(fù)雜的處理過(guò)程,在很多情況下你并不需要這樣做。

3.這里我們指定使用 NioServerSocketChannel 類來(lái)舉例說(shuō)明一個(gè)新的 Channel 如何接收進(jìn)來(lái)的連接。

4.這里的事件處理類經(jīng)常會(huì)被用來(lái)處理一個(gè)最近的已經(jīng)接收的 Channel。ChannelInitializer 是一個(gè)特殊的處理類,他的目的是幫助使用者配置一個(gè)新的 Channel。也許你想通過(guò)增加一些處理類比如DiscardServerHandler 來(lái)配置一個(gè)新的 Channel 或者其對(duì)應(yīng)的ChannelPipeline 來(lái)實(shí)現(xiàn)你的網(wǎng)絡(luò)程序。當(dāng)你的程序變的復(fù)雜時(shí),可能你會(huì)增加更多的處理類到 pipline 上,然后提取這些匿名類到最頂層的類上。

5.你可以設(shè)置這里指定的 Channel 實(shí)現(xiàn)的配置參數(shù)。我們正在寫(xiě)一個(gè)TCP/IP 的服務(wù)端,因此我們被允許設(shè)置 socket 的參數(shù)選項(xiàng)比如tcpNoDelay 和 keepAlive。請(qǐng)參考 ChannelOption 和詳細(xì)的 ChannelConfig 實(shí)現(xiàn)的接口文檔以此可以對(duì)ChannelOption 的有一個(gè)大概的認(rèn)識(shí)。

6.你關(guān)注過(guò) option() 和 childOption() 嗎?option() 是提供給NioServerSocketChannel 用來(lái)接收進(jìn)來(lái)的連接。childOption() 是提供給由父管道 ServerChannel 接收到的連接,在這個(gè)例子中也是 NioServerSocketChannel。

7.我們繼續(xù),剩下的就是綁定端口然后啟動(dòng)服務(wù)。這里我們?cè)跈C(jī)器上綁定了機(jī)器所有網(wǎng)卡上的 8080 端口。當(dāng)然現(xiàn)在你可以多次調(diào)用 bind() 方法(基于不同綁定地址)。

恭喜!你已經(jīng)熟練地完成了第一個(gè)基于 Netty 的服務(wù)端程序。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)