webserver 架構(gòu)闡述
寫在前面
web應(yīng)用服務(wù)器是互聯(lián)網(wǎng)時代最為重要之一的底層支持浩考。它處理相應(yīng)的應(yīng)用訪問請求,并為前端提供相應(yīng)的展示數(shù)據(jù)搭伤。
不同的web應(yīng)用服務(wù)器實現(xiàn)性能不同袜瞬,大型網(wǎng)站服務(wù)器可以每秒處理幾萬到幾十萬的應(yīng)用請求,中小型網(wǎng)站服務(wù)器可能會因為每秒幾千次請求停機拍鲤。
從架構(gòu)的角度上而言,web-server的升級是一個迭代的過程擅这,只有現(xiàn)在的應(yīng)用服務(wù)器無法滿足網(wǎng)站的訪問量景鼠,才會在此之上進行優(yōu)化铛漓。對于一名好的架構(gòu)師而言,落地和防災(zāi)玫坛、可擴展是優(yōu)先需要考慮的相關(guān)事宜包晰。
web應(yīng)用的開發(fā)
首先要說的是軟件開發(fā)是一個確定性的事件,有章可循肠骆,有理可溯塞耕,任何現(xiàn)象都是可以被解釋的莉钙,這是入門級程序員和高級程序員的區(qū)別之處磁玉。
我們以這種思路自頂向下去分析解決問題蚊伞。
以主流的JavaEE為例吮铭,傳統(tǒng)的應(yīng)用開發(fā)兩個較為核心的工作內(nèi)容是:
- 對具體的業(yè)務(wù)進行抽象谓晌,形成系統(tǒng)的應(yīng)用模型,并制定好相關(guān)的接口與數(shù)據(jù)結(jié)構(gòu)喊熟。(此后接口與數(shù)據(jù)結(jié)構(gòu)處于不斷的update的狀態(tài))
- 以某種框架為主芥牌,結(jié)合數(shù)據(jù)庫與其它的中間件聂使、開源庫等岩遗,搭建好項目框架凤瘦。
這可能會涉及持續(xù)化集成梆靖、自動化測試笔诵、測試驅(qū)動開發(fā)概念乎婿。
在這之后,可能還會存在的工作是:
- 業(yè)務(wù)上的編碼谢翎。基于項目框架實現(xiàn)相應(yīng)的業(yè)務(wù)模塊榨婆。
在這個過程中褒侧,可能會涉及封裝、基類闷供、工具類歪脏、反射、泛型的概念唾糯。
從上面可以看出鬼贱,軟件開發(fā)是一件團隊合作的事情。應(yīng)該由不同的人員去從事不同的事情葡秒。傳統(tǒng)項目的分工基本如下(基于個人主觀猜測):
- 業(yè)務(wù)模型的建立(架構(gòu)師)
- 框架的搭建與接口、數(shù)據(jù)結(jié)構(gòu)的定義(架構(gòu)師與高級軟件工程師結(jié)合)
- 類的封裝眯牧、基類的編寫蹋岩、架構(gòu)的分層学少、持續(xù)化集成工具,自動化測試流程扣囊。(架構(gòu)師與高級軟件工程師結(jié)合绒疗,高級工程師為主)
- 理解業(yè)務(wù),增刪改查惕虑。(入門級軟件工程師)
目前比較主流的web應(yīng)用框架是以spring-boot為主的微服務(wù)框架磨镶。對于上面說的三個事情而言,重要的是把其中任何一件事情當(dāng)作一個工程去做酒唉,賦予一個合適的時間周期沸移。 這部分內(nèi)容在預(yù)研過程中非常關(guān)鍵,前期未考慮到的因素后期再修改代價可能為指數(shù)級网沾。
以spring-boot為主蕊爵,結(jié)合mysql搭建web應(yīng)用服務(wù)器的例子github上有很多,在這里不再贅述醋旦。
我的應(yīng)用響應(yīng)時間很慢,可能出現(xiàn)了什么問題?
從客戶端傳遞到服務(wù)器钉凌,響應(yīng)時間由以下三個部分組成:
- 數(shù)據(jù)傳輸時間捂人。包括客戶端送達服務(wù)器的數(shù)據(jù)傳輸時間t1和服務(wù)器送達客戶端的數(shù)據(jù)時間t3.
- 請求處理時間。具體指服務(wù)器接收請求后酸纲,處理所消耗的時間t2.
- 前端顯示時間瑟匆。具體指客戶端獲取數(shù)據(jù)后脓诡,對數(shù)據(jù)進行渲染和展示的時間t4媒役。
當(dāng)出現(xiàn)應(yīng)用響應(yīng)時間過高這個問題時,對于相關(guān)人員交惯,首先需要做的是:
對上面三個部分進行測試穿仪,分析它們分別所消耗的時間,然后再對此進行優(yōu)化只锻。做到有的放矢紫谷,不要四處放槍。
web應(yīng)用的部署 servlet
當(dāng)我們開發(fā)完應(yīng)用程序之后祖驱,該如何進行應(yīng)用的部署呢瞒窒?怎樣的部署才能夠保證服務(wù)器的處理時間較短?
下面我們討論單個tomcatweb應(yīng)用服務(wù)器和多個tomcatweb應(yīng)用服務(wù)器匕坯。
單tomcat web應(yīng)用服務(wù)器
通過spring boot 創(chuàng)建web應(yīng)用有兩種方式:war包與jar包。在本文中以war包為例妻怎。
servlet解析web請求過程:
- web客戶向Servlet容器發(fā)出HTTP請求;
- Servlet容器解析web的HTTP請求.
- Servlet容器創(chuàng)建一個HttpRequest對象,在這個對象中封裝了http請求信息;
- Servlet容器創(chuàng)建一個HttpResponse對象;
- Servlet容器(如果訪問的該servlet不是在服務(wù)器啟動時創(chuàng)建的泞歉,則先創(chuàng)建servlet實例并調(diào)用init()方法初始化對象)調(diào)用HttpServlet的service()方法,把HttpRequest和HttpResponse對象為service方法的參數(shù)傳給HttpServlet對象;
- HttpServlet調(diào)用HttpRequest的有關(guān)方法,獲取HTTP請求信息;
- HttpServlet調(diào)用HttpResponse的有關(guān)方法,生成響應(yīng)數(shù)據(jù);
- Servlet容器把HttpServlet的響應(yīng)結(jié)果傳給web客戶.
tomcat作為servlet容器的一種腰耙,管理著部署的多個web應(yīng)用。tomcat運行架構(gòu)圖如下:
從上圖中可以看出:
- tomcat維護著一個線程池晰赞,一個servlet池选侨。
- 每一個線程池里面有多個線程援制,每一個servlet池中有多個servlet。需要注意的是每個web應(yīng)用只對應(yīng)與一個servlet
- 如果有多個請求到來時晨仑,tomcat會根據(jù)請求數(shù)量創(chuàng)建多個線程洪己,這多個線程同時調(diào)用同一個servlet實例的service()方法,實現(xiàn)并發(fā)處理逝钥。而不是創(chuàng)建多個servlet實例拱镐。
所以由于每個web應(yīng)用只創(chuàng)建了一個servlet實例,所以需要線程安全問題磷箕。(即servlet中包含靜態(tài)變量和成員變量的時候會出現(xiàn)線程安全的問題阵难。應(yīng)該使用局部變量。)
多tomcat web應(yīng)用服務(wù)器
從單個tomcat運行web應(yīng)用中可以看出:
java web通過封裝servlet屏蔽了服務(wù)細節(jié)空繁,使web開發(fā)人員專注與業(yè)務(wù)邏輯的實現(xiàn)。這是j2ee能在web開發(fā)中有一定地位的原因闷祥。
然而傲诵,由于servlet的創(chuàng)建和tomcat 多線程的并發(fā)處理全部交由tomcat來做,在這一個層次程序員無法做太多的事情悟衩,只能對tomcat和jvm進行調(diào)優(yōu)栓拜。
萬幸的是cpu不是系統(tǒng)性能的瓶頸。但是目前有很多的游戲已經(jīng)使用goroutine來實現(xiàn)了挑势。因為golang的協(xié)程可以開上萬個啦鸣,非常適合多線程的處理赏陵。
在一些大型網(wǎng)站中饲漾,對這部分性能調(diào)優(yōu)的解決方案有:
- 提高機器的cpu計算能力,對tomcat和jvm調(diào)優(yōu)吃型。使在同一臺機器上能夠運行盡量多的tomcat線程僚楞,平衡吞吐量與響應(yīng)時間。
- 堆機器赐写,通過提高tomcat的個數(shù)進而提高servlet的個數(shù)和運行的線程數(shù)膜赃。
第二種方案就引入了多tomcat web應(yīng)用服務(wù)器。它的思路是:
- 在多臺機器上搭建tomcat端铛,讓這些tomcat運行同一個web應(yīng)用禾蚕。
- 搭建DNS服務(wù)器,每當(dāng)有用戶請求過來時换淆,首先經(jīng)由DNS服務(wù)器分配給相應(yīng)的tomcat服務(wù)器产舞。
- tomcat服務(wù)器處理http請求。
在這里出現(xiàn)了一個問題耻煤,如何讓DNS服務(wù)器把請求平均分配給合適的tomcat服務(wù)器准颓?
這里引入了LoadBalance的概念。通過負載均衡均勻地分配請求炮赦。
在云計算尚未出現(xiàn)時样勃,負載均衡及容器的維護往往由內(nèi)部的技術(shù)部自行實現(xiàn),在云計算時代剧防,由于K8S和Docker的出現(xiàn)辫樱,使這類問題解決更為容易。
K8S的彈性伸縮鸡挠,把容器進行拷貝復(fù)制搬男,并自動負責(zé)負載均衡缔逛,可以大大簡化其流程溜腐。
ps:在K8S上運行的多個tomcat容器是相同的拷貝瓜喇。
淘寶的例子
開發(fā)發(fā)布流程
開發(fā)機乘寒、項目環(huán)境都是一臺機器,進行開發(fā)伞辛。
開發(fā)完成之后蚤氏,發(fā)布上線,會有發(fā)布機器將代碼拉出來佳恬,然后分批發(fā)布(部署的時候需要把機器關(guān)掉于游,由其他機器繼續(xù)提供服務(wù))。同步到每一臺服務(wù)器倾剿。(持續(xù)集成)
用戶訪問時會
有專門的服務(wù)器進行流量控制和分配蚌成,將該用戶的請求轉(zhuǎn)移到某一臺服務(wù)器上面担忧,由該服務(wù)器提供服務(wù)忌怎。(負載均衡)
web應(yīng)用的數(shù)據(jù)緩存
從傳統(tǒng)的意義上講翘盖,系統(tǒng)的性能瓶頸并不存在于cpu的計算能力螟碎,而在于I/O鹉勒。
所以大型網(wǎng)站架構(gòu)上通常在思考如何降低I/O的時間。
最常用的降低I/O時間是使用reddis和memcached做緩存锯厢,關(guān)于這塊前輩的經(jīng)驗摘引如下:
作者:匿名用戶
鏈接:https://www.zhihu.com/question/27738066/answer/45475986
來源:知乎
著作權(quán)歸作者所有实辑。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處摄乒。
1.首先明確是不是一定要上緩存残黑,當(dāng)前架構(gòu)的瓶頸在哪里,若瓶頸真是數(shù)據(jù)庫操作上拭荤,再繼續(xù)往下看疫诽。
2.明確memcached和redis的區(qū)別,到底要使用哪個歇终。前者終究是個緩存评凝,不可能永久保存數(shù)據(jù)(LRU機制)腺律,支持分布式,后者除了緩存的同時也支持把數(shù)據(jù)持久化到磁盤等翎碑,redis要自己去實現(xiàn)分布式緩存(貌似最新版本的已集成)之斯,自己去實現(xiàn)一致性hash佑刷。因為不知道你們的應(yīng)用場景,不好說一定要用memcache還是redis涨冀,說不定用mongodb會更好麦萤,比如在存儲日志方面扁眯。
3.緩存量大但又不常變化的數(shù)據(jù)姻檀,比如評論涝滴。
4.讀DB前,先讀緩存僵娃,如果有直接返回腋妙,如果沒有再讀DB骤素,然后寫入緩存層并返回。
5.考慮是否需要主從痕檬,讀寫分離送浊,考慮是否分布式部署,考慮是否后續(xù)水平伸縮唁桩。
6.想要一勞永逸荒澡,后續(xù)維護和擴展方便与殃,那就將現(xiàn)有的代碼架構(gòu)優(yōu)化,按你說的替換數(shù)據(jù)庫組件需要改動大量代碼米奸,說明當(dāng)前架構(gòu)存在問題躏升±浅溃可以利用現(xiàn)有的一些框架,比如SpringMVC佃却,將你的應(yīng)用層和業(yè)務(wù)層和數(shù)據(jù)庫層解耦饲帅。再上緩存之前把這些做好瘤泪。
7.把讀取緩存等操作做成服務(wù)組件,對業(yè)務(wù)層提供服務(wù)赦邻,業(yè)務(wù)層對應(yīng)用層提供服務(wù)实檀。
8.保留原始數(shù)據(jù)庫組件膳犹,優(yōu)化成服務(wù)組件,方便后續(xù)業(yè)務(wù)層靈活調(diào)用緩存或者是數(shù)據(jù)庫铐料。
9.不建議一次性全量上緩存豺旬,最開始不動核心業(yè)務(wù)哈垢,可以將邊緣業(yè)務(wù)先換成緩存組件,一步步換至核心業(yè)務(wù)举塔。
10.刷新內(nèi)存求泰,以memcached為例,新增渴频,修改和刪除操作芽丹,一般采用lazy load的策略,即新增時只寫入數(shù)據(jù)庫卜朗,并不會馬上更新Memcached拔第,而是等到再次讀取時才會加載到Memcached中咕村,修改和刪除操作也是更新 數(shù)據(jù)庫,然后將Memcached中的數(shù)據(jù)標(biāo)記為失效蚊俺,等待下次讀取時再加載懈涛。
安全
安全內(nèi)容博大精深,關(guān)于安全方面相關(guān)的一些基本的認知鏈接如下:
另外泳猬,如果對于java 而言批钠,可以使用一個apache的安全框架
shiro
更多
此外還有一些諸如分布式文件存儲得封、加快服務(wù)器腳本運算速度埋心、頁面組件分離等都是提高服務(wù)器響應(yīng)的方法。
tips
在web開發(fā)中忙上,cookie和seesion經(jīng)常用到踩窖。接下來進行簡單的說明。cookie和session主要是用來保存數(shù)據(jù)及狀態(tài)晨横。
cookie 和session 的區(qū)別:
- cookie數(shù)據(jù)存放在客戶的瀏覽器上洋腮,session數(shù)據(jù)放在服務(wù)器上。
- cookie不是很安全手形,別人可以分析存放在本地的COOKIE并進行COOKIE欺騙考慮到安全應(yīng)當(dāng)使用session啥供。
- session會在一定時間內(nèi)保存在服務(wù)器上。當(dāng)訪問增多库糠,會比較占用你服務(wù)器的性能考慮到減輕服務(wù)器性能方面伙狐,應(yīng)當(dāng)使用COOKIE。
- 單個cookie保存的數(shù)據(jù)不能超過4K瞬欧,很多瀏覽器都限制一個站點最多保存20個cookie贷屎。
建議:
- 將登陸信息等重要信息存放為SESSION
- 其他信息如果需要保留,可以放在COOKIE中
cookie和session可以解決跨頁面?zhèn)鬟f數(shù)據(jù)的問題艘虎。
前端跨頁面?zhèn)鬟f數(shù)據(jù)是一個比較繁瑣的問題唉侄,依賴于瀏覽器的架構(gòu)和實現(xiàn)。cookie和session是一種通用的解決方案野建。