netty服务端新连接接入的流程分析

由前面的分析可知NioEventLoop的run()方法是一个无限循环,NioEventLoop会不断的调用Selector的select(timeout)方法查询是否有新的IO事件,所以当一个客户端连接进入的时候会被Boss线程select到,故新连接接入流程的入口为Bose线程的select方法。

select(boolean oldWakenUp)方法如下所示(仅保留相关代码):

private void select(boolean oldWakenUp) throws IOException {
 	......
    int selectedKeys = selector.select(timeoutMillis);
	......
    if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
        break;
    }   
    ......
   
}

当客户端发起连接请求时,int selectedKeys = selector.select(timeoutMillis);返回的selectedKeys的值将会从0变为1。从而满足后面selectedKeys != 0 的判断,进而跳出select()方法,执行后续的操作。

select到IO事件后会进入NIOEventLoop的processSelectedKeys()方法,因为默认情况netty会对选择器Selector的SelectionKeys做相关优化(将原先的HashSet通过反射的方式修改为数组),故会执行NIOEventLoop的processSelectedKeysOptimized方法。

NIOEventLoop的processSelectedKeysOptimized()如下所示,因为当前是服务端,所以当前ServerSocketChannel是AbstractNioChannel类的子类,故进而由调用processSelectedKey(SelectionKey k, AbstractNioChannel ch)方法处理

private void processSelectedKeysOptimized() {
    for (int i = 0; i < selectedKeys.size; ++i) {
        final SelectionKey k = selectedKeys.keys[i];
        selectedKeys.keys[i] = null;

        final Object a = k.attachment();

        if (a instanceof AbstractNioChannel) {
            processSelectedKey(k, (AbstractNioChannel) a);
        } else {
            @SuppressWarnings("unchecked")
            NioTask<SelectableChannel> task = (NioTask<SelectableChannel>) a;
            processSelectedKey(k, task);
        }

        if (needsToSelectAgain) {
            selectedKeys.reset(i + 1);

            selectAgain();
            i = -1;
        }
    }
}

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) {
            return;
        }
        if (eventLoop != this || eventLoop == null) {
            return;
        }
        unsafe.close(unsafe.voidPromise());
        return;
    }

    try {
        int readyOps = k.readyOps();
        if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
            int ops = k.interestOps();
            ops &= ~SelectionKey.OP_CONNECT;
            k.interestOps(ops);

            unsafe.finishConnect();
        }

        if ((readyOps & SelectionKey.OP_WRITE) != 0) {
            ch.unsafe().forceFlush();
        }

        if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
            unsafe.read();
        }
    } catch (CancelledKeyException ignored) {
        unsafe.close(unsafe.voidPromise());
    }
}

该方法的主要作用是根据SelectionKey中就绪事件的类型,执行不同的逻辑。因为当前分析的是服务端处理新连接的过程,由下面的代码片段可知,对于SelectionKey.OP_ACCEPT类型的事件,将会调用Unsafe接口的read()方法进行处理,这里实际调用的是AbstractNioMessageChannel的read()方法。

if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
     unsafe.read();
}

AbstractNioMessageChannel的read()方法如下:

read()方法中有三个核心的步骤,即:

int localRead = doReadMessages(readBuf);

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

**pipeline.fireChannelReadComplete();**	
public void read() {
    assert eventLoop().inEventLoop();//断言当前线程为ServerSocketChannel绑定的EventLoop
    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 {
        if (!readPending && !config.isAutoRead()) {
            removeReadOp();
        }
    }
}

doReadMessages(List buf)

@Override
protected int doReadMessages(List<Object> buf) throws Exception {
    SocketChannel ch = SocketUtils.accept(javaChannel());//调用JDK的accpt方法获取一个客户端连接通道
    try {
        if (ch != null) {
            buf.add(new NioSocketChannel(this, ch));//创建一个netty的客户端通道类,并加入到集合中
            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;
}

fireChannelRead(Object msg)方法

该方法会触发Pipeline上所有的ChannelHandler的channelRead方法,最终会触发ServerBootstrapAcceptor类的channelRead方法,该实例是在ServerSocketChannel实例化的时候添加到pipeline的尾部的(ServerBootStrap的init(Channel channel)方法)

@Override
public final ChannelPipeline fireChannelRead(Object msg) {
    AbstractChannelHandlerContext.invokeChannelRead(head, msg);
    return this;
}

接着分析ServerBootstrapAcceptor的channelRead(ChannelHandlerContext ctx, Object msg)方法

该方法主要逻辑为:

1、根据ServerBootStrap中的配置,对客户端通道SocketChannel进行设置

2、在work线程组中选择一个NioEventLoop,并将当前客户连接注册在NioEventLoop的Selector上

当客户端后续再有read事件被触发,则会由当前work线程组中获得的NioEventLoop进行处理

public void channelRead(ChannelHandlerContext ctx, Object msg) {
    final Channel child = (Channel) msg;//客户端连接channel

    child.pipeline().addLast(childHandler);//添加childHandler

    setChannelOptions(child, childOptions, logger);//设置通道的option
	//设置socketChannel的属性
    for (Entry<AttributeKey<?>, Object> e: childAttrs) {
        child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
    }
	//在work线程组里挑选一个EventLoop,并将当前的SocketChannel注册到EventLoop的Selector上
    try {
        childGroup.register(child).addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                if (!future.isSuccess()) {
                    forceClose(child, future.cause());
                }
            }
        });
    } catch (Throwable t) {
        forceClose(child, t);
    }
}

pipeline.fireChannelReadComplete();

pipeline.fireChannelReadComplete();方法同上面fireChannelRead方法的流程类似,将Pipeline中的所有ChannelHandler中的channelReadComplete(ChannelHandlerContext ctx)方法都执行一遍

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty中,可以使用ChannelFutureListener来监听连接的状态,当连接断开时,可以在监听器中进行重连操作。以下是一个简单的示例代码: ```java public class NettyClient { private final String host; private final int port; private final EventLoopGroup group; private final Bootstrap bootstrap; public NettyClient(String host, int port) { this.host = host; this.port = port; this.group = new NioEventLoopGroup(); this.bootstrap = new Bootstrap() .group(group) .channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { // 添加自定义处理器 ch.pipeline().addLast(new MyHandler()); } }); } public void start() { // 连接服务器 ChannelFuture future = bootstrap.connect(host, port); future.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { System.out.println("连接成功"); } else { System.out.println("连接失败"); // 进行重连操作 future.channel().eventLoop().schedule(new Runnable() { @Override public void run() { start(); } }, 1, TimeUnit.SECONDS); } } }); } public void stop() { group.shutdownGracefully(); } public static void main(String[] args) throws InterruptedException { NettyClient client = new NettyClient("localhost", 8888); client.start(); Thread.sleep(3000); client.stop(); } } ``` 在上述代码中,我们在连接状态监听器中进行了重连操作。当连接失败时,我们使用eventLoop的schedule方法在1秒后再次进行连接。这样就可以实现对客户端连接的重连。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值