Tomcat系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html


tomcat可以处理静态资源的请求,也可以通过servlet处理动态资源的请求。处理jsp动态资源时,先通过jasper组件(具体的是JspServlet)将jsp翻译成java源代码并编译成class后运行。需要知道的是,静态资源也一样是通过servlet处理的,只不过它使用的servlet是定义在$catalina_home/conf/web.xml中默认的servlet。本文将详细分析tomcat如何处理客户端请求(并发)以及如何处理动、静态资源。

1.Tomcat组件体系结构

如下两图:上面的图是tomcat组件体系的简图,下面的图是Service组件细化后的图。

其中:

  1. server组件是管理tomcat实例的组件,可以监听一个端口,从此端口上可以远程向该实例发送shutdown关闭命令。
  2. service组件是一个逻辑组件,用于绑定connector和container,有了service表示可以向外提供服务,就像是一般的daemon类服务的service。可以认为一个service就启动一个JVM,更严格地说,一个engine组件才对应一个JVM(定义负载均衡时,jvmRoute就定义在Engine组件上用来标识这个JVM),只不过connector也工作在JVM中。
  3. connector组件是监听组件,它有四个作用:
    • (1).开启监听套接字,监听外界请求,并建立TCP连接;
    • (2).使用protocolHandler解析请求中的协议和端口等信息,如http协议、AJP协议;
    • (3).根据解析到的信息,使用processer将请求数据转发给绑定的Engine;
    • (4).接收响应数据并返回给客户端。
  4. container是容器,它是一类组件,在配置文件(如server.xml)中没有体现出来。它包含4个容器类组件:engine容器、host容器、context容器和wrapper容器。
  5. engine容器用于从connector组件处接收转发过来的请求,然后按照分析的结果将相关参数传递给匹配出的虚拟主机。engine还用于指定默认的虚拟主机。
  6. host容器定义虚拟主机,由于tomcat主要是作为servlet容器的,所以为每个webapp指定了它们的根目录appBase。
  7. context容器主要是根据path和docBase获取一些信息,将结果交给其内的wrapper组件进行处理(它提供wrapper运行的环境,所以它叫上下文context)。一般来说,都采用默认的标准wrapper类,因此在context容器中几乎不会出现wrapper组件。
  8. wrapper容器对应servlet的处理过程。它开启servlet的生命周期,根据context给出的信息以及解析web.xml中的映射关系,负责装载相关的类,初始化servlet对象init()、执行servlet代码service()以及服务结束时servlet对象的销毁destory()。
  9. executor组件为每个Service组件提供线程池,使得Engine可以从线程池中获取线程处理请求,从而实现tomcat的并发处理能力。一定要注意,Executor的线程池大小是为Engine组件设置,而不是为Connector设置的,Connector的线程数量由Connector组件的acceptorThreadCount属性来设置。如果要在配置文件中设置该组件,则必须设置在Connector组件的前面,以便在Connector组件中使用executor属性来引用配置好的Executor组件。如果不显式设置,则采用Connector组件上的默认配置,默认配置如下:
    • (1).maxThreads:最大线程数,默认值200。
    • (2).minSpareThreads:最小空闲线程数,默认值25。
    • (3).maxIdleTime:空闲线程的线程空闲多长时间才会销毁,默认值60000即1分钟。
    • (4).prestartminSpareThreads:是否启动executor时就直接创建等于最小空闲线程数的线程,默认值为false,即只在有连接请求进入时才会创建。

根据上面描述的tomcat组件体系结构,处理请求的大致过程其实很容易推导出来:

Client(request)-->Connector-->Engine-->Host-->Context-->Wrapper(response data)-->Connector(response header)-->Client

2.Tomcat和httpd/nginx在监听和处理请求上的区别

在监听和处理请求上,tomcat和httpd/nginx等服务程序不一样,而且是巨大的区别。因此,在理解处理请求时,万万不可将httpd/nginx的处理模式套在tomcat上。

关于httpd/nginx等服务程序处理连接时的过程,此处仅简单说明以体现它们和tomcat的不同之处,详细内容可参见我另一篇文章:不可不知的socket和TCP连接过程

(1).httpd/nginx等都是监听进程/线程负责监听,当监听到连接请求时,将生成一个新的已连接套接字放进一个称为已连接队列中,然后监听进程/线程继续回去监听。而负责处理请求的工作进程/线程则从该队列中获取已连接套接字并与客户端建立TCP连接,然后与客户端进行通信,包括接收客户端的资源请求数据、构建和响应数据给客户端。

(2).tomcat虽然也将监听和处理请求的工作分别使用不同的组件进行处理,但connector线程监听到请求就直接建立TCP连接,并一直与客户端保持该连接。connector线程会分析请求并将结果转发给与之绑定的Engine组件,Engine线程负责处理请求以及构建响应数据,但Engine组件不会和客户端建立任何连接。Engine的一切数据来源都是Connector,客户端任何一次资源请求都会发送到connector上,并从connector转发给Engine。Engine构建响应后,再次将响应数据转发给Connector,并由Connector做一些处理(如加上首部字段)回复给客户端。

只要明确一点即可推导出tomcat的连接和请求处理机制:任何一次从外界流入的请求都必将经过connector,任何一次从本地流出的响应数据也都必将经过connector。这正是连接器的意义所在──连接客户端和服务端servlet。

2.1 tomcat如何处理并发请求

connector组件支持4种IO协议类型:同步阻塞BIO、同步非阻塞NIO、异步非阻塞NIO2、apache基金会提供的IO模型APR(IO模型只是APR类库的其中一种功能模块)。它们的区别如下图所示,除了BIO,其他IO模型在接受新请求上都是非阻塞的,因此这里不考虑BIO,而且现在也不会有人将connector设置成BIO模式。

该表中,最需要关注的是"Wait for next Request"行,NIO/NIO2/APR都是Non Blocking,这表示正在处理某个请求时不会被阻塞,可以接收额外的请求,这是tomcat实现并发处理请求的关键。

再来看connector组件和并发数量有关的设置选项:

acceptorThreadCount:用于接收连接请求的线程数。默认值为1。多核CPU系统应该增大该值,另外由于长连接的存在,也应该考虑增大该值。
maxThreads:线程池中最多允许存在多少线程用于处理请求。默认值为200。它是最大并发处理的数量,但不影响接收线程接收更多的连接。
maxConnections:服务端允许接收和处理的最大连接数。当达到该值后,操作系统还能继续接收额外acceptCount个的连接请求,但这些连接暂时不会被处理。当Connector类型为BIO模型时的默认值等于maxThread的值,当为NIO/NIO2模型时的默认值为10000,当APR时默认长度为8192。
acceptCount:当所有请求处理线程都处于忙碌状态时,连接请求将进入等待队列,该值设置等待队列的长度。当达到队列最大值后,如果还有新连接请求进入,则会被拒绝。默认队列长度为100。

从上面几个属性的意义来分析并发机制:

  • (1).connector中最多有acceptorThreadCount个专门负责监听、接收连接请求并建立TCP连接的线程,这些线程是非阻塞的(不考虑BIO)。当和某客户端建立TCP连接后,可以继续去监听或者将Engine返回的数据发送给客户端或者处理其它事情。
  • (2).线程池中的最大线程数maxThreads决定了某一刻允许处理的最大并发请求数,这是专门负责处理connector转发过来的请求的线程,可以认为这些线程专门是为Engine组件服务的(因此我将其称之为Engine线程)。注意,maxThreads决定的是某一刻的最大并发处理能力,但不意味着maxThreads数量的线程只能处理maxThreads数量的请求,因为这些Engine线程也是非阻塞的,当处理某个请求时出现IO等待时,它不会阻塞,而是继续处理其它请求。也就是说,每个请求都占用一个Engine线程直到该客户端的所有请求处理完毕,但每个Engine线程可以处理多个请求。同时还能推测出,每个connector线程可以和多个Engine线程绑定(connector线程的数量远少于Engine线程的数量)。
  • (3).当并发请求数量逐渐增多,tomcat处理能力的极限由maxConnector决定,这个值是由maxThreads和acceptorThreadCount以及非阻塞特性同时决定的。由于非阻塞特性,无论是connector线程还是Engine线程,都能不断接收、处理新请求。它的默认值看上去很大(10000或8192),但分配到每个线程上的数量并不大。假设不考虑监听线程对数量的影响,仅从处理线程上来看,10000个连接分配给200个处理线程,每个处理线程可以轮询处理50个请求。和nginx默认的一个worker线程允许1024个连接相比,已经很少了,当然,因为架构模型不一样,它们没有可比性。
  • (4).当并发请求数量继续增大,tomcat还能继续接收acceptCount个请求,但不会去建立连接,所以也不会去处理。实际上,这些请求不是tomcat接收的,而是操作系统接收的,接收后放入到由Connector创建的队列中,当tomcat有线程可以处理新的请求了再去队列中取出并处理。

再来细分一下tomcat和httpd/nginx的不同点:

  • (1).httpd/nginx的监听者只负责监听和产生已连接套接字,不会和客户端直接建立TCP连接。而tomcat的监听者connector线程不仅会监听,还会直接建立TCP连接,且一直处于ESTABLISHED状态直到close
  • (2).httpd/nginx的工作进程/线程首先从已连接套接字队列中获取已连接套接字,并与客户端建立TCP连接,然后和客户端通信兵处理请求、响应数据。而tomcat的工作线程(Engine线程)只接受来自connector转发过来的请求,处理完毕后还会将响应数据转发回connector线程,由connector将响应数据传输给客户端(和客户端的所有通信数据都必须经过连接器connector来传输)
  • (3).不难推断出,一个Connector线程可以和多个客户端建立TCP连接,也可以和多个Engine线程建立绑定关系,而一个Engine线程可以处理多个请求。如果不理解并发处理机制,这一点很容易被"Connector组件和Engine组件绑定在一起组成Service组件"这句话误导。这句话的意思并不是要求它们1:1对应,就像httpd/nginx也一样,一个监听者可能对应多个工作者。

因此,tomcat处理连接的过程如下图所示,其中我把Engine线程处理请求的过程用"Engine+N"来表示,例如Engine线程1下的Engine1表示该Engine线程处理的某个请求,Engine2表示该线程处理的另一个请求。

3.Tomcat处理jsp动态资源的过程

假设tomcat的配置如下,其中项目名称为"xiaofang"。

<Connector connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/>

<Engine name="Catalina" defaultHost="localhost">
<Host name="www.xiaofang.com" appBase="webapps/xiaofang"
unpackWARs="true" autoDeploy="true">
<Context path="" docBase="" reloadable="true" />
<Context path="/xuexi" docBase="xuexi" reloadable="true" />
</Host> <Host name="localhost" appBase="webapps"
unpackWARs="true" autoDeploy="true">
</Host>
</Engine>

当客户端访问http://www.xiaofang.com:8080/xuexi/abc.jsp时,其请求的是$CATALINA_HOME/webapps/xiaofang/xuexi/abc.jsp文件。

(1).Connector组件扮演的角色。

Connector组件首先监听到该请求,于是建立TCP连接,并分析该请求。Connector分析请求的内容包括请求的协议、端口、参数等。因为这里没考虑集群问题,因此只可能是http协议而不可能是ajp协议的请求。分析后,将请求和相关参数转发给关联的Engine组件进行处理。

(2).Engine组件扮演的角色。

Engine组件主要用于将请求分配到匹配成功的虚拟主机上,如果没有能匹配成功的,则分配到默认虚拟主机上。对于上面的请求,很显然将分配到虚拟主机www.xiaofang.com上。

(3).Host组件扮演的角色。

Host组件收到Engine传递过来的请求参数后,将对请求中的uri与Context中的path进行匹配,如果和某个Context匹配成功,则将请求交给该Context处理。如果匹配失败,则交给path=""对应的Context来处理。所以,根据匹配结果,上面的请求将交给<Context path="/xuexi" docBase="xuexi" />进行处理。

注意,这次的uri匹配是根据path进行的匹配,它是目录匹配,不是文件匹配。也就是说,只匹配到uri中的xuexi就结束匹配。之所以要明确说明这一点,是因为后面还有一次文件匹配,用于决定交给哪个Servlet来处理。

(4).Context和Wrapper组件扮演的角色。

到了这里,就算真正到了Servlet程序运行的地方了,相比于前面几个组件,这里的过程也更复杂一些。

请求http://www.xiaofang.com:8080/xuexi/abc.jsp经过Host的uri匹配后,分配给<Context path="/xuexi" docBase="xuexi" />进行处理,此时已经匹配了url中的目录,剩下的是abc.jsp。abc.jsp也需要匹配,但这个匹配是根据web.xml中的配置进行匹配的。

首先,从项目名为xiaofang的私有web.xml中进行查找,即webapps/xiaofang/WEB-INF/web.xml。由于此处仅为简单测试,因此并没有该文件。

于是从全局web.xml即$CATALINA_HOME/conf/web.xml中匹配abc.jsp。以下是web.xml中能匹配到该文件名的配置部分。

<!-- The mappings for the JSP servlet -->
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping> <servlet>
<servlet-name>jsp</servlet-name>
<servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>
</servlet>

首先根据<servlet-mapping>中的url-pattern进行文件匹配,发现该url匹配的是servlet-name为"jsp"的servlet,然后再找到与该名称对应的<servlet>标签段,发现处理该动态资源的类为org.apache.jasper.servlet.JspServlet,于是找到该类对应的class文件,该class文件归档在$catalina_home/lib/jasper.jar中。

JspServlet程序的作用是将jsp文件翻译成java源代码文件,并放在$catalina_home/work目录下。然后将该java源文件进行编译,编译后的class文件也放在work目录下。这个class文件就是abc.jsp最终要执行的servlet小程序。

[root@xuexi ~]# ls /usr/local/tomcat/work/Catalina/www.xiaofang.com/xuexi/org/apache/jsp/
index_jsp.class index_jsp.java new_

在翻译后的servlet小程序中,不仅会输出业务逻辑所需的数据,还会输出html/css代码,这样一来,客户端接收到的数据都将是排版好的。

4.Tomcat处理静态资源的过程

对于tomcat来说,无论是动态还是静态资源,都是经过servlet处理的。只不过处理静态资源的servlet是默认的servlet而已。

在$catalina_home/conf/web.xml中关于静态资源处理的配置如下。

<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping> <servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
</servlet>

需要记住的是,web.xml中的url-pattern是文件匹配,而server.xml中的<Context path="URL-PATTERN" />是目录匹配。

上面web.xml中的<url-pattern>/</url-pattern>表示的是默认servlet。这意味着,当web.xml中没有servlet-mapping能匹配请求url中的路径时,将匹配servlet-name,即名为default的servlet。然后找到处理default的类为org.apache.catalina.servlets.DefaultServlet,该类的class文件归档在$catalina_home/lib/catalina.jar中。该servlet不像JspServlet会翻译jsp文件,它只有最基本的作用:原样输出请求文件中的内容给客户端。

例如,根据前面的配置,下面几个请求都将采用默认servlet进行处理,即当作静态资源处理。

http://www.xiaofang.com:8080/xuexi/index.html
http://www.xiaofang.com:8080/xuexi/abc.js
http://www.xiaofang.com:8080/xuexi/index
http://www.xiaofang.com:8080/xuexi/index.txt

http://www.xiaofang.com:8080/xuexi则不一定,因为tomcat中默认的index文件包含index.jsp和index.html,而index.jsp排在index.html的前面,只有不存在index.jsp时才请求index.html。

Tomcat配置(三):tomcat处理连接的详细过程的更多相关文章

  1. Tomcat(三):tomcat处理连接的详细过程

    Tomcat系列文章:http://www.cnblogs.com/f-ck-need-u/p/7576137.html tomcat可以处理静态资源的请求,也可以通过servlet处理动态资源的请求 ...

  2. 关于Linux系统下基于Tomcat部署和升级war包的详细过程

    1.首先Linux先安装java,Tomcat 中间件规范要求: 1)         软件必须下载到/services/download_soft_v        --------(用xftp上传 ...

  3. HTTPS 建立连接的详细过程

    HTTPS是在HTTP的基础上和ssl/tls证书结合起来的一种协议,保证了传输过程中的安全性,减少了被恶意劫持的可能.很好的解决了解决了http的三个缺点(被监听.被篡改.被伪装) 对称加密和非对称 ...

  4. 安装配置OPENCMS的Replication cluster(从)详细过程

    1.  把opencms.war拷贝到tomcat下的webapps目录,启动tomcat服务. 2.  在安装之前,打开解压缩后的war包目录(tomcat启动后会自动把war包解开),删除目录 $ ...

  5. Tomcat的SSL证书配置以及Tomcat+Nginx实现SSL配置

    把jks上传到java容器在的服务器上,路径只要不是webapps下就可以,然后到conf目录下server.xml里配置 <Connector port=" protocol=&qu ...

  6. 图文解说:Nginx+tomcat配置集群负载均衡

    图文解说:Nginx+tomcat配置集群负载均衡 博客分类: appserver nginxTomcatUbuntuLinux网络应用  作者:niumd Blog:http://ari.iteye ...

  7. Nginx+tomcat配置集群负载均衡

    开发的应用采用F5负载均衡交换机,F5将请求转发给5台hp unix服务器,每台服务器有多个webserver实例,对外提供web服务和socket等接口服务.之初,曾有个小小的疑问为何不采用开源的a ...

  8. apache+tomcat配置负载均衡,实现http与websocket接口分压

    一.应用场景 在生产环境中,应用服务器(此文以tomcat为例)不免面临高访问量的压力,相比而言web服务器(此文以apache为例)能够承担更大的并发量.Apache本身带有一些模块,可以完成对应用 ...

  9. 02 eclipse中配置Web项目(含eclipse基本配置和Tomcat的配置)

    eclipse搭建web项目 一.Eclipse基本配置 找到首选项: (一)配置编码 (二)配置字体 (三)配置jdk (四)配置Tomcat 二.Tomcat配置 三.切换视图,检查Tomcat ...

随机推荐

  1. 卸载CentOS7-x64自带的OpenJDK并安装Sun的JDK7的方法

    第一步:查看并卸载CentOS自带的OpenJDK 安装好的CentOS会自带OpenJdk,用命令 java -version ,会有下面的信息: java version "1.6.0& ...

  2. vue 设计日历表

    日历的功能,我们会经常用到,且逻辑比较复杂,小算法较多,花了半天时间写了个,特此详记. 先贴图 功能阐述:返回本月不多说,设置工作日和节假日是为了公司制度需要,后台会有假日表来记录. 为了适应于vue ...

  3. 【CentOS】阿里云CentOS安装php环境

    本文在介绍安装php环境前,已安装了nginx.mysql. 一.安装 使用国内的搜狐镜像站下载php5.6安装包,执行: wget  http://mirrors.sohu.com/php/php- ...

  4. uboot中 make xxx_config 的作用(以make smdk2410_config为例)

    1.创建到目标板相关文件的链接 ln -s asm-arm asm ln -s arch-s3c24x0 asm-arm/arch ln -s proc-armv asm-arm/proc 2.创建i ...

  5. 第一次写博客,就写如何向外行介绍自己做的是什么,那个我是做web的

    如果想外行问你是做什么的,改如何回答.和内行说java后台就可以了,但外行听不懂,我们该如何描述呢? 我的方法是:我做的是java web开发,不是内外的外,是个英文单词web,全名叫world wi ...

  6. DQN算法

    DQN算法:基础入门看看 # -*- coding: utf-8 -*- import random import gym import numpy as np from collections im ...

  7. Array对象的方法详情

    题外话:从事前端开发有很长一段时间了,一直在不断的扩充各种框架的学习,总觉得要学的东西好多,但是技能并没有得到很大的提升,后发现自己一味去追求的它的广度,并没用去深究其深度,所以决定打算从零开始,从最 ...

  8. BZOJ3676: [Apio2014]回文串(回文树)

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=3676 这叫模版题TAT #include<cstring> #include< ...

  9. codeforces A. Orchestra B. Island Puzzle

    A. Orchestra time limit per test 2 seconds memory limit per test 256 megabytes input standard input ...

  10. android 2048游戏、kotlin应用、跑马灯、动画源码

    Android精选源码 2048游戏源码 android实现获取号码归属地和其他信息诈骗.骚扰 android kotlin仿开眼app源码 android多种reveal动画效果 android K ...