如今tomcat源碼量很是大,想讀懂那麼多人寫的代碼很耗時間,這裏記下我學習tomcat的一點筆記java
Tomcat是Web服務器git
幾個月前我本科沒畢業的時候,還不知道web服務器和應用服務器的區別github
用我本身的話描述,本身的程序裏帶jar文件來支持JavaEE組件的是web服務器web
從socket開始bootstrap
我以爲不少中間件源碼讀起來很困難,由於咱們只看到了最終的產品,中間發展的過程以及增長的功能咱們並不清楚.瀏覽器
Tomcat如今的源碼量至關的大,即便讀一些解讀的文章再調試也不容易理解透徹,並且在業務團隊裏也不必死摳每一個技術細節,因此我以爲應該按部就班tomcat
我先寫一個最簡單的Socket,啓動時只要用瀏覽器訪問 localhost:8080 就能夠看到socket 傳來的信息,並能看返回當前系統時間
收到的消息
服務器
返回的結果
多線程
Socket改進app
一個socket雖然很簡單,但咱們能夠明白HTTP協議的原理
若是咱們把它改進一下,input封裝到Request裏,output封裝到response裏,就好理解多了
我在github上找到一個挺有意思的東東
https://github.com/dasanjos/java-WebServer
我稍微改動了一下這個代碼,讓它變得更直觀些
它就是把我上面寫的代碼改爲線程池管理,並把socket經過handler傳給線程,分爲Request和Response,支持了本地文件的讀寫
RequestHandler處理過程
執行效果
雖然和上面的代碼相比不過就是封裝了一下罷了
但線程池把socket監聽請求以及io流的處理給分開了,其實這個過程就是tomcat的connector和container經過HttpProcessor傳遞socket的過程的簡化,Main裏管線程池的代碼咱們能夠封裝到一個叫Connector裏面,有請求就從池子裏取個線程,讓它合成Request和Response,把它上交給.....容器
Classloader原理
其實作到上一步,基本能夠把這坨代碼當webServer跑了,但它不支持servlet,也沒有webapps目錄和Context額,並且咱們都是打war包的,war包裏的那些類tomcat是怎麼調用的?
由於tomcat的classloader是封裝到Context裏面的,這個加載過程很差解釋,我就寫一個簡單的例子說明一下:
這個目錄結構至關簡單,folder下有個編譯好的B類,咱們啓動tomcat而後啓動服務的過程能夠視爲Main類跑時把B類也加載進去
爲了更有說服力,咱們能夠先ps -ef|grep java 肯定 folder不在咱們的classpath裏面
而後VM參數加上-verbose:class 親眼看看B這個class文件是怎麼被加載進去的
在學校的時候配java環境變量,老是要把rt.jar 這堆玩意加到classpath裏,時間久了基本背下來要加哪些包了
Java的classloader分爲 bootstrap classloader,extension classloader和system classloader,其實rt.jar裏的那些java核心類就是bootstrap classloader給加進去的,咱們能夠執行如下這個代碼看看bootstrap classloader都加載了什麼
Tomcat結構
寫了這麼多卻一直沒提tomcat有些三紙無驢的感受,但其實寫到這裏,咱們本身就能寫個很是粗陋的支持servlet的webServer相似物了
我學習tomcat源碼不只是由於好奇,爲了方便定位問題,也是爲了瞭解一點它的設計思想
我畫了個草圖,並按本身當即標註了一下這些組件是幹啥的,大體說明一下tomcat的結構
固然,這裏面還有一些Loader, Pipeline ,Valve ,Repository 這類的小東西我沒標,否則圖就太亂了
由一個Socket忽然變得這麼複雜稍微有點過分不天然,也不要緊,我在萬能的github上又找到一個過分天然的項目
https://github.com/luminocean/Tommycat
這個項目麻雀雖小五臟俱全,雖然沒Server.xml配置文件,但tomcat該有的它基本都有,只是容器只有Context和Wrapper,但看懂這個基本就明白tomcat的設計思想了
這個項目下面有個Mushroom的項目,主項目跑起來,會把Mushroom放到Context容器裏,並經過Loader加載裏面編譯好的class
這裏沒有filter,因此若是訪問的uri直接能找到文件就直接返回文件內容,找不到就會從ContextValve的map裏找加載好的Servlet而後invoke調用
這裏面還有個LifeCycle接口,就是我們常說的生命週期,每一個容器都實現了這個接口裏的方法
我讀源碼的辦法
我是先debug,邊debug邊看涉及到的類結構,由於這是多線程的,單純debug不是很好理解
我把主要的類摳出來,只保留類裏重要的域,再debug一遍就能明白類之間的調用關係