首先摘自我的好友。傻博語錄:人生就是悲劇
上次說到每一個請求會分配給一個Worker處理,而Worker與我們平時寫代碼都是圍繞Servlet寫的,到底又有些什麼差別呢?
worker後面是委託給Handler處理的。
handler處理的時候分配給Http11Processor(Http11Processor被維護在一個先進先出隊列當中)。
而所有的HttpProcessor共享Http11Protocal 的Adapter成員變量,Adapter會調用到Servlet,即所有Handler都用同一個Adapter。
首先請看圖:
講述下每個部分的作用:
JioEndPointer:啓動一個線程監聽socket,並把到來的請求分配給相應的worker
Http11ConnectionHandler: Http協議的基於普通IO的處理方式.
Adapter:Http協議的請求 委託或 適配給 servlet容器處理的適配器
attributes:Http協議的相關屬性設置
其中的對應關係也非常重要:
A,1個worker 必須對應一個 Http11ConnectionHandler 中的Http11ConnectionProcessor(是handler裏面真正處理的Http協議的類)
B,所有的worker都持有同一個Adapter
詳細類圖下圖所示:
我在HttpServlet的service方法裏設置了一個斷點,然後調試了下,可以看一下調用棧。
調用棧很清晰得說明了調用到哪些類和順序:也可以看官方化得時序圖:
http://tomcat.apache.org/tomcat-6.0-doc/architecture/requestProcess/requestProcess.pdf
JIoEndpoint.java
/** * Create (or allocate) and return an available processor for use in * processing a specific HTTP request, if possible. If the maximum * allowed processors have already been created and are in use, return * <code>null</code> instead. */ protected Worker createWorkerThread() { synchronized (workers) { if (workers.size() > 0) { curThreadsBusy++; return workers.pop(); } if ((maxThreads > 0) && (curThreads < maxThreads)) { // 默認maxThreads 爲200 curThreadsBusy++; if (curThreadsBusy == maxThreads) { log.info(sm.getString("endpoint.info.maxThreads", Integer.toString(maxThreads), address, Integer.toString(port))); } return (newWorkerThread()); } else { if (maxThreads < 0) { curThreadsBusy++; return (newWorkerThread()); } else { return (null); } } } } /** * Create and return a new processor suitable for processing HTTP * requests and returning the corresponding responses. */ protected Worker newWorkerThread() { Worker workerThread = new Worker(); workerThread.start(); return (workerThread); }
從代碼來看,如果沒有到達請求最大值,首先會從棧中取得空閒的Worker,或者創建新的Worker。
所以來一個請求,會分配給一個Worker,而Worker做了一些接收Socket,設置一些socket和需要的環境類,然後就交給了Servlet實例,而servlet實例啓動的時候就是對於一個context 中配置的一個servlet只啓動一個實例。
StandardWrapper.java
/** * Load and initialize an instance of this servlet, if there is not already * at least one initialized instance. This can be used, for example, to * load servlets that are marked in the deployment descriptor to be loaded * at server startup time. */ public synchronized Servlet loadServlet() throws ServletException { // Nothing to do if we already have an instance or an instance pool // 初始化servlet的時候,如果發現已經初始化完畢,則不再初始化 if (!singleThreadModel && (instance != null)) return instance;
所以首先servlet不是線程安全的。
其次 HttpSession不是線程安全的。
還有 Context不是現成安全的。
Request是線程安全的。