Netty 接受请求过程源码剖析 2021SC@SDUSC

2021SC@SDUSC

Netty 接受请求过程源码剖析

1 源码剖析目的

  1. 服务器启动后肯定是要接受客户端请求并返回客户端想要的信息的,下面源码分析 Netty 在启动之后是如何接受客户端请求的

  2. 在 io.netty.example 包 下

2 源码剖析

说明:

  1. 从之前服务器启动的源码中,我们得知,服务器最终注册了一个 Accept 事件等待客户端的连接。我们也知道,
    NioServerSocketChannel 将自己注册到了 boss 单例线程池(reactor 线程)上,也就是 EventLoop 。

  2. 先简单说下 EventLoop 的逻辑(后面我们详细讲解 EventLoop)

EventLoop 的作用是一个死循环,而这个循环中做 3 件事情:

  1. 有条件的等待 Nio 事件。
  2. 处理 Nio 事件。
  3. 处理消息队列中的任务。

仍用前面的项目来分析:进入到 NioEventLoop 源码中后,在 private void processSelectedKey(SelectionKey k,AbstractNioChannel ch) 方法开始调试最终我们要分析到 AbstractNioChannel 的 doBeginRead 方法, 当到这个方法时,针对于这个客户端的连接就完成了,接下来就可以监听读事件了

3 源码分析过程


## 1.断点位置 NioEventLoop  的如下方法 processSelectedKey
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) { unsafe.read(); //断点位置
}

## 2.执行 浏览器 http://localhost:8007/, 客户端发出请求
## 3.从的断点我们可以看到, readyOps  是 16 ,也就是 Accept 事件。说明浏览器的请求已经进来了。

## 4.这个 unsafe 是 boss 线程中 NioServerSocketChannelAbstractNioMessageChannel$NioMessageUnsafe 对象。我们进入到 AbstractNioMessageChannel$NioMessageUnsafe 的 read 方法中
## 5.read 方法代码并分析:
	@Override
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());

}
}
method

} 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(...)
//
// See https://github.com/netty/netty/issues/2254 if (!readPending && !config.isAutoRead()) {
removeReadOp();
}
}
}
说明:
1)检查该 eventloop  线程是否是当前线程。assert eventLoop().inEventLoop()
2)执行 doReadMessages  方法,并传入一个 readBuf 变量,这个变量是一个 List,也就是容器。
3)循环容器,执行 pipeline.fireChannelRead(readBuf.get(i));
4)doReadMessages 是读取 boss  线程中的 NioServerSocketChannel 接受到的请求。并把这些请求放进容器,
	一会我们 debug 下 doReadMessages 方法.
5) 循环遍历 容器中的所有请求,调用 pipeline 的 fireChannelRead 方法,用于处理这些接受的请求或者其他事件,在 read 方法中,循环调用 ServerSocket 的 pipeline 的 fireChannelRead 方法, 开始执行 管道中的
handler 的 ChannelRead 方法(debug 进入)	

## 6.追踪一下 doReadMessages 方法,  就可以看得更清晰
protected int doReadMessages(List<Object> buf) throws Exception { SocketChannel ch = SocketUtils.accept(javaChannel()); buf.add(new NioSocketChannel(this, ch));
return 1;
}
说明:
1)通过工具类,调用 NioServerSocketChannel 内部封装的 serverSocketChannel 的 accept 方法,这是 Nio  做法。
2)获取到一个 JDK 的 SocketChannel,然后,使用 NioSocketChannel  进行封装。最后添加到容器中
3)这样容器 buf 中就有了 NioSocketChannel [如果有兴趣可以追一下 NioSocketChannel 是如何创建的,我就不追了]

## 7.回到 read 方法,继续分析 循环执行 pipeline.fireChannelRead 方法

1)前面分析 doReadMessages 方法的作用是通过 ServerSocket  的 accept  方法获取到 Tcp 连接,然后封装成
NettyNioSocketChannel  对象。最后添加到 容器中
2)在 read 方法中,循环调用 ServerSocket 的 pipeline 的 fireChannelRead 方法,  开始执行 管道中的 handler
的 ChannelRead  方法(debug 进入)
3)经过 dubug (多次),可以看到会反复执行多个 handler 的 ChannelRead ,我们知道,pipeline 里面又 4 个handler ,分别是 HeadLoggingHandlerServerBootstrapAcceptorTail4)我们重点看看 ServerBootstrapAcceptor。debug 之后,断点会进入到 ServerBootstrapAcceptor 中来。我们来看看 ServerBootstrapAcceptor 的 channelRead  方法(要多次 debug 才可以)
5)channelRead 方法
	public void channelRead(ChannelHandlerContext ctx, Object msg) { final Channel child = (Channel) msg;

child.pipeline().addLast(childHandler);


setChannelOptions(child, childOptions, logger);


for (Entry<AttributeKey<?>, Object> e: childAttrs) { child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
}

try {//将客户端连接注册到 worker 线程池
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);	

	}
}
说明:
1)msg 强转成 Channel  ,实际上就是 NioSocketChannel2)添加 NioSocketChannel 的 pipeline 的 handler ,就是我们 main  方法里面设置的 childHandler  方法里的。
3)设置 NioSocketChannel  的各种属性。
4)将该 NioSocketChannel 注册到 childGroup 中的一个 EventLoop 上,并添加一个监听器。
5)这个 childGroup 就是我们 main 方法创建的数组	workerGroup。	



## 8. 进入 register 方法查看(步步追踪会进入)
	@Override
public final void register(EventLoop eventLoop, final ChannelPromise promise) {
AbstractChannel.this.eventLoop = eventLoop;

if (eventLoop.inEventLoop()) { register0(promise);
} else {
eventLoop.execute(new Runnable() { @Override
public void run() {
register0(promise);//进入到这里	

	}
});
}
}

继续进入到 下面方法, 执行管道中可能存在的任务, 这里我们就不追了	

## 9. 最终会调用 doBeginRead  方法,也就是 AbstractNioChannel 类的方法
	@Override	
	protected void doBeginRead() throws Exception {	
	// Channel.read() or ChannelHandlerContext.read() was called	
	final SelectionKey selectionKey = this.selectionKey; //断点	
	if (!selectionKey.isValid()) {	
	return;	
	}	
	
readPending = true;	
	
final int interestOps = selectionKey.interestOps();	
	if ((interestOps & readInterestOp) == 0) {	
	selectionKey.interestOps(interestOps | readInterestOp);	
	}	
	}	

在这里插入图片描述
1.Netty抽象出两组线程池BossGroup专门负责接收客户端的连接,WorkerGroup专门负责网络的读写
2.BossGroup和,WorkerGroup 对应的类型都是NioEventLoopGroup
3.NioEventLoopGroup相当于一个事件循环组,这个组中含有多个事件循环,每一个事件循环是NioEventLoop
4.NioEventLoop表示一个不断循环的执行处理任务的线程,每个NioEventLoop都有一个selector,用于监听绑定在其上的socket的网络通讯
5.NioEventLoopGroup可以有多个线程,即可以含有多个NioEventLoop
6.每个Boss NioEventLoop循环执行的步骤有三步:

1.轮询accept事件
2.处理accept事件,与client建立连接,生成一个NioSocketChannle,并将其注册到某个worker NioEventLoop上的selector   
3.处理任务队列的任务,即runAllTasks

7.每个worker的NioEventLoop 循环执行的步骤:

1.轮询read write事件
2.处理IO事件,即read ,write,在对应的NioSocketChannel处理
3.处理任务队列的任务,即runAllTasks

8.每个worker的NioEventLoop处理业务时,会使用pipeline(管道),pipeline中包含了channel,即通过pipeline可以获取到对应通道,管道中维护了很多的处理器

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值