Netty怎么切換三種I/O模式和源碼解釋


參考文獻:極客時間傅健老師的《Netty源碼剖析與實戰》Talk is cheap.show me the code!

三種I/O模式

  BIO:Block I/O,即同步並阻塞的IO;BIO就是傳統的java.io包下的代碼實現

  NIO:New IO(non-blocking IO):同步非阻塞的IO,jdk1.4及以上版本提供

  AIO:Async IO: 異步非阻塞IO,jdk1.7

 阻塞和非阻塞

   阻塞:沒有數據傳輸過來時,讀會阻塞直到有數據;緩沖區滿時,寫操作也會阻塞。

  非阻塞: 非阻塞遇到這些情況都是直接返回。

同步和異步

  同步:數據就緒后需要自己去讀是同步。

  異步:數據就緒后直接讀好再回調給程序是異步。

Netty對三種IO的支持

  

 

首先Netty是都支持三種IO模式的,准確的來說是曾經都支持過,因為BIO的被Netty給過期了,AIO被Netty給刪除了,具體原因這就不多贅述;知道BIO在Netty被稱為OIO,NIO在多平台下都有對應的支持,有人會問為啥有common的支持了還有Linux等其他的意義嗎,這好比全棧和后端前端之分一樣,一個通用一個專用的區別。

Netty切換IO模式

  如上圖所示,對應的實現類都差不多,甚至可以看出都是頭不一樣,如果NIO的通用是NioEventLoopGroup,而OIO的實現則是OioEventLoopGroup,先看之前的一個demo

public class MyServer {
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup(); try {
            ServerBootstrap sb = new ServerBootstrap();
            sb.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline p = ch.pipeline();
                    p.addLast(new LoggingHandler(LogLevel.INFO));
                    p.addLast(new MyServerHandler());
                }
            });
            ChannelFuture f = sb.bind(8090).sync();
            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

 

  圖上標粗的就是切換的模式的關鍵點。現在看看切換成OIO的代碼

public class MyServer {
    public static void main(String[] args) throws Exception {
        EventLoopGroup bossGroup = new OioEventLoopGroup();
        EventLoopGroup workerGroup = new OioEventLoopGroup(); try {
            ServerBootstrap sb = new ServerBootstrap();
            sb.group(bossGroup, workerGroup).channel(OioServerSocketChannel.class)
                    .handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline p = ch.pipeline();
                    p.addLast(new LoggingHandler(LogLevel.INFO));
                    p.addLast(new MyServerHandler());
                }
            });
            ChannelFuture f = sb.bind(8090).sync();
            f.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

 

  上面代碼改動也就是標粗的那些。運行起來是完全沒問題的。

  那么具體是怎么做的呢,我們看看源碼就知道了;

  點進channel()方法里,核心步驟在此:

  不難發現傳入的是OioServerSocketChannel.class,由channelFactory工廠創建返回,進入ReflectiveChannelFactory();

上圖的代碼中有“this.constructor = clazz.getConstructor();”獲取無參構造;

可以看出“return constructor.newInstance();”返回泛型“T” 就是要使用的IO模式。

總結來說:Netty實現IO模式的切換就是泛型+反射+工廠實現的。

除此之外,還有一點,"EventLoopGroup bossGroup = new NioEventLoopGroup();";實際上,它就相當於一個死循環,在“NioEventLoop.java”中,有個run(),如下圖源碼,可以看出它是個死循環(for (;;) {}),現在可以簡單的理解它就是循環監聽、處理事件的。

 

@Override
    protected void run() {
        for (;;) {
            try {
                try {
                    switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                    case SelectStrategy.CONTINUE:
                        continue;

                    case SelectStrategy.BUSY_WAIT:
                        // fall-through to SELECT since the busy-wait is not supported with NIO

                    case SelectStrategy.SELECT:
                        select(wakenUp.getAndSet(false));

                        // 'wakenUp.compareAndSet(false, true)' is always evaluated
                        // before calling 'selector.wakeup()' to reduce the wake-up
                        // overhead. (Selector.wakeup() is an expensive operation.)
                        //
                        // However, there is a race condition in this approach.
                        // The race condition is triggered when 'wakenUp' is set to
                        // true too early.
                        //
                        // 'wakenUp' is set to true too early if:
                        // 1) Selector is waken up between 'wakenUp.set(false)' and
                        //    'selector.select(...)'. (BAD)
                        // 2) Selector is waken up between 'selector.select(...)' and
                        //    'if (wakenUp.get()) { ... }'. (OK)
                        //
                        // In the first case, 'wakenUp' is set to true and the
                        // following 'selector.select(...)' will wake up immediately.
                        // Until 'wakenUp' is set to false again in the next round,
                        // 'wakenUp.compareAndSet(false, true)' will fail, and therefore
                        // any attempt to wake up the Selector will fail, too, causing
                        // the following 'selector.select(...)' call to block
                        // unnecessarily.
                        //
                        // To fix this problem, we wake up the selector again if wakenUp
                        // is true immediately after selector.select(...).
                        // It is inefficient in that it wakes up the selector for both
                        // the first case (BAD - wake-up required) and the second case
                        // (OK - no wake-up required).

                        if (wakenUp.get()) {
                            selector.wakeup();
                        }
                        // fall through
                    default:
                    }
                } catch (IOException e) {
                    // If we receive an IOException here its because the Selector is messed up. Let's rebuild
                    // the selector and retry. https://github.com/netty/netty/issues/8566
                    rebuildSelector0();
                    handleLoopException(e);
                    continue;
                }

                cancelledKeys = 0;
                needsToSelectAgain = false;
                final int ioRatio = this.ioRatio;
                if (ioRatio == 100) {
                    try {
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        runAllTasks();
                    }
                } else {
                    final long ioStartTime = System.nanoTime();
                    try {
                        processSelectedKeys();
                    } finally {
                        // Ensure we always run tasks.
                        final long ioTime = System.nanoTime() - ioStartTime;
                        runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
            // Always handle shutdown even if the loop processing threw an exception.
            try {
                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        return;
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
        }
    }

我只想做的更好,僅此而已


免責聲明!

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



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