一、 前言

​ 最近在看tomcat connector组件的相关源码,对Nio2的异步回调过程颇有兴趣,平时读源码不读,自己读的时候很多流程都没搞明白,去查网上相关解析讲的给我感觉也不是特别清晰,于是就自己慢慢看源码,以下是我自己的见解,因为开发经验也不多,刚成为社畜不久,有些地方讲错如果有大佬看到也希望能够指正指导。

以下代码基于tomcat8.5版本

二、基本流程

​ 在tomcat的nio2流程下,会有多个Acceptor通过线程池进行管理运行,一个连接请求进来,会先被Acceptor监听

   protected class Acceptor extends AbstractEndpoint.Acceptor {

        @Override
public void run() {
....
// Configure the socket
if (running && !paused) {
// setSocketOptions() will hand the socket off to
// an appropriate processor if successful
if (!setSocketOptions(socket)) { // 监听到socket请求后进入到这里面
closeSocket(socket);
}
} else {
closeSocket(socket);
}
...

进入setSocketOptions()方法

    protected boolean setSocketOptions(AsynchronousSocketChannel socket) {
try {
socketProperties.setProperties(socket);
Nio2Channel channel = nioChannels.pop();
...
Nio2SocketWrapper socketWrapper = new Nio2SocketWrapper(channel, this);
channel.reset(socket, socketWrapper);
...
// 用另外一个线程处理这个socketWrapper(实现了runnable)
return processSocket(socketWrapper, SocketEvent.OPEN_READ, true);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error("",t);
}
// Tell to close the socket
return false;
}

再进入processSocket()方法,sc被提交到了线程池里面处理

继续跟进源码

在workQueue.offer(command)里面可以看到提交到了任务队列里面,等待线程池的线程执行这个任务

看看执行processSocket()时,做了那些事情,这个线程调度最终会执行到Nio2EndPoint里面的doRun()方法:

在doRun()方法里面执行到这行

通过getHandler拿到了AbstactProtocol

再通过后续流程,拿到了Http11Processor来对当前这个socketWrapper进行处理,Http11Processor会调用Nio2SocketWrapper中的read()方法进行处理

注意:Nio2SocketWrapper有个回调方法,这个回调方法会被注册,后续当数据准备好后会调用这个completed()方法来进行数据读取,部分代码如下:

第一次是非回调读,主要是进行注册操作,会经历进入sockerwrapper里面的read()方法再到fillReadBuffer(),并且会在fillReadBuffer()里面进行注册回调操作

先看以下read方法(),这个地方是关键,第一次读和回调读的区别就在下面这行代码,第一次读因为应用层的buffer没有数据,不会返回,会继续执行

会继续执行到fillReadBuffer()方法里面,在这里面进行回调函数的注册,并把数据的读取交到操作系统内核,由内核将数据拷贝到应用层的buffer,再这个执行回调

这是相关的调用栈

跟进源码,会调用到WindowsAsychronusSocketChannel的相关方法,由内核去拷贝数据

数据准备完成后,我这里猜测是底层会调用我们的回调方法,进行后续的读取操作。

数据已经准备到了buffer里面,这时另外启动一个线程执行回调方法,会执行到里面最后一行,processSocket()

然后你会发现,回调的流程和首次进行注册的流程的调用栈基本一致

差别在,read()方法里面,在回调读的时候,会因为nRead>0返回,并进行后续读到数据的处理

最后再把整套逻辑捋一遍:在tomcat的nio2下,会有多个acceptor,通过tommcat的线程池管理,当一个acceptor监听到连接后,将socket包装成一个socketWrapper,再建一个SocketProcessor,丢到线程池里面,另外启动一个线程执行SocketProcessor的run方法,这时候这个acceptor的监听任务就结束,会返回继续监听其他请求。 后面执行run的时候拿到了Http11Processor来对当前这个socketWrapper进行处理,Http11Processor会调用Nio2SocketWrapper中的read()方法进行处理,在这里会进行第一次读数据,因为buffer里面并没有数据,会进行回调函数的注册,并把拷贝数据的任务交到内核去完成。内核完成后执行回调函数,回调函数再去进行第二次读,将数据从buffer里面读出来,并执行后面的操作,至此实现了非阻塞异步读的流程。

核心思想:应用程序是无法直接访问到内核空间的,内核空间涉及到的数据都需要内核将数据拷贝到用户空间。为了解决这个问题,NIO2实际上让应用程序调用读数据操作的时候,告诉内核数据应该拷贝到哪个buffer,以及将回调函数进行注册,告诉内核调用哪个回调函数。之后,内核会在网卡数据到达,产生硬件中断,内核在中断程序里面把数据从网卡拷贝到内核空间,接着做TCP/IP协议层面的数据解包重组,把数据拷贝到应用程序指定的Buffer,最后执行回调函数。

参考资料:《深入拆解Tomcat & Jetty》

tomcat nio2源码分析的更多相关文章

  1. Servlet和Tomcat底层源码分析

    Servlet 源码分析   Servlet 结构图 Servlet 和 ServletConfig 都是顶层接口,而 GenericServlet 实现了这两个顶层接口,然后HttpServlet ...

  2. JavaWeb过滤器Filter(附tomcat部分源码分析)

    过滤器Filter 过滤器通常对一些web资源进行拦截,做完一些处理器再交给下一个过滤器处理,直到所有的过滤器处理器,再调用servlet实例的service方法进行处理.过滤器可以对request进 ...

  3. tomcat8 源码分析 | 组件及启动过程

    tomcat 8 源码分析 ,本文主要讲解tomcat拥有哪些组件,容器,又是如何启动的 推荐访问我的个人网站,排版更好看呦: https://chenmingyu.top/tomcat-source ...

  4. Tomcat源码分析 (八)----- HTTP请求处理过程(一)

    终于进行到Connector的分析阶段了,这也是Tomcat里面最复杂的一块功能了.Connector中文名为连接器,既然是连接器,它肯定会连接某些东西,连接些什么呢? Connector用于接受请求 ...

  5. Java网络编程与NIO详解11:Tomcat中的Connector源码分析(NIO)

    本文转载 https://www.javadoop.com 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.c ...

  6. Servlet容器Tomcat中web.xml中url-pattern的配置详解[附带源码分析]

    目录 前言 现象 源码分析 实战例子 总结 参考资料 前言 今天研究了一下tomcat上web.xml配置文件中url-pattern的问题. 这个问题其实毕业前就困扰着我,当时忙于找工作. 找到工作 ...

  7. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  8. [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat

    概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...

  9. Tomcat源码分析

    前言: 本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, ...

  10. Tomcat源码分析之—具体启动流程分析

    从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...

随机推荐

  1. PTA 21级数据结构与算法实验4—字符串和数组

    目录 7-1 字符串模式匹配(KMP) 7-2 [模板]KMP字符串匹配 7-3 统计子串 7-4 好中缀 7-5 病毒变种 7-6 判断对称矩阵 7-7 三元组顺序表表示的稀疏矩阵转置运算Ⅰ 7-8 ...

  2. Linux 日志收集器:syslog,syslog-ng,rsyslog

    一 参考:https://www.cnblogs.com/zhaoyong631/p/14441090.html 基本上,它们都是相同,它们都允许在中央存储库中记录来自不同类型系统的数据. 但是它们是 ...

  3. 【做题笔记】树形 dp

    luoguP1122 最大子树和 Solve 设计状态 \(dp[i]\) 表示子树 \(i\) 的最大点权和,则有: 当 \(dp[son[i]] > 0\) 时,选以 \(son[i]\) ...

  4. 渗透-02:HTTPS主干-分支和HTTPS传输过程

    一.HTTPS主干-分支 第一层 第一层,是主干的主干,加密通信就是双方都持有一个对称加密的秘钥,然后就可以安全通信了. 问题就是,无论这个最初的秘钥是由客户端传给服务端,还是服务端传给客户端,都是明 ...

  5. tcpdump抓指定端口/ip的数据包

    抓指定端口,这里以7070端口为例 tcpdump -i ens192 port 7070 -s 0 -l -A -w /chris/70.pcap ens192是网卡  抓完ctrl c停止 抓指定 ...

  6. 明白error类型让你更快定位开发报错

    在javascript中,开发遇到的项目报错,很多时候都是通过 Error 这个类来展示的,清楚Error的类型可以更好定位项目中的问题 Error的类型 Error Error是所有其它错误的父类, ...

  7. javascript事件循环机制及面试题详解

    javascript是单线程执行的程序,也就是它只有一条主线,所有的程序都是逐行"排队"执行,在这种情况下可能存在一些问题,比如说setTimeout.ajax等待执行的时间较长, ...

  8. 好用的css3特性-动画和3d变换

    上一篇文章总结了过渡和2D变化,这一篇来总结一下动画和3D变换,动画可用的场景也很多,比如在加载的页面的时候,可以放置一个gif图,也可以自定义小动画来缓解用户等待的焦虑感,比如以下三个小圆圈转圈圈的 ...

  9. Programming abstractions in C阅读笔记: p114-p117

    <Programming Abstractions in C>学习第48天,p114-p117,​总结如下: 一.技术总结 主要通过random number介绍了随机数的相关用法,int ...

  10. CVE-2022-42475-FortiGate-SSLVPN HeapOverflow 学习记录

    前言 之前就想复现这个洞,不过因为环境的问题迟迟没有开工.巧在前一阵子有个师傅来找我讨论劫持 ssl结构体中函数指针时如何确定堆溢出的偏移,同时还他把搭建好了的环境发给了我,因此才有了此文. 如何劫持 ...