`
zddava
  • 浏览: 243607 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Tomcat NIO源代码分析(一) -- Acceptor

阅读更多
这里主要讲一下Tomcat使用NIO启动和进行请求处理的大致流程,使用的源码版本是7.0.5,对于其他处理等流程就不写了,我在别的文章里已经大致写过了,不过是用的6.0版本:http://zddava.iteye.com/category/53603

当Tomcat配置成使用NIO时,启动过程其实和过去差不多,也是Connector#startInternal -> Protocol(Http11NioProtocol)#start() -> Endpoint(NioEndPoint)#start()的过程,这里主要看一下NioEndPoint:

	public void start() throws Exception {
		// 初始化
		if (!initialized) {
			init();
		}
		if (!running) {
			running = true;
			paused = false;

			// 创建一个ThreadPoolExecutor对象,和JDK里的功能一样,只不过进行了一些扩展
			if (getExecutor() == null) {
				createExecutor();
			}

			// 开启poll的线程
			pollers = new Poller[getPollerThreadCount()];
			for (int i = 0; i < pollers.length; i++) {
				pollers[i] = new Poller();
				Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-" + i);
				pollerThread.setPriority(threadPriority);
				pollerThread.setDaemon(true);
				pollerThread.start();
			}

			// 开启Acceptor的线程
			for (int i = 0; i < acceptorThreadCount; i++) {
				Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
				acceptorThread.setPriority(threadPriority);
				acceptorThread.setDaemon(getDaemon());
				acceptorThread.start();
			}
		}
	}


这里先看一下init()方法,没有全列出来,最主要的一点就是初始化ServerSocketChannel:

	public void init() throws Exception {

		if (initialized)
			return;

		// 初始化ServerSocketChannel,这里用的是阻塞的方式,没有用Selector
		serverSock = ServerSocketChannel.open();
		socketProperties.setProperties(serverSock.socket());
		InetSocketAddress addr = (getAddress() != null ? new InetSocketAddress(getAddress(), getPort())
				: new InetSocketAddress(getPort()));
		serverSock.socket().bind(addr, getBacklog());
		serverSock.configureBlocking(true); // mimic APR behavior
		serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());

		......

	}


Tomcat每种Endpoint的Acceptor线程其实作用都一样,对来访的请求进行最初的处理之用,NioEndpoint的Acceptor也不例外,它内部也只定义一个继承自Runnable的方法:

        public void run() {
            while (running) {
                
                while (paused && running) {
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        // Ignore
                    }
                }

                if (!running) {
                    break;
                }
                try {
		    // 接受请求
                    SocketChannel socket = serverSock.accept();
                    if ( running && (!paused) && socket != null ) {
                    	// 将SocketChannel给pollor处理
                        if (!setSocketOptions(socket)) {
                            try {
                                socket.socket().close();
                                socket.close();
                            } catch (IOException ix) {
                                if (log.isDebugEnabled())
                                    log.debug("", ix);
                            }
                        } 
                    }
                } catch (SocketTimeoutException sx) {
                    //normal condition
                } catch (IOException x) {
                    if (running) {
                        log.error(sm.getString("endpoint.accept.fail"), x);
                    }
                } catch (OutOfMemoryError oom) {
                    try {
                        oomParachuteData = null;
                        releaseCaches();
                        log.error("", oom);
                    }catch ( Throwable oomt ) {
                        try {
                            try {
                                System.err.println(oomParachuteMsg);
                                oomt.printStackTrace();
                            }catch (Throwable letsHopeWeDontGetHere){
                                ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
                            }
                        }catch (Throwable letsHopeWeDontGetHere){
                            ExceptionUtils.handleThrowable(letsHopeWeDontGetHere);
                        }
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("endpoint.accept.fail"), t);
                }
            }
        }
    }


方法其实挺容易理解,就是得到请求用的SocketChannel后交给Poller处理,这里poll是一个UNIX的系统调用名字,Java开发者可以google下,我也是才准备开始啃《UNIX网络编程》,闲言少叙,看一下#setSocketOptions()方法吧:

	protected boolean setSocketOptions(SocketChannel socket) {
		// Process the connection
		try {
			// disable blocking, APR style, we are gonna be polling it
			// 这里终于看到了印象中的NIO的影子了
			socket.configureBlocking(false);
			Socket sock = socket.socket();
			socketProperties.setProperties(sock);

			// NioChannel是ByteChannel的子类
			// 从队列里取出第一个可用的Channel,这样的话NioChannel应该是设计成非GC的
			// 感觉其目的主要是对SocketChannel进行下封装
			NioChannel channel = nioChannels.poll();
			if (channel == null) {
				// 不过这里如果没有可用的就初始化一个的话请求数陡然增高再慢慢回落的时候不就浪费了内存了吗?
				// NioBufferHandler里分别分配了读缓冲区和写缓冲区
				// SSL setup
				if (sslContext != null) {
					SSLEngine engine = createSSLEngine();
					int appbufsize = engine.getSession().getApplicationBufferSize();
					NioBufferHandler bufhandler = new NioBufferHandler(Math.max(appbufsize,
							socketProperties.getAppReadBufSize()), Math.max(appbufsize,
							socketProperties.getAppWriteBufSize()), socketProperties.getDirectBuffer());
					channel = new SecureNioChannel(socket, engine, bufhandler, selectorPool);
				} else {
					// normal tcp setup
					NioBufferHandler bufhandler = new NioBufferHandler(socketProperties.getAppReadBufSize(),
							socketProperties.getAppWriteBufSize(), socketProperties.getDirectBuffer());

					channel = new NioChannel(socket, bufhandler);
				}
			} else {
				// 这里就是对Channel的重用了
				channel.setIOChannel(socket);
				if (channel instanceof SecureNioChannel) {
					SSLEngine engine = createSSLEngine();
					((SecureNioChannel) channel).reset(engine);
				} else {
					channel.reset();
				}
			}
			// 这里就是将SocketChannel注册到Poller了。
			// getPoller0用的循环的方式来返回Poller,即Poller 1, 2, 3... n 然后再回到1, 2, 3....
			getPoller0().register(channel);
		} catch (Throwable t) {
			ExceptionUtils.handleThrowable(t);
			try {
				log.error("", t);
			} catch (Throwable tt) {
				ExceptionUtils.handleThrowable(t);
			}
			// Tell to close the socket
			return false;
		}
		return true;
	}


好了,终于到了Poller了,下一篇开始Poller。
3
0
分享到:
评论

相关推荐

    Tomcat源代码学习研究

    本篇文章将主要围绕“Tomcat源代码学习研究”这一主题,探讨Tomcat的核心概念、设计模式以及关键组件的底层实现原理。 1. **核心概念** - **Servlet**:Servlet是Java提供的一种服务器端编程接口,Tomcat通过...

    tomcat-7.0.90-src-源码

    3. **Jasper**:Jasper是Tomcat中的JSP编译器,负责将JSP文件转换为Java源代码,然后编译成Servlet。这一过程使得JSP能够像普通的Java类一样执行。 深入Tomcat源码,我们可以学习到以下关键知识点: 1. **Web应用...

    tomcat底层原理深入学习

    当JSP文件被请求时,Jasper将其转换为Java源代码,再编译成Servlet,最后由Catalina调用执行。Jasper还支持JSP的预编译,提高应用启动速度。 5. **线程模型与并发** Tomcat采用两种线程模型:基于线程池的BIO...

    tomcat7_source_code_analysis-tomcat source code

    JSP页面在第一次被请求时会被转换为Servlet源代码,然后编译为字节码。Jasper还负责跟踪JSP页面的修改,以便在变化时重新编译。 4. **Coyote处理HTTP连接** Coyote是Tomcat的网络通信模块,负责接收和响应HTTP...

    基于PHP+Mysql实现的酒店客房管理系统

    后端基于PHP+mysql,简单实现了预订、入住、结账以及客户信息维护等功能。年代久远,注释较少,功能简陋,仅供学习交流。

    springboot项目基于Hadoop的高校固定资产管理系统研究与实现_hot.zip

    springboot项目基于Hadoop的高校固定资产管理系统研究与实现_hot,含有完整的源码和报告文档

    基于AlexNet深度学习的11种中草药智能识别系统【python源码+c++ qt5界面+数据集+训练代码】目标识别、深度学习实战

    本文基于AlexNet深度学习模型,通过百度爬取的较少图片,训练了一个进行中草药的识别模型,可用于识别11种不同的中草药类型有:{'曼陀罗': 0, '白花蛇舌草': 1, '芍药': 2, '苍耳': 3, '蒲公英': 4, '薄荷': 5, '藿香': 6, '蛇莓': 7, '金银花': 8, '鸡蛋花': 9, '龙葵': 10}。并基于此模型开发了一款带UI界面的中草药智能识别系统,可用于识别场景中的中草药类别,更方便进行功能的展示。该系统是基于python与c++ QT5开发的,支持图片识别检测。本文提供了完整的Python代码和使用教程,给感兴趣的小伙伴参考学习。

    springboot项目基于协同过滤算法的私人诊所管理系统_to.zip

    springboot项目基于协同过滤算法的私人诊所管理系统_to,含有完整的源码和报告文档

    彩色铅笔形PPT柱形比例图-2.ppt

    图表分类ppt

    立体纸条效果时间轴PPT素材-4.ppt

    图表分类ppt

    Z源逆变器闭环仿真模型,并网,采用L滤波器

    Z源逆变器闭环仿真模型,并网,采用L滤波器。

    西门子PLC1500大型程序 汽车产线fanuc机器人焊装 2台触摸屏TP1500程序 9个智能远程终端ET200SP Profinet连接 15个Festo智能模块Profinet通讯 10台Fan

    西门子PLC1500大型程序 汽车产线fanuc机器人焊装 2台触摸屏TP1500程序 9个智能远程终端ET200SP Profinet连接 15个Festo智能模块Profinet通讯 10台Fanuc发那科机器人Profinet通讯 3台G120变频器Profinet通讯 2台智能电能管理仪表PAC3200 4个GRAPH顺控程序 图尔克RFID总线模组通讯 和MES系统通讯,西门子安全模块 程序经典,结构清晰,SCL算法,堆栈,梯形图 包含需要的GSD文件,博图V14以上版本均可打开,需要博图软件安装完整。 可以用来借鉴学习西门子大型程序的结构思路方法,通讯应用等

    四轴抓取视觉旋转标定源代码,学习机器视觉和运动控制的最佳例子,基于VS2015 C++ 实现,仿雅马哈四轴机械手抓取程序,实现把两个任意摆放的物料通过视觉算法和运动控制指令定位摆放到指定的位置并拼接起

    四轴抓取视觉旋转标定源代码,学习机器视觉和运动控制的最佳例子,基于VS2015 C++ 实现,仿雅马哈四轴机械手抓取程序,实现把两个任意摆放的物料通过视觉算法和运动控制指令定位摆放到指定的位置并拼接起来。 使用研华控制卡搭配工业相机实现,图像算法使用halcon实现,包含界面控制,图像采集,手动控制,图像建模,路径规划,运动仿真动画。 对需要做低成本替代进口机器人的四轴运动视觉方案的朋友具有极高的参考价值。

    花瓣形微立体四项并列PPT模板.pptx

    图表分类ppt

    esp32 can大于8字节分包传输协议,支持ping节点功能

    stm32 esp32 can大于8字节分包传输协议,支持ping节点功能

    php-redis中文帮助手册chm格式最新版本

    《PHP-Redis中文帮助手册》是由我突发灵感所编撰的参考资料。在编撰过程中,部分翻译内容参考了网络上的众多资料。遗憾的是,由于难以追溯,我无法确切知晓某些翻译内容的原始作者。在此,我向所有可能的贡献者表示感谢。 我自知英语水平和对Redis的使用经验都处于中等水平,因此手册中难免存在翻译误解和函数分类错误。我恳请读者们宽容使用此手册。同时,我热切期盼在文档翻译方面有经验的专家能够提供帮助。如果您有更优质的翻译,请不吝赐教,我将及时更新手册内容。我期待与大家进行更深入的交流。

    jsp+servlet+tomcat+mysql的javaee人事管理系统.zip

    ===如资源质量问题,可半价退款,代下全网资源,价格公道==== 【JavaEE人事管理系统详解】 JavaEE是一个广泛应用的企业级开发平台,它由Java SE(标准版)扩展而来,专为构建分布式、多层架构的应用而设计。在这个“jsp+servlet+tomcat+mysql的javaee人事管理系统”中,我们主要探讨四个关键组件:JSP(JavaServer Pages)、Servlet、Tomcat服务器以及MySQL数据库。 1. **JSP**:JavaServer Pages是JavaEE平台的一部分,用于创建动态网页。开发者可以在HTML页面中嵌入Java代码,使得网页能够与服务器进行交互,处理用户请求。在人事管理系统中,JSP通常用于展示数据,如员工信息、部门列表等,并能根据用户的操作(如搜索、添加、编辑、删除员工)进行动态更新。 2. **Servlet**:Servlet是Java编写的服务器端程序,用于扩展Web服务器的功能。在本系统中,Servlet接收来自JSP的请求,执行业务逻辑,例如处理数据验证、计算、数据库操作等,然后将结。内容来源于网络分享,如有侵权请联系我删除。另外如果没有积分的同学需要下载,请私信我。

    新能源plc程序 欧姆龙nj系列程序,注释完整,面向对象的编程方法 ,多轴控制,凸轮同步,设备已经正常使用软件资料出概不 此项目程序已经实际设备成熟稳定应用,程序注释详细、非常适合用来欧姆龙NJ P

    新能源plc程序 欧姆龙nj系列程序,注释完整,面向对象的编程方法 ,多轴控制,凸轮同步,设备已经正常使用软件资料出概不 此项目程序已经实际设备成熟稳定应用,程序注释详细、非常适合用来欧姆龙NJ PLC大型项目学习,包括plc程序程序运用ST语言和FB块,可用作欧姆龙NJ大型项目编程模板直接调用

    Delphi 12 控件之BDE-Installer-for-RAD-Studio-10.2-Tokyo.7z

    BDE_Installer_for_RAD_Studio_10.2_Tokyo.7z

    西南科技大学密码学希尔密码实验

    内容概要:本文档是关于希尔密码加密与解密的实验报告,旨在帮助学生理解和掌握这一经典的密码学技术。通过随机生成密钥矩阵并实施明文的加密与解密步骤,加深对希尔密码运作机制的理解,并通过文件I/O操作巩固数据处理技能。此外,在实验过程中,作者还深入探讨了对合密钥的特点,分析为何这种类型的密钥不适合应用于实际情况,并给出了可能存在的具体对合密钥总数的求解方法。 适用人群:适用于计算机科学专业、信息安全方向的学生或研究人员,特别是那些有兴趣深入了解经典密码系统及其实现细节的人群。 使用场景及目标:本报告不仅作为课程作业的一部分提交,而且也可以作为研究参考资料使用。读者可以通过这份详细的报告学习到希尔密码的具体实现步骤,包括但不限于密钥的选择、密文的创建和恢复。此外,针对实验中存在的局限性进行了讨论,为后续相关领域的进一步探索提供了思考方向。 其他说明:文档详细记录了一个完整的实验流程,涵盖从实验环境搭建、关键步骤演示到最后结果呈现全过程,并就实际遇到的问题给出改进意见。对于初次接触此类概念的学习者来说非常友好。附录中还包括了C语言源码用于检测2*2维度内的所有有效'对合密钥’实例,证明数学推导的结果。

Global site tag (gtag.js) - Google Analytics