首页 > 编程语言 >tomcat nio2源码分析

tomcat nio2源码分析

时间:2023-11-01 21:14:51浏览次数:40  
标签:nio2 socket tomcat 源码 线程 内核 里面 回调 数据

一、 前言

​ 最近在看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被提交到了线程池里面处理

image-20231021175643146

继续跟进源码

image-20231021175748851

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

image-20231021175844968

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

image-20231031194943380

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

image-20231031195038431

通过getHandler拿到了AbstactProtocol

image-20231031195408233

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

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

image-20231031200137181

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

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

image-20231031200455714

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

image-20231031200617723

这是相关的调用栈

image-20231031200603284

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

image-20231031200703481

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

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

image-20231031201142899

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

image-20231031201224388

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

image-20231031200455714

最后再把整套逻辑捋一遍:在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》

标签:nio2,socket,tomcat,源码,线程,内核,里面,回调,数据
From: https://www.cnblogs.com/scottyzh/p/17804093.html

相关文章

  • 花了3个小时解决了和异地女朋友一起看电影的需求(内附源码)
     一、这是一件悲伤的故事我是一名程序员,我和我的女友是异地恋,sosad!!!一次视频时,她突然来了一句:“我们已经很久没有一起看电影了吧。。。。。。”“呃。。。好像是”,毕竟离得那么远,一起看电影有点难啊。女友“哦”了下,我们便陷入了沉默。视频结束,心情有点忧伤。是啊,已经很久没有好好......
  • 三维可视化智慧工地云平台源码,微服务架构模式
    智慧工地是指利用移动互联、物联网、智能算法、地理信息系统、大数据挖掘分析等信息技术,提高项目现场的“人•机•料•法•环•安”等施工要素信息化管理水平,实现工程施工可视化智能管理,并逐步实现绿色生态建造。技术架构:微服务+Java+SpringCloud+UniApp+MySql功能特点:支持多端......
  • 手机App的开发步骤和部分源码
    随着智能手机的广泛普及,手机应用程序已经成为我们日常生活中不可或缺的一部分,从通讯、购物、娱乐到工作,手机App正在改变着我们的生活方式,本文将详细介绍App开发步骤和部分源码,帮助大家更好地了解这一领域。一、准备工作1、确定开发平台:目前主流的手机操作系统包括iOS和Android,开发......
  • JAVA第一课 TomCat部署与启动
    1.启动路径 C:\apache-tomcat-10.1.14\bin\startup.bat2.如出现闪退可到C:\apache-tomcat-10.1.14\logs\catalina.log这个路径查看bat执行的日志启动startup.bat时会闪退,按照其他分享的方法使用命令行来启动bat,但是由于它会在startup.bat里启动另外几个bat文件,所以只有star......
  • 使用Maven+Nexus+Jenkins+Svn+Tomcat+Sonar搭建持续集成环境
    前言   但凡一个略有规模的项目都需要一个持续集成环境的支撑,为什么需要持续集成环境,我们来看一个例子。假如一个项目,由A、B两位程序员来协作开发,A负责前端模块,B负责后端模块,前端依赖后端。A和B都习惯使用SVN作为代码管理工具,他们分别开始工作,一个功能完成后会提交到SVN,以便对......
  • 程序员的硬核浪漫 — 女友专属语聊房(内附源码)
    ​ 人人都说找个程序猿做男朋友就是好,钱多话少over早。额。。。估计小编把文章发布出去后就要被公司的程序猿同胞们疯狂逮捕挨打了。虽然日常生活中大家对程序猿的标签大多是呆板、木讷、不懂浪漫,格子衫牛仔裤和黑框眼镜,整天宥与Coding(脱发),哪怕是红酒蜡烛鲜花围绕,一个告警也......
  • FreeRTOS深入教程(队列内部机制和源码分析)
    (文章目录)前言本篇文章主要来为大家分析队列的内部机制和源码实现。一、队列结构体分析在FreeRTOS中队列会使用一个结构体来表示:1.int8_t*pcHead和int8_t*pcWriteTo:这些指针指向队列存储区的头部和下一个可写入的位置。队列存储区是一个用于存储队列中数据项的缓冲......
  • 直播平台源码,自定义下拉刷新控件
    直播平台源码,自定义下拉刷新控件 importFoundationimportUIKitimportSnapKitclassXRefreshControl:UIRefreshControl{  varobservation:NSKeyValueObservation?  varisLocalRefreshing:Bool=false  letindicator=UIProgressView(progressViewStyle:......
  • LIMS系统源码:从样品登记到检验全面管理实验室流程
    LIMS可用于管理完整的实验程序,从样品登记到检验、校核、审核到最终批准报告,建立在过程质量控制的基础上,对检测流程进行有效全面的管理,对影响质量的人、机、料、法、环因素加以控制,同时为质量改进提供数据依据。LIMS实验室信息管理系统,功能包括以下几个模块:委托管理:样品登记、样品接......
  • Kamailio 源码编译
    Kamailio源码编译一、环境1、OS:Debianbullseye2、版本:Kamailio-5.7.23、MySQL5.7 二、编译1、在Kamailio-5.7.2目录下执行如下指令,生产modules.lst文件makeFLAVOUR=kamailiocfg2、打开modules.lst文件,找到exclude_modules,将等号后面的db_mysql删除。然后执行编译......