tomcat和servlet

最好先了解一下tomcat的源码解析,再阅读下面的文字。

Context的部署配置文件web.xml的说明  

 一个Context对应于一个Web App,每个Web App是由一个或者多个servlet组成的  当一个Web App被初始化的时候,它将用自己的ClassLoader对象载入“部署配置文件web.xml”中定义的每个servlet类 。
1、 它首先载入在$CATALINA_HOME/conf/web.xml中部署的servlet类 ;

2、然后载入在自己的Web App根目录下的WEB-INF/web.xml中部署的servlet类 

-----所有的webappX都会执行$CATALINA_HOME/conf/web.xml。web.xml中指明了jsp的servlet映射、缺省映射

web.xml文件servlet类有两部分:servlet类定义和servlet映射定义。

每个被载入的servlet类都有一个名字,且被填入该Context的映射表(mapping table)中,和某种URL PATTERN对应 。当该Context获得请求时,将查询mapping table,找到被请求的servlet,并执行以返回请求回应。


Tomcat Server处理一个http请求的过程  

 假设来自客户的请求为:http://localhost:8080/wsota/wsota_index.jsp  
 1) 请求被发送到本机端口8080,被在那里侦听的Coyote HTTP/1.1 Connector获得  
 2) Connector把该请求交给它所在的Service的Engine来处理,并等待来自Engine的回应  
 3) Engine获得请求localhost/wsota/wsota_index.jsp,匹配它所拥有的所有虚拟主机Host  
 4) Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
 5) localhost Host获得请求/wsota/wsota_index.jsp,匹配它所拥有的所有Context  
 6) Host匹配到路径为/wsota的Context(如果匹配不到就把该请求交给路径名为""的Context去处理) 

以上配置在server.xml中

 7) path="/wsota"的Context获得请求/wsota_index.jsp,在它的mapping table中寻找对应的servlet
 8) Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类  
 9) 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法 .......

以上配置在web.xml中

 10)Context把执行完了之后的HttpServletResponse对象返回给Host  
 11)Host把HttpServletResponse对象返回给Engine  
 12)Engine把HttpServletResponse对象返回给Connector  
 13)Connector把HttpServletResponse对象返回给客户browser


Tomcat Server的结构图

分析conf/server.xml:

 <!----------------------------------------------------------------------------------------------->

<!-- 启动Server 在端口8005处等待关闭命令 如果接受到"SHUTDOWN"字符串则关闭服务器 -->

<Server port="8005" shutdown="SHUTDOWN" debug="0">

<!-- Listener ??? 目前没有看到这里 -->

<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" debug="0"/>

<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" debug="0"/>

<!-- Global JNDI resources ??? 目前没有看到这里,先略去 -->

<GlobalNamingResources>

 ... ... ... ...

</GlobalNamingResources>

<!-- Tomcat的Standalone Service Service是一组Connector的集合 它们共用一个Engine来处理所有Connector收到的请求 -->

<Service name="Tomcat-Standalone">

<!-- Coyote HTTP/1.1 Connector
className : 该Connector的实现类是org.apache.coyote.tomcat4.CoyoteConnector
port :在端口号8080处侦听来自客户browser的HTTP1.1请求
minProcessors : 该Connector先创建5个线程等待客户请求,每个请求由一个线程负责
maxProcessors : 当现有的线程不够服务客户请求时,若线程总数不足75个,则创建新线程来处理请求
acceptCount : 当现有线程已经达到最大数75时,为客户请求排队 当队列中请求数超过100时,后来的请求返回Connection refused错误
redirectport : 当客户请求是https时,把该请求转发到端口8443去 其它属性略 -->

<Connector className="org.apache.coyote.tomcat4.CoyoteConnector"

 port="8080"

 minProcessors="5" maxProcessors="75" acceptCount="100"

 enableLookups="true"

 redirectPort="8443"

 debug="0"

 connectionTimeout="20000"

 useURIValidationHack="false"

 disableUploadTimeout="true" />

<!-- Engine用来处理Connector收到的Http请求 它将匹配请求和自己的虚拟主机,并把请求转交给对应的Host来处理默认虚拟主机是localhost -->

<Engine name="Standalone" defaultHost="localhost" debug="0">

<!-- 日志类,目前没有看到,略去先 -->

<Logger className="org.apache.catalina.logger.FileLogger" .../>

<!-- Realm,目前没有看到,略去先 -->

<Realm className="org.apache.catalina.realm.UserDatabaseRealm" .../>

<!-- Host,对应虚拟主机
name:虚拟主机名称localhost
appBase : 该虚拟主机的根目录是webapps/ 。它将匹配请求和自己的Context的路径,并把请求转交给对应的Context来处理 -->

<Host name="localhost" debug="0" appBase="webapps" unpackWARs="true" autoDeploy="true">

<!-- 日志类,目前没有看到,略去先 -->

<Logger className="org.apache.catalina.logger.FileLogger" .../>

<!-- Context,对应于一个Web App
path : 该Context的路径名是"",故该Context是该Host的默认Context
docBase : 该Context的根目录是webapps/mycontext/ -->

<Context path="" docBase="mycontext" debug="0"/>

<!-- 另外一个Context,路径名是/wsota -->

<Context path="/wsota" docBase="wsotaProject" debug="0"/>

</Host>

</Engine>

</Service>

</Server>

<!----------------------------------------------------------------------------------------------->

分析conf/web.xml

<!----------------------------------------------------------------------------------------------->

<web-app>

<!-- 概述: 该文件是所有的WEB APP共用的部署配置文件, 每当一个WEB APP被DEPLOY,该文件都将先被处理,然后才是WEB APP自己的/WEB-INF/web.xml -->

<!-- +-------------------------+ -->

<!-- | servlet类定义部分 | -->

<!-- +-------------------------+ -->

<!-- DefaultServlet

当用户的HTTP请求无法匹配任何一个servlet的时候,该servlet被执行

URL PATTERN MAPPING : / -->

<servlet>

<servlet-name>default</servlet-name>

<servlet-class>

org.apache.catalina.servlets.DefaultServlet

</servlet-class>

<init-param>

   <param-name>debug</param-name>

   <param-value>0</param-value>

</init-param>

<init-param>

   <param-name>listings</param-name>

   <param-value>true</param-value>

</init-param>

<load-on-startup>1</load-on-startup>

</servlet>

<!-- InvokerServlet

处理一个WEB APP中的匿名servlet 当一个servlet被编写并编译放入/WEB-INF/classes/中,却没有在/WEB-INF/web.xml中定义的时候,该servlet被调用,把匿名servlet映射成/servlet/ClassName的形式

URL PATTERN MAPPING : /servlet/* -->

<servlet>

   <servlet-name>invoker</servlet-name>

   <servlet-class>org.apache.catalina.servlets.InvokerServlet </servlet-class>

   <init-param>

     <param-name>debug</param-name>

     <param-value>0</param-value>

   </init-param>

   <load-on-startup>2</load-on-startup>

</servlet>

<!-- JspServlet

当请求的是一个JSP页面的时候(*.jsp)该servlet被调用。它是一个JSP编译器,将请求的JSP页面编译成为servlet再执行

URL PATTERN MAPPING : *.jsp -->

<servlet>

  <servlet-name>jsp</servlet-name>

  <servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class>

  <init-param>

     <param-name>logVerbosityLevel</param-name>

     <param-value>WARNING</param-value>

  </init-param>

 <load-on-startup>3</load-on-startup>

</servlet>

<!-- +---------------------------+ -->

<!-- | servlet映射定义部分 | -->

<!-- +---------------------------+ -->

<servlet-mapping>

  <servlet-name>default</servlet-name>

  <url-pattern>/</url-pattern>

</servlet-mapping>

<servlet-mapping>

   <servlet-name>invoker</servlet-name>

   <url-pattern>/servlet/*</url-pattern>

</servlet-mapping>

<servlet-mapping>

  <servlet-name>jsp</servlet-name>

  <url-pattern>*.jsp</url-pattern>

</servlet-mapping>

<!-- +------------------------+ -->

<!-- | 其它部分,略去先 | -->

<!-- +------------------------+ -->

... ... ... ...

</web-app>

<!----------------------------------------------------------------------------------------------->

http://www.ha97.com/4820.html


Tomcat Wrapper组件

http://www.goldendoc.org/2011/06/tomcat_wrapper/

这篇文章非常好,有个小疑问

一些基本概念

1、ServletContext:
作用:表示一个web应用的上下文;可以想象成一个Web应用程序的共享数据区域,该区域保存该Web应用程序的共享数据;
生命周期:每个Web应用程序都对应一个ServletContext,保存在Context中,在Context初始化时创建,Context撤销时销毁;


2、servlet-mapping:
作用:按url将请求匹配到servlet;
匹配过程:1) url按最长context path匹配当前Host下的Context;
2) 余下的将用于匹配该Context下的Servlet;
匹配规则,顺序如下:
1) 精确匹配:如/user/login;必须以/开头,不能以/*结尾;
2) 通配符匹配:如/customers/*,以最长串优先依次匹配;
3) 扩展名匹配:如/user/register.jsp;任何以*开头,以jsp结尾的mapping都会匹配;
4) 默认匹配:上述3种匹配失败后,将匹配到该mapping上;


3、Filter:
作用:Filter使用户可以改变一个request和修改一个response,它不是一个servlet,也不能产生response,它能够在一个request到达servlet之前预处理request,也可以在response离开servlet时处理response。

Tomcat的过滤器主要由Filter、FilterChain组成,FilterChain包含一个Filter数组.当Wrapper执行FilterChain的doFilter(request,response)方法时,FilterChain首先调用第一个Filter的doFilter(request,response,filterchain)方法,当第一个filter做完过滤操作后,它又会调用filterchain的doFilter方法,此时filterchain的当前filter已变为第二个filter,第二个filter又执行dofilter方法,依此类推,直至所有过滤器都执行完毕。


4、Listener:
作用:通过在web.xml中配置<listener>元素,可以在适当的时候收到Tomcat容器的事件通知,从而可以做出相应的响应;
类型:
1) 事件类型:当某事件发生时触发;
a) ServletContextAttributeListener:当ServletContext的属性发生变化(新增、修改、删除)时触发;
b) ServletRequestAttributeListener:当request的属性发生变化(新增、修改、删除)时响应;
c) ServletRequestListener:当请求在经Filter链处理前后时响应;
d) HttpSessionAttributeListener:当session中的属性发生变化(新增、修改、删除)时响应;
2) 生命周期类型:在生命周期中的某个点触发;
a) ServletContextListener:在ServletContext初始化完成和销毁时触发;
b) HttpSessionListener:在session创建和销毁时触发;


重要的数据结构

1、Mapper:
保存Tomcat容器中所有Host, Context, Wrapper及servlet-mapping的映射关系;
2、MappingData:
当前请求经过Mapper后,即决定将该请求交给哪个Host, 哪个Context, 哪个Wrapper来处理;这些数据组成MappingData;


解析部署描述符web.xml

1、初始化Wrapper
当StandardContext初始化时,会解析web.xml文件(参考WebRuleSet);当解析到web-app/servlet标签时会调用StandardContext.createWrapper()方法,从而构造Wrapper组件,并将其作为子节点添加到当前Context下;初始化Wrapper后,会设置与该servlet相关的配置,如servlet-name, servlet-class, init-param, jsp-file, load-on-startup等;这些配置值都是刚才构建出来的Wrapper组件的属性;
2、初始化servlet-mapping
由于servlet-mapping是针对Context的,处理servlet-mapping的工作由Context来完成.
3、初始化filter及filter-mapping
4、初始化listener
当解析到web.xml中的<listener>元素时,会调用Standard.addApplicationListener(String listener)将listener的class信息保存起来,然后在StandardContext启动时,通过listenerStart()方法来初始化listener:实例化先前保存起来的listener,并按事件/生命周期分类保存其实例;
此时StandardContext初始化也快完成了,因此会触发ServletContextListener.contextInitialized()方法;

web.xml 的加载顺序是:ServletContext-> context-param ->listener -> filter -> servlet


Wrapper组件对请求的处理

Wrapper组件对请求的处理,是通过StandardWrapperValve来完成的:
1) 从Wrapper中分配一个servlet实例;
2) 根据当前servlet和requestPath将匹配到的filter加到filter链中;
3) 调用filter chain处理请求和响应;当所有filter执行完,最后才执行servlet.service()方法;


Servlet在tomcat中有两种工作方式

一种是单线程模型,即某一时刻一个Servlet实例的service方法只能被一个线程调用(synchronized (this)),这种模式下,tomcat会为每一servlet类创建一个实例池,有请求过来时都从该实例池中取一个实例来进行处理。

另一种则是非线程模型,即有对某个servlet的请求过来时每次都使用该servlet的同一个实例来进行处理,就是相应的请求线程都使用同一个servlet进行处理,这种模型下会有线程并发执行的问题,在编写servlet的时候要加以注意(让servlet类不要有状态,可以解决吗?)。


Servlet的loadOnStartup

在配置Servlet时,可以指定其loadOnStartup属性,该属性值决定了Servlet在容器启动后是否加载及加载顺序,默认为-1,表示不加载;
StandardContext在加载完所有Servlet配置后,会检查当前Context下的所有Wrapper的loadOnStartup属性值,当值大于0时,通过TreeMap按loadOnStartup从小到大排序后,分别调用每个Wrapper.load()方法来完成Servlet的初始化:
1) 根据servletClass是否是Tomcat内部servlet,决定是用webappClassloader还是用serverClassloader来装载servletClass;
2) 实例化servletClass;
3) 如果servlet是默认采用单实例多线程(原作者这里好像错了,对吧??),则初始化一个servlet pool,用于存放空闲的servlet实例;后面在处理请求时分配servlet则会用到该servlet pool(这是最常用的模式);


--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

http://blog.163.com/qqabc20082006@126/blog/static/2292852520106249123739/

Servlet如何同时处理多个请求?

       Servlet采用多线程来处理多个请求的同时访问。Servlet容器通过线程池来管理维护服务请求。所谓线程池,相当于数据库连接池,实际上是等待执行代码的一组线程,叫做工作者线程。Servlet容器通过一个调度线程来管理工作者线程。

· 当容器收到一个Servlet的访问请求,调度者线程就从线程池中选出一个工作者线程,将用户请求传递给该线程,然后由该线程处理Servlet的service()方法;

· 当这个线程在执行的时候,容器收到一个新的请求,调度者线程再次从线程池中选出一个新的工作者线程;

· 当容器同时收到对同一个Servlet的多个请求时,那么Servlet的service方法将在多线程中并发执行。

注:1.Servlet容器默认采用单实例多线程的方式来处理请求。这样减少了产生Sevlet实例的开销,提升了对请求的响应时间;

       2.对于Tomcat容器来讲,可以在其server.xml中通过<Connector>中设置线程池中的线程数目。

问题:Servlet可不可以采用多实例多线程?或者多实例单线程?


如何开发线程安全的Servlet

       Servlet容器采用多线程来处理请求,提高性能的同时也造成了线程安全问题。要开发线程安全的Servlet应该从一下几个方面进行:

1. 变量的线程安全; 多线程并不共享局部变量,所以我们要尽可能的在Servlet中使用局部变量;

2. 代码块的线程安全; 使用同步块Synchronized,防止可能调用的代码块;但是要注意的是,要尽可能得缩小同步代码的方范围,不要在service方法和响应方法上直接使用同步,这会严重影响性能。

3.  属性的线程安全;ServletContext,HttpSession,ServletRequest对象中属性;

4. 使用同步集合; 使用Vector代替ArrayList,使用HashTable代替HashMap;

5. 不要在Servlet中创建自己的线程来完成某个功能; Servlet本身就是多线程的,如果再创建新的线程,将会导致线程执行复杂化,出现线程安全问题;

6. 在多个Servlet中,对外部对象,比如:文件;进行修改操作一定要加锁,做到互斥访问;

 

SingleThreadMode

javax.servlet.SingleThreadModel接口是一个标识接口,如果一个Servlet实现了这个接口,那Servlet容器将保证在一个时刻仅有一个线程可以在给定的servlet实例的service方法中执行,将其他所有请求进行排队。这就是单线程模式,很明显,它会导致请求排队等待的问题。

如何实现servlet的单线程模式?

实现方法:<%@ page isThreadSafe="false" %>



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章