Netty-服务端channl的创建(源码分析)

服务端启动的流程

创建服务端channel

AbstractBootstrap.java
    private ChannelFuture doBind(final SocketAddress localAddress) {
        /**
         *  initAndRegister():创建服务端的channel
         */
        final ChannelFuture regFuture = initAndRegister();
        
 final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            /**
             * channelFactory根据反射创建channel,channel类型在启动类的.channel中指定
             */
            channel = channelFactory.newChannel();
            init(channel);
        }

在创建服务端channel的时候都做了哪些事情,可以从NioServerSocketchannel的构造函数中观察:
在这里插入图片描述

  public NioServerSocketChannel() {
        /**
         *  创建jdk底层的socket
         */
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
  public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        /**
         * 创建tcp参数配置类,比如backlog reuseaddr ,专属于单独的服务端或者客户端channel
         */
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
        super(parent);
        this.ch = ch;
        this.readInterestOp = readInterestOp;
        try {
            /**
             * 设置非阻塞模式
             */
            ch.configureBlocking(false);
        } catch (IOException e) {
            try {
                ch.close();
            } catch (IOException e2) {
                logger.warn(
                            "Failed to close a partially initialized socket.", e2);
            }
            throw new ChannelException("Failed to enter non-blocking mode.", e);
        }
    }
  protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        /**
         * pipeline客户端逻辑的相关链
         */
        pipeline = newChannelPipeline();
    }

初始化服务端channel

在这里插入图片描述

set ChildOptions与ChildAttrs:为服务端创建的新连接的channel设定的,每一个新建连接都会设置属性
add ServerBootstrapAcceptor:每一个服务端的pipeline都会有一个ServerBootstrapAcceptor这个特殊的处理器,这个特殊的处理器会为每一个新建立的连接分配一个Nio线程。

初始化流程源码分析
 @Override
    void init(Channel channel) {
        setChannelOptions(channel, newOptionsArray(), logger);
        setAttributes(channel, newAttributesArray());
        ChannelPipeline p = channel.pipeline();

        /**
         * 设置childgroup,相当于在启动类设置的workerGroup
         */
        final EventLoopGroup currentChildGroup = childGroup;
        /**
         * 为每一个客户端连接接入设置的handler处理逻辑
         */
        final ChannelHandler currentChildHandler = childHandler;
        /**
         * 在启动类设置的childOptions与childAttrs
         */
        final Entry<ChannelOption<?>, Object>[] currentChildOptions = newOptionsArray(childOptions);
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs = newAttributesArray(childAttrs);
        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) {
                final ChannelPipeline pipeline = ch.pipeline();
                /**
                 * 添加服务端的handler,服务端也可以自己设置serverbootstrap.handler
                 */
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }
                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

注册selector

服务端channel创建初始化完成之后,然后做的就是注册到事件轮询器当中
在这里插入图片描述

 @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
         
            /**
             * 绑定eventloop(绑定线程),后续对这个channel的读写事件都交由这个线程来做
             */
            AbstractChannel.this.eventLoop = eventLoop;
            if (eventLoop.inEventLoop()) {
                register0(promise);
            } else {
                try {
                    eventLoop.execute(new Runnable() {
                        @Override
                        public void run() {
                        实际注册方法
                            register0(promise);
                        }
                    });
                } catch (Throwable t) {
                    logger.warn(
                            "Force-closing a channel whose registration task was not accepted by an event loop: {}",
                            AbstractChannel.this, t);
                    closeForcibly();
                    closeFuture.setClosed();
                    safeSetFailure(promise, t);
                }
            }
        }
 private void register0(ChannelPromise promise) {
            try {
                // check if the channel is still open as it could be closed in the mean time when the register
                // call was outside of the eventLoop
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                boolean firstRegistration = neverRegistered;
                /**
                 * 调用jdk底层channel进行注册
                 */
                doRegister();
                neverRegistered = false;
                registered = true;

                // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
                // user may already fire events through the pipeline in the ChannelFutureListener.

                /**
                 * 调用回调方法,如果服务端或者客户端的handler重写了channelAdded,则服务端的channelAdded方法会被调用
                 */
                pipeline.invokeHandlerAddedIfNeeded();

                safeSetSuccess(promise);
                /**
                 * 调用回调方法,如果服务端或者客户端的handler重写了channelRegister,则服务端的channelRegister方法会被调用
                 */
                pipeline.fireChannelRegistered();
                // Only fire a channelActive if the channel has never been registered. This prevents firing
                // multiple channel actives if the channel is deregistered and re-registered.
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        // This channel was registered before and autoRead() is set. This means we need to begin read
                        // again so that we process inbound data.
                        //
                        // See https://github.com/netty/netty/issues/4805
                        beginRead();
                    }
                }
            }
  @Override
    protected void doRegister() throws Exception {
        boolean selected = false;
        for (;;) {
            try {
                /**
                 * javachannel():jdk底层的channel
                 * 可以看到这里做的就是将jdk底层的channel注册到selector上面,同时通过一个
                 * attachment将服务端的channel绑定到selector,所以如果selector轮询到jdk底层channel
                 * 有读写事件发生的时候,就可以拿到我们netty的NioServerSocketChannel或者NioSocketChanel
                 * 针对netty的channel做事件传播
                 * 0:代表没有注册任何事件
                 */
                selectionKey = javaChannel().register(eventLoop().unwrappedSelector(), 0, this);
                return;
            } catch (CancelledKeyException e) {
                if (!selected) {
                    // Force the Selector to select now as the "canceled" SelectionKey may still be
                    // cached and not removed because no Select.select(..) operation was called yet.
                    eventLoop().selectNow();
                    selected = true;
                } else {
                    // We forced a select operation on the selector before but the SelectionKey is still cached
                    // for whatever reason. JDK bug ?
                    throw e;
                }
            }
        }
    }

端口绑定

在这里插入图片描述

 @Override
        public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
            assertEventLoop();
            if (!promise.setUncancellable() || !ensureOpen(promise)) {
                return;
            }
            // See: https://github.com/netty/netty/issues/576
            if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
                localAddress instanceof InetSocketAddress &&
                !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
                !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
                // Warn a user about the fact that a non-root user can't receive a
                // broadcast packet on *nix if the socket is bound on non-wildcard address.
                logger.warn(
                        "A non-root user can't receive a broadcast packet if the socket " +
                        "is not bound to a wildcard address; binding to a non-wildcard " +
                        "address (" + localAddress + ") anyway as requested.");
            }
            在绑定之前 isActive()返回的是false
            boolean wasActive = isActive();
            try {
                /**
                 * 调用jdk底层的bind方法进行端口地址绑定
                 */
                doBind(localAddress);
            } catch (Throwable t) {
                safeSetFailure(promise, t);
                closeIfClosed();
                return;
            }
            /**
             * 在没有绑定之前是false,在绑定之后isActive返回就是true了,这时候可以向下传播
             * active事件
             */
            if (!wasActive && isActive()) {
                invokeLater(new Runnable() {
                    @Override
                    public void run() {
                        /**
                         * 传播active事件
                         */
                        pipeline.fireChannelActive();
                    }
                });
            }
            safeSetSuccess(promise);
        }

在绑定端口之后,会调用pipeline.fireChannelActive(),进行事件传播,事件传播从pipeline的head头结点开始向下传播。在头结点的channelActive中会调用 readIfIsAutoRead();最后会传播到AbstractChannel的AbstractUnsafe的beginread方法,这个beginread方法最终会调用到
AbstractNioChannel的dobeginread方法

 @Override
    protected void doBeginRead() throws Exception {
        // Channel.read() or ChannelHandlerContext.read() was called
		回到之前注册jdk底层channel的时候,返回了一个selectionkey,对于服务端channl注册的感兴趣事件为0,
		同时在创建服务端channel的时候,见下面代码,保存的readInterestOp为连接事件
        final SelectionKey selectionKey = this.selectionKey;
        if (!selectionKey.isValid()) {
            return;
        }

        readPending = true;
        如果是服务端chnanel返回的是0
        final int interestOps = selectionKey.interestOps();
        所以下面的代码表示已经绑定成功了,告诉selector要关心一个acceept事件,selector发现如果有新连接
        接入就回交由netty来处理
        if ((interestOps & readInterestOp) == 0) {
            selectionKey.interestOps(interestOps | readInterestOp);
        }
    }
```java
 public NioServerSocketChannel(ServerSocketChannel channel) {
        /**
         * SelectionKey.OP_ACCEPT:相当于注册感兴趣事件为连接事件
         */
        super(null, channel, SelectionKey.OP_ACCEPT); ----super(parent, ch, readInterestOp);
        /**
         * 创建tcp参数配置类,比如backlog reuseaddr ,专属于单独的服务端或者客户端channel
         */
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

之前服务端底层socketchannel注册的ops为0,代表不监听任何事件,这时候端口绑定的时候调用

readIfIsAutoRead,就是将之前绑定到selector事件重新绑定为accept事件,这样有新连接新来,selector就会轮询到一个accept事件,这时候就可以将这个连接较为netty来处理。

## 监听端口
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty-socketio是一个用于构建实时通信应用程序的开源框架。它基于Netty框架,通过使用WebSocket协议来实现异步、高性能的网络通信。Netty-socketio具有以下特点: 1. 强大的异步处理能力:Netty-socketio采用事件驱动的方式处理客户端请求,能够高效地处理大量并发连接。 2. 完善的消息传递机制:Netty-socketio提供了灵活的消息传递方式,支持广播、点对点、房间等不同的消息发送方式,满足不同场景下的通信需求。 3. 多协议支持:Netty-socketio不仅支持WebSocket协议,还支持其他常用的协议,如TCP、HTTP等,便于与现有的系统集成。 4. 可扩展性强:Netty-socketio提供了丰富的拓展接口,用户可以根据自己的需求定制和扩展框架的功能。 5. 易于使用:Netty-socketio提供了简洁的API和丰富的文档,可以快速上手使用,并提供了相应的示例代码,方便开发者学习和理解。 对于客服应用来说,Netty-socketio作为一个实时通信框架,可以用于构建在线客服聊天系统。通过使用Netty-socketio,我们可以实现客户与客服人员之间的实时消息传递,支持文字、图片、文件等多种类型的消息。客户可以通过网页或移动端应用与客服人员进行沟通,实时解决问题,提升用户体验。 Netty-socketio提供了强大的异步处理能力和全双工通信机制,能够处理大量并发连接,并保持连接的稳定性和可靠性。同时,它的多协议支持和可扩展性强的特点,使得我们可以根据自己的业务需求进行定制和拓展,满足不同客服场景下的通信需求。 总之,Netty-socketio作为一个强大的实时通信框架,为客服应用提供了一种高效、稳定的解决方案,帮助企业构建更好的客服系统,并提升客户的满意度。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值