Netty组件简单介绍

Netty组件简单介绍

1、NioEventLoop组件

(1)NioEventLoop与Thread对应关系

Thread包括两部分线程:

  • 服务端接受客户端连接线程
  • 处理每个连接读写线程

服务端接收客户端连接的线程:

客户端处理读和写的线程:

Netty中NioEventLoop能做上面两件事情

(2)引入Netty包版本

              <dependency>

                     <groupId>io.netty</groupId>

                     <artifactId>netty-all</artifactId>

                     <version>4.1.6.Final</version>

              </dependency>

 

(3)NioEventLoop源码逻辑类比Socket中Server、Client逻辑

NioEventLoop的run()方法->while循环->select方法对应Server中accept()方法

NioEventLoop的run()方法->while循环->select方法对应Client中输出流操作

Server中while循环:Socket client = serverSocket.accept();

Client中while循环:socket.getOutputStream()

2、Channel组件

(1)Channel与Socket对应关系

Channel组件底层封装了Socket,一个Channel对应一个Socket

(2)源码分析前需要下载源码

(3)Channel源码分析

Channel源码详解:

NioEventLoop类中有这样一个方法:processSelectedKey(SelectionKey k, AbstractNioChannel ch)

 

    private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {

        final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();

        if (!k.isValid()) {

            final EventLoop eventLoop;

            try {

                eventLoop = ch.eventLoop();

            } catch (Throwable ignored) {

                // If the channel implementation throws an exception because there is no event loop, we ignore this

                // because we are only trying to determine if ch is registered to this event loop and thus has authority

                // to close ch.

                return;

            }

            // Only close ch if ch is still registerd to this EventLoop. ch could have deregistered from the event loop

            // and thus the SelectionKey could be cancelled as part of the deregistration process, but the channel is

            // still healthy and should not be closed.

            // See https://github.com/netty/netty/issues/5125

            if (eventLoop != this || eventLoop == null) {

                return;

            }

            // close the channel if the key is not valid anymore

            unsafe.close(unsafe.voidPromise());

            return;

        }

 

        try {

            int readyOps = k.readyOps();

            // We first need to call finishConnect() before try to trigger a read(...) or write(...) as otherwise

            // the NIO JDK channel implementation may throw a NotYetConnectedException.

            if ((readyOps & SelectionKey.OP_CONNECT) != 0) {

                // remove OP_CONNECT as otherwise Selector.select(..) will always return without blocking

                // See https://github.com/netty/netty/issues/924

                int ops = k.interestOps();

                ops &= ~SelectionKey.OP_CONNECT;

                k.interestOps(ops);

 

                unsafe.finishConnect();

            }

 

            // Process OP_WRITE first as we may be able to write some queued buffers and so free memory.

            if ((readyOps & SelectionKey.OP_WRITE) != 0) {

                // Call forceFlush which will also take care of clear the OP_WRITE once there is nothing left to write

                ch.unsafe().forceFlush();

            }

 

            // Also check for readOps of 0 to workaround possible JDK bug which may otherwise lead

            // to a spin loop

            if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {

                unsafe.read();

                if (!ch.isOpen()) {

                    // Connection already closed - no need to handle write.

                    return;

                }

            }

        } catch (CancelledKeyException ignored) {

            unsafe.close(unsafe.voidPromise());

        }

}

SelectionKey.OP_ACCEPT表示有一个Accept事件进来,然后执行unsafe.read()方法

NioByteUnsafe表示对应一个client数据流读写

NioMessageUnsafe表示对应是否有新连接进来的情况

我们这里是处理一个新连接进来的情况,进入到NioMessageUnsafe中:

        public void read() {

            assert eventLoop().inEventLoop();

            final ChannelConfig config = config();

            final ChannelPipeline pipeline = pipeline();

            final RecvByteBufAllocator.Handle allocHandle = unsafe().recvBufAllocHandle();

            allocHandle.reset(config);

 

            boolean closed = false;

            Throwable exception = null;

            try {

                try {

                    do {

                        int localRead = doReadMessages(readBuf);

                        if (localRead == 0) {

                            break;

                        }

                        if (localRead < 0) {

                            closed = true;

                            break;

                        }

 

                        allocHandle.incMessagesRead(localRead);

                    } while (allocHandle.continueReading());

                } catch (Throwable t) {

                    exception = t;

                }

 

                int size = readBuf.size();

                for (int i = 0; i < size; i ++) {

                    readPending = false;

                    pipeline.fireChannelRead(readBuf.get(i));

                }

                readBuf.clear();

                allocHandle.readComplete();

                pipeline.fireChannelReadComplete();

 

                if (exception != null) {

                    closed = closeOnReadError(exception);

 

                    pipeline.fireExceptionCaught(exception);

                }

 

                if (closed) {

                    inputShutdown = true;

                    if (isOpen()) {

                        close(voidPromise());

                    }

                }

            } finally {

                // Check if there is a readPending which was not processed yet.

                // This could be for two reasons:

                // * The user called Channel.read() or ChannelHandlerContext.read() in channelRead(...) method

                // * The user called Channel.read() or ChannelHandlerContext.read() in channelReadComplete(...) method

                //

                // See https://github.com/netty/netty/issues/2254

                if (!readPending && !config.isAutoRead()) {

                    removeReadOp();

                }

            }

        }

NioServerSocketChannel是用来处理新连接的,然后进入到doReadMessage方法,在这里你可以看到java底层相关逻辑,javaChannel()其实返回ServerSocketChannel对象,这里的ServerSocketChannel其实就是NIO模型的SocketChannel,也对应IO模型中的ServerSocket

这里把SocketChannel封装成Netty中自定义的NioSocketChannel

doReadMessages方法:

    protected int doReadMessages(List<Object> buf) throws Exception {

        SocketChannel ch = javaChannel().accept();

 

        try {

            if (ch != null) {

                buf.add(new NioSocketChannel(this, ch));

                return 1;

            }

        } catch (Throwable t) {

            logger.warn("Failed to create a new channel from an accepted socket.", t);

 

            try {

                ch.close();

            } catch (Throwable t2) {

                logger.warn("Failed to close a socket.", t2);

            }

        }

 

        return 0;

    }

结论:最后只要基于NioSocketChannel进行读写就行了。

3、ByteBuf组件

(1)ByteBuf与IO Bytes对应关系

(2)ByteBuf类讲解

ByteBuf主要用于流的读写。

read方法(对应输入流)

write方法(对应输出流)

4、Pipleline组件

(1)Pipeline与Logic Chain对应关系

(2)NioSocketChannel与逻辑链

逻辑链:每一层父类构造方法就是一层逻辑链。

NioSocketChannel构造方法如下(倒数第一逻辑链)

进入上一级super构造方法(倒数第二逻辑链)

进入上一级super构造方法(倒数第三逻辑链)

进入上一级super构造方法(倒数第四逻辑链)

(3)Pipeline创建过程

进入newChannelPipeline()方法下一级

接着进入DefaultChannelPipeline(this)方法下一级

结论:每个Channel都会有一个Pipeline,Channel每个数据流的读写都会经过pipeline逻辑链。

5、ChannelHandler组件

(1)ChannelHandler与Logic关系

一个ChannelHandler对应一个Logic逻辑块。

(2)DefaultChannelPipeline类讲解

ChannelPipeline增加逻辑和删除逻辑方法

(3)添加ChannelHandler方法

在Pipeline中添加ChannelHandler逻辑

ChannelHandler类中回调方法:

public interface ChannelHandler {

 

    /**

     * Gets called after the {@link ChannelHandler} was added to the actual context and it's ready to handle events.

     */

    void handlerAdded(ChannelHandlerContext ctx) throws Exception;

 

    /**

     * Gets called after the {@link ChannelHandler} was removed from the actual context and it doesn't handle events

     * anymore.

     */

    void handlerRemoved(ChannelHandlerContext ctx) throws Exception;

 

    /**

     * Gets called if a {@link Throwable} was thrown.

     *

     * @deprecated is part of {@link ChannelInboundHandler}

     */

    @Deprecated

    void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;

 

    /**

     * Indicates that the same instance of the annotated {@link ChannelHandler}

     * can be added to one or more {@link ChannelPipeline}s multiple times

     * without a race condition.

     * <p>

     * If this annotation is not specified, you have to create a new handler

     * instance every time you add it to a pipeline because it has unshared

     * state such as member variables.

     * <p>

     * This annotation is provided for documentation purpose, just like

     * <a href="http://www.javaconcurrencyinpractice.com/annotations/doc/">the JCIP annotations</a>.

     */

    @Inherited

    @Documented

    @Target(ElementType.TYPE)

    @Retention(RetentionPolicy.RUNTIME)

    @interface Sharable {

        // no value

    }

}

6、Netty组件总结

(1)NioEventLoop是最重要的组件,它起着两种作用,它会起两个线程,一个监听新连接线程,一个负责读写的线程;

 

(2)Channel封装了数据流读写API,通过Channel API可以完成数据流的读写操作;

 

(3)Pipeline就是在封装在Channel中的逻辑链;

 

(4)ChannelHandler就是可以添加进Pipeline中的单个逻辑块;

 

(5)ByteBuf用于读写,Channel数据流读写就是基于ByteBuf。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值