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" %>



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