主題簡介:html
內核實現原理前端
分佈式集羣java
生產部署關鍵參數web
性能監控和分析瀏覽器
1、內核實現原理緩存
HTTPtomcat
Web服務器與瀏覽器之間以HTTP協議通訊,瀏覽器要訪問服務器即向服務器發送HTTP請求報文。安全
如圖,此處用get方法訪問了localhost的8080端口的Web、Index、JSP,服務器返回200狀態碼並將一些HTTP報文返回到客戶端。服務器
HTTP報文網絡
從圖中能夠看到,HTTP報文中的請求報文和響應報文都由三部分組成。請求報文由請求行、請求頭和請求體三部分組成,其中請求行主要包括method、uri和協議版本;請求頭主要包含kv對;請求體通常以post方法來存放參數;而響應報文則由響應行、響應頭和響應體組成,其中響應行主要包括協議版本和狀態碼;響應頭包含kv對;響應體則包含真正的報文。
HTTPS協議
咱們也能夠把HTTPS當作是HTTP的安全版本,此時它再也不是明文通訊,而是雙方協商出密鑰後對報文進行加密後再通訊。在這過程當中,加密後須要對其進行解密,而後才能進行下一步處理。
HTTPS在TCP/TP協議上層多加了一層SSL/TLS層,因此它能作到對Web應用的透明化。咱們能夠看到,客戶端鏈接服務端後經過必定的步驟來協商肯定密鑰,而Java也已經提供了SSL/TLS協議過程的包,就無需本身再作了。
套接字通訊
你們應該都很熟悉套接字了,那咱們再深刻地探討下服務端套接字的過程:
當應用層new ServerSocket 阻塞等待,操做系統會作一系列操做並監聽客戶端的訪問。而當服務端接收到客戶端鏈接時,就會建立一個socket數據結構並放到隊列中,隨後應用層的accept就會輪詢獲取客戶端socket。
套接字通訊
當客戶端Socket在new Socket後阻塞等待,操做系統會負責發起對服務端的鏈接請求,直到完成三次握手,應用層纔會解除等待。
服務器模型
(1)線程阻塞模式
從單線程阻塞模式來看,共有兩個客戶端請求服務器,其中第二個客戶端必須等到第一個客戶端處理完成後才能開始處理。
多線程阻塞模式一樣也有兩個客戶端請求服務器,但此模式的第二個客戶端不須要等到第一個客戶端處理完,而是兩個客戶端併發的被處理。
在單線程非阻塞模式中,服務器一個線程維護着多個客戶端的請求,該線程不斷遍歷處理全部socket,嘗試讀寫。基於事件監測模式,服務器會告訴操做系統須要關注的事件,接着操做系統負責檢測全部客戶端的鏈接並將檢測到的事件放進兩個列表中,最後,應用層只須要遍歷這兩個列表便可開始處理。
在多線程非阻塞模式中,服務器有多個線程共同負責多個客戶端,客戶端的鏈接會均勻分配給每一個線程管理。
(2)Reactor模式
實際項目中,咱們較經常使用的模式是Reactor模式。Reactor線程負責將客戶端鏈接的不一樣事件分配到不一樣的處理器中進行處理,如accept處理器、read處理器、write 處理器和process處理器。
但事實上,Reactor模式有個不容忽視的缺點,好比處理耗時較長操做的處理器有可能會影響到總體的處理能力,因此須要在process處理器中引入一個線程池,並將比較耗時的操做放到線程池中處理,從而使得Reactor的總體運轉處於正常狀態。
除此以外,還有一種改良的Reactor模式,即若是一個Reactor不夠用,那麼就建立多幾個Reactor來同時處理。以下圖,這裏有兩個Reactor對象,每一個對象都有read處理器、write處理器和process處理器。而客戶端鏈接的分發工做則共同由一個accept處理器完成,再均勻分配到不一樣的Reactor對象中。
總體結構
咱們先來認識Tomcat的總體結構。它的頂層容器爲Server,下面包括service、監聽器和全局資源。Tomcat的主要對象爲Connector(可有多個)和Container,其中每一個Connector對應一個端口,用於處理不一樣的協議。
Container包含了四個級別,分別爲Engine、Host、Context和Wrapper,其中Engine是全局的Servlet引擎,Host是虛擬主機,Context對應web應用,Wrapper則對應web應用中的servlet對象。
請求處理過程
一個完整的請求處理過程是怎樣的呢?如圖,Connector啓動後會由JioEngdpoint 來負責接受客戶端的請求鏈接,並在接收後交由任務池進行處理。該任務池會根據Http11Processor的邏輯(按照HTTP1.1協議)來對請求報文進行解析處理。接着,CoyoteAdapter適配器會適配到對應的servlet來進行業務邏輯處理。這一過程會經歷四個管道,每一個管道可能有若干個閥門,處理後最後將到達Wrapper容器的servlet來處理,並將響應報文返回到客戶端,完成整個請求過程。
Servlet工做機制
這裏主要想說的是Servlet的非線程安全。正常的Servlet只有一個對象,而實現了指定接口的Servlet則會有一個Servlet對象池,該池默認的對象數是20。
前文已經簡單說起Servlet的工做機制,就是經過四個級別的容器,經過管道一層層往下找到請求對應的servlet,執行完邏輯處理後將響應報文返回到客戶端。
實現了SingleThreadModel接口的servlet則會在請求過程當中先從Servlet池中allocate一個對象,使用完後再deallocate 回池裏,給其它線程使用。
根據請求資源的不一樣種類,能夠把Servlet分紅三種類別,好比普通Servlet、JspServlet和DefaultServlet。其中不一樣類別的請求資源會經過Mapper映射到對應類型的Servlet上。
過濾連機制
這一處理過程當中還有過濾連機制,即先經過不一樣的filter,最後纔到servlet中。
Comet模式
客戶端發送一個請求到服務器,服務器接收後就將其註冊到NioChannel隊列中,隨後Poller組件不斷輪詢是否有須要處理的NioChannel。若是有須要處理的NioChannel,那就調用前面實例化的Comet模式Servlet。
這裏主要用到CometProcessor藉口的event方法,Poller會將對應的請求對象、響應對象和事件封裝成CometEvent對象並傳入event方法,隨後執行event方法的邏輯,完成對不一樣事件的處理,從而實現Comet模式。
WebSocket模式
首先,客戶端先發送一個「WebSocker協議升級」的握手包到服務器端;若是服務器端支持WebSocket協議,則會返回一個「升級確認」的握手包。這時就成功創建起了一條可雙向通訊的WebSocket鏈接,可使用WebSocket協議的數據幀格式來發送消息。
當WebSocket的客戶端鏈接被接收器接收並註冊到NioChannel隊列後,Poller組件不斷輪詢是否有須要處理的NioChannel。若是 有,則通過處理管道後進入到繼承了WebSocketServelt的Servelt上。WebSocketServlet的doGet方法會處理WebSocket握手,告知客戶端贊成升級協議。隨後Poller繼續輪詢相關NioChannel,一旦發現使用WebSocket協議的管道,則會調用MessageInbound的相關方法,完成不一樣事件的處理,從而實現對WebSocket協議的支持。
同步Servlet
Servlet在同步狀況下的處理過程,如圖所示。
Tomcat的客戶端請求由管道處理,最後會經過Wrapper容器的管道,這時它會調用Servlet實例的service方法進行邏輯處理,處理完後響應客戶端。整個處理由Tomcat的Executor線程池的線程處理,而線程池的最大線程數是有限制的,因此這個處理過程越短,就能越快地將線程釋放回線程池。但若是Servlet中的處理邏輯耗時越長,就會致使長期地佔用Tomcat的處理線程池,最終影響Tomcat的總體處理能力。
異步Servelt
爲了解決上面的問題,咱們能夠引入支持異步的Servlet,如圖所示。
一樣,當客戶端請求到來時,首先經過管道,而後進入到Wrapper容器的管道,再調用Servlet實例的service後,建立一個異步Servlet將耗時的邏輯操做封裝起來,交給用戶本身定義的線程池。這樣就能夠避免因Servlet中的處理邏輯耗時長而影響Tomcat的總體處理能力。
2、分佈式集羣
爲何要使用集羣?
這主要有兩個緣由:
一是對於一些核心系統要求長期不能中斷服務,爲了提供高可用性咱們須要由多臺機器組成的集羣;
二是隨着訪問量愈來愈大且業務邏輯愈來愈複雜,單臺機器的處理能力已經不足以處理如此多且複雜的邏輯,因而須要增長若干臺機器使整個服務處理能力獲得提高。
集羣難在哪?
若是沒有狀態,那麼作集羣很簡單,直接堆機器便可,請求無論到哪一個節點上都能正確處理。但在有狀態的狀況下,則需在對應節點能獲取該客戶端對應的會話信息後才能正確處理,最簡單的處理方法就是將會話信息放到DB,全部節點都從DB去拿客戶端會話信息。
全節點會話同步模型
全節點會話同步模型可在服務端全部的節點之間分享全部會話信息,而每一個節點都包含了全部客戶端的會話信息,能夠保障服務端能準確獲取到客戶端的會話信息並正確處理。但全節點會話同步模型也可能會引入網絡堵塞的風險。
會話備份單節點模型
一個請求經由Apache分發到Tomcat集羣中某個節點,再生成會話信息。這些會話信息能夠經過必定的備份機制,只將信息都同步在某一個節點上,而不是同步到全部節點,這樣大大減小了網絡開銷,能有效避免網絡阻塞。
生產部署選型
一、較小的應用可直接用Tomcat內置的會話共享方案
此種方案在實際生產上推薦的集羣節點個數爲3-6個,它沒法組建更大的集羣,並且冗餘了大量數據,利用率低。
此種方案在實際生產上推薦的集羣節點個數可達到10個以上。
二、較大的應用通常會把會話剝離出來放到緩存集羣中
Redis
memcached
這二者都有相關的jar包,便於集成。
部署
常見的部署方式以下圖,經過一個負載均衡器拖若干個Tomcat節點,前端不一樣的客戶端經過訪問負載均衡來訪問Tomcat。
反向代理
常見的負載均衡器可分爲軟件和硬件。硬件包括F五、A十、Cisco等,軟件包括Nginx、Apache httpd、Lighttpd、Squid等。
3、生產部署關鍵參數
JVM設置
因爲Tomcat也是運行在JVM上,因此JVM也有一些參數須要設置,加上-server參數,java堆初始化和最大值,默認是1/64物理內存和1/4物理內存,通常不超過物理內存的80%,且這兩個最好設置成同樣,夠用就好,過高會致使浪費內存和GC回收週期長。其它參數以下所示。
通常使用HotSpot JVM
加上-server
-Xms/-Xmx:設置java堆初始化和最大值,默認爲1/64物理內存和1/4物理內存通常不超過物理內存的80%,且這兩個最好設置成同樣,夠用就好,過高會致使浪費內存和GC回收週期長。
-XX:NewSize/-XX:NewRatio:設置成25%-33%java堆總量,過高過低都會致使無效GC。
-XX:PermSize/-XX:MaxPermSize:非堆內內存初始值最大值分別設爲128M,256M。
-XX:+AggressiveOpts:使用最新優化技術。
參考oracle官網
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html,還有其它參數可根據實際配置。
集羣設置
負載均衡用四層仍是七層,根據實際狀況選擇。其中:
四層七層:四層不認識http協議,只按照客戶端ip和port分配流量,但性能好;七層認識http協議,可用http某些頭部分配流量,因爲須要計算,性能相對差點。
鏈接池:負載均衡器到tomcat的鏈接數,通常小於等於tomcat集羣節點處理鏈接能力之和。例如集羣有4個節點,每一個tomcat預計處理500個連接,那麼鏈接池的長鏈接數最大設爲2000。
全節點複製(DeltaManager)模式集羣節點數3-6爲宜。
主備複製(BackupMnagager)模式集羣節點可到10臺以上。
設置
一共有三種模式:
JAVA BIO,最原始最穩定的堵塞模式,也是tomcat7以前的默認模式。它支持較小的併發處理,高併發且短鏈接的處理也能夠爲首選。BIO模式下有一個很是重要的參數:maxThreads,它表示最大同時處理請求書,通常範圍爲200-800,看從400開始根據實際調節。若是是CPU密集型的應用能夠減小,而非CPU密集型的應用能夠增長。
JAVA NIO,是tomcat8的後默認模式,能支持發併發多鏈接處理,屬於非堵塞模式。
Native APR,爲提升性能而使用本地代碼的一種非堵塞模式,由C++編寫,支持更大併發處理。
性能調優是不斷找瓶頸的動態過程,包括:
肯定應用的性能指標
搞清楚應用的系統架構
測試目前應用的性能參數
分析性能問題找到瓶頸
解決優化瓶頸
不斷重複上述幾步直到知足性能指標
分析Connector
Tomcat性能相關因素有不少,通常包括網絡網卡、TCP鏈接參數、HTTP長短鏈接、SSL、BIO&NIO、Connector自身參數、負載均衡的選擇和負載均衡參數等。分析性能瓶頸應該考慮如上多個相關因素。
JVM分析
在JVM分析上,咱們要關注Java堆內存、直接內存、永久代、GC、線程棧、本地代碼和TCP緩存等。
經常使用分析工具
Jmeter 壓測:獲得併發數、TPS、響應時間等
Druid 自帶:SQL耗時、池使用率
JVM自帶:JPS、jinfo、jstat、jmap、jstack等
Linux監控:CPU、內存、磁盤io、網卡、swap等
經常使用工具:top、tail、grep、iotop、iftop等
總體壓測
單個Tomcat壓測並調優後就對整個集羣進行集體壓測,關鍵是看性能可否基本知足線性增加。
-------------推薦閱讀------------
------------------廣告時間----------------
公衆號的菜單已分爲「分佈式」、「機器學習」、「深度學習」、「NLP」、「Java深度」、「Java併發核心」、「JDK源碼」、「Tomcat內核」等,可能有一款適合你的胃口。
鄙人的新書《Tomcat內核設計剖析》已經在京東銷售了,有須要的朋友能夠購買。感謝各位朋友。
歡迎關注: