2、Netty之Client端注册&Connect事件&Read事件的关注

客户端启动流程基本跟server端大同小异。

public class Bootstrap extends AbstractBootstrap<Bootstrap, Channel> {
	private ChannelFuture doResolveAndConnect(SocketAddress remoteAddress,SocketAddress localAddress) {
	    //普通任务队列添加 NioSocketChannel在Selector注册任务,并开启reactor线程不断轮询任务队列
	    ChannelFuture regFuture = initAndRegister();
	    Channel channel = regFuture.channel();
	    if (regFuture.isDone()) {
	        ...
	    } else {
	    	// 线程切换完毕后返回DefaultChannelPromise,并且将selectionKey关注的connect事件封装为
	    	//监听器ChannelFutureListener感兴趣的任务。NioSocketChannel注册过程中由reactor线程触发监听器并完成connect事件的关注
	        PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
	        regFuture.addListener(new ChannelFutureListener() {
	            @Override
	            public void operationComplete(ChannelFuture future) throws Exception {
	                promise.registered();
	                // 同样是将connect事件添加到普通任务队列中
                    doResolveAndConnect0(channel, remoteAddress, localAddress, promise);
	            }
	        });
	        return promise;
	    }
	}
}
public abstract class AbstractChannel extends DefaultAttributeMap implements Channel {
	protected abstract class AbstractUnsafe implements Unsafe {
		private void register0(ChannelPromise promise) {//main线程切换为某个reactor线程
		    ...
		    boolean firstRegistration = neverRegistered;
		    doRegister();//NioSocketChannel完成在Selector注册
		    neverRegistered = false;
		    registered = true;
		    // 将用户自定义ChannelHandler添加到NioSocketChannel中的pipeline中
		    pipeline.invokeHandlerAddedIfNeeded();
		    //reactor线程触发监听器ChannelFutureListener完成connect事件的关注
		    safeSetSuccess(promise);
		    // 调用ChannelInboundHandler所有子类重写后的方法:channelRegistered
		    pipeline.fireChannelRegistered();
		    if (isActive()) {
		        if (firstRegistration) {
		            pipeline.fireChannelActive();
		        } else if (config().isAutoRead()) {
		            beginRead();
		        }
		    }
		}
	}
}

通过上述得知:启动流程中分别将NioSocketChannel在Selector注册、connect事件绑定都作为任务添加到普通任务队列中,并且在首次注册时唤醒某个reactor线程不断轮询执行队列中任务。

1.reactor线程执行队列任务

public final class NioEventLoop extends SingleThreadEventLoop {
	@Override
	protected void run() {
	    int selectCnt = 0;
	    for (;;) {
	        try {
	            int strategy;
	            try {
	            	// 策略是指:优先从普通队列获取任务,否则返回SELECT【-1】
	                strategy = selectStrategy.calculateStrategy(selectNowSupplier, hasTasks());
	                switch (strategy) {
	                case SelectStrategy.CONTINUE:
	                    continue;
	                case SelectStrategy.BUSY_WAIT:
	                case SelectStrategy.SELECT:
	                	// 获取selector存在的IO事件
	                    strategy = select(curDeadlineNanos);
	                    ...
	                }
	            } 
	            //伪代码
	            if (strategy > 0) {
                     processSelectedKeys();// 处理IO事件
                 }
                 long ioTime = System.nanoTime() - ioStartTime;
                 // 处理队列中普通任务
                 ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                 ...
	        } 
	       ...
	    }
	}
	
	private int select(long deadlineNanos) throws IOException {
        if (deadlineNanos == NONE) {
            return selector.select();
        }
        // Timeout will only be 0 if deadline is within 5 microsecs
        long timeoutMillis = deadlineToDelayNanos(deadlineNanos + 995000L) / 1000000L;
        // selector.selectNow():非阻塞方式,如果没有相应的IO事件则返回0
        return timeoutMillis <= 0 ? selector.selectNow() : selector.select(timeoutMillis);
    }
}

1.1.执行队列普通任务

public abstract class SingleThreadEventExecutor extends AbstractScheduledEventExecutor{
	protected boolean runAllTasks(long timeoutNanos) {
		...
	    final long deadline = timeoutNanos > 0 ? ScheduledFutureTask.nanoTime() + timeoutNanos : 0;
	    long runTasks = 0;
	    long lastExecutionTime;
	    for (;;) {
	        safeExecute(task);
	        runTasks ++;
			...
	        task = pollTask();
	        if (task == null) {
	            break;
	        }
	    }
	    return true;
	}
}

每次处理队列中普通任务都是全部处理完毕后,reactor线程才会退出当前轮询。例如先处理NioSocketChannel在Selector注册,后处理connect事件的绑定。

1.2.执行IO事件

2.NioSocketChannel之Connect事件的绑定

public class NioSocketChannel{
	protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
	    ...
	    boolean success = false;
	    boolean connected = SocketUtils.connect(javaChannel(), remoteAddress);// 跟服务端建立连接~ TCP协议
        if (!connected) {
            selectionKey().interestOps(SelectionKey.OP_CONNECT);//绑定注册 OP_CONNECT = 8 连接事件
        }
        success = true;
        return connected;
	}
}

利用TCP协议客户端与服务端建立连接。此时服务端会在NioServerSocketChannel对应SelectedKey上监测到对应的Acceptor事件。

不理解的现象:当NioSocketChannel绑定连接事件后,在其事件循环组中会监测到该IO事件,区别于服务端绑定Accept事件。通过processSelectedKeys方法处理其上的IO事件时触发channelActive方法。

3.客户端发送数据并NioSocketChannel关注read事件

public final class NioEventLoop extends SingleThreadEventLoop {
	private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
	    AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
	   	...
	   	// 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.
        //NioSocketChannel一旦完成connect事件,就会在SelectorKey注册一个事件,即readyOps = 8
		int readyOps = k.readyOps();
	    if ((readyOps & SelectionKey.OP_CONNECT) != 0) {//正常读写事件发生之前该条件必须成立
	        int ops = k.interestOps();// ops = 8
	        ops &= ~SelectionKey.OP_CONNECT;
	        k.interestOps(ops);//ops = 0 ,关注事件为ops = 0
	        unsafe.finishConnect();//此处会触发channelActive 方法的执行,客户端通过通道发送数据到服务端
	    }
	    // 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();//处理服务端的响应,并回调ChannelInboundHandler所有子类重写后的方法:channelRead
	    }
	}
}

3.1.客户端执行channelActive 方法发送数据

public abstract class AbstractNioChannel{

	public final void finishConnect() {
	    boolean wasActive = isActive();//此时客户端完成connect事件,但是 wasActive = false
        doFinishConnect();// 此处修改connect的状态值为 连接状态。如果此时再执行 isActive 方法则返回true
        fulfillConnectPromise(connectPromise, wasActive);
	}
	
	private void fulfillConnectPromise(ChannelPromise promise, boolean wasActive) {
        ...
        boolean active = isActive();//返回true,表明当前连接可以正常使用
        boolean promiseSet = promise.trySuccess();
        if (!wasActive && active) {// 条件成立
            pipeline().fireChannelActive();
        }
		...
    }
}
public class DefaultChannelPipeline implements ChannelPipeline {
	public final ChannelPipeline fireChannelActive() {
	    AbstractChannelHandlerContext.invokeChannelActive(head);
	    return this;
	}
	
	public void channelActive(ChannelHandlerContext ctx) {
        ctx.fireChannelActive();//负责调用自定义的channelActive方法,客户端发送数据
        readIfIsAutoRead();//数据发送完毕,NioSocketChannel注册read事件
    }
}

3.2.read事件的关注

public abstract class AbstractNioChannel extends AbstractChannel {
	protected void doBeginRead() throws Exception {
	    final SelectionKey selectionKey = this.selectionKey;
	   ...
	    final int interestOps = selectionKey.interestOps();//上述得知,connect事件完成之后,设置的感兴趣的事件ops = 0
	    if ((interestOps & readInterestOp) == 0) {
	        selectionKey.interestOps(interestOps | readInterestOp);
	    }
	}
}

readInterestOp:1,读事件。

至此,NioSocketChannel、NioServerSocketChannel都完成读IO事件的关注。各自reactor线程着重开始处理read等IO事件,完成普通任务队列中任务消费处理。

截止客户端关注read事件为止,整个流程都是reactor线程触发执行的。当然在客户端发送数据时用户可以异步执行,这种情况最终也是被放入任务队列,由reactor线程最终发出。

3、客户端发送数据

尾部 -> 首部方向分别执行管道pipeline中的handler。

abstract class AbstractChannelHandlerContext{
	public ChannelFuture writeAndFlush(Object msg) {
	    return writeAndFlush(msg, newPromise());
	}
	
	private void write(Object msg, boolean flush, ChannelPromise promise) {//flush:true
        ...
        final AbstractChannelHandlerContext next = findContextOutbound(flush ?
                (MASK_WRITE | MASK_FLUSH) : MASK_WRITE);
        final Object m = pipeline.touch(msg, next);
        EventExecutor executor = next.executor();
        if (executor.inEventLoop()) {
            if (flush) {//默认为true
                next.invokeWriteAndFlush(m, promise);
            } else {
                next.invokeWrite(m, promise);
            }
        } else {// 客户端如果是通过自定义线程发送数据,则会将其包装台为WriteTask,最终作为普通任务添加到普通队列任务中。最终
            final WriteTask task = WriteTask.newInstance(next, m, promise, flush);
            if (!safeExecute(executor, task, promise, m, !flush)) {
                task.cancel();
            }
        }
    }
	
	void invokeWriteAndFlush(Object msg, ChannelPromise promise) {
        if (invokeHandler()) {
            invokeWrite0(msg, promise);// 首先执行所有handler的Write方法
            invokeFlush0();// 最后执行所有handler的Flush方法,最终由HeadContext将数据flush到套接字,触发服务端IO事件继续处理
        } else {
            writeAndFlush(msg, promise);
        }
    }
}

findContextOutbound:判断当前handler是否存在对应的方法,即write、flush。【并不是所有handler都实现接口inboundHandler、outboundHandler的所有方法】。

终极猜想:在客户端与服务端交互过程中,客户端Netty内部都是某个具体reactor线程在执行,初始化客户端指定多个线程好像没有啥意义。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值