元件分離
DNS負載均衡
不同的web內容分佈到不同的伺服器上,並劃分子域,利用DNS將請求自然轉移到不同的伺服器上。主要可以分為兩大內容:
1.動態內容,CPU、IO密集型
2.靜態內容,IO密集型
透過在DNS中配置多個A記錄,將請求轉移到叢集中不同的伺服器,這有助於具有地域性問題的大型web站點上,DNS可以使使用者就近訪問相應的web伺服器。像BIND這樣的DNS服務軟體提供豐富的排程策略。但是,如果叢集中的主機出現故障的話,需要更新DNS快取,這通常需要一定的時間。另外,客戶端也可以透過設定host來繞開DNS排程。
跨域共享cookie:將cookie的範圍擴大到父域。
HTTP重定向
透過使客戶端重定向,來分散和轉移請求壓力,比如一些下載服務通常都有幾個映象伺服器。
分散式快取
無法使用頁面級別快取時,需要考慮直接快取資料,比如使用memcached作為快取。此時,需要考慮併發寫memcached的問題。 另外,當memcached橫向規模擴大,伺服器數量增加時,需要一種對應演算法,能夠使應用程式知道應該連結哪個memcached伺服器(比如,取模運算)。分散式快取能夠自動重建快取,不必擔心down機。
負載均衡
負載均衡就是將請求分散,這涉及到應當如何設計排程策略,以讓叢集發揮最大的效能。當叢集中的主機能力相當時應當儘量平均排程,能力不均時應當能者多勞。隨著問題的複雜,要時刻關注排程的效能,不要讓排程成為效能瓶頸。
反向代理負載均衡
反向代理伺服器工作在HTTP層,類似代理伺服器,與普通的代理伺服器不同的是,伺服器在代理的後端,而不是客戶端在代理的後端,這類似於NAT,只是NAT工作在網路層。同樣是負載均衡,反向代理伺服器強調"轉發"而不是"轉移",因為它不僅要轉發客戶端的請求,還要轉發服務端的響應。可以用作反向代理伺服器的軟體有Nginx、lighttp、Apache,另外目前也有一些專業的代理轉發裝置能夠工作在應用層,例如A10。
黏滯會話:對於啟動session儲存使用者資訊,或者後端伺服器使用動態內容快取的應用,必須將使用者在一段會話中的的請求保持在同一臺伺服器上。代理伺服器一般支援類似的配置。然而,儘量不要使應用過於本地化,比如可以使用cookie儲存使用者資料,或者分散式Session或分散式快取。
IP負載均衡
字面上看,便是利用網路層進行請求轉發,類似NAT閘道器。然而,使用閘道器轉發在頻寬上可能出現瓶頸,因為出口只有一個,所以出口的頻寬要求較高。Linux中的Netfilter模組可以透過iptables的配置。比如:對網路埠8001的請求轉發給內網某臺伺服器,而對網路埠8002的請求轉發給內網另一臺伺服器。這種方式簡單易行,但是無法對排程做太多配置。LVS-NAT同樣是Linux中的在網路層進行轉發的方式,與Netfilter不同,它支援一些動態排程演算法,比如最小連結、帶權重的最小連結、最短期望延遲等。
直接路由
這種方式的工作原理略微複雜:
首先每臺伺服器都需要設定一個IP別名,這個IP別名是面向客戶端的一個虛擬IP,只有代理伺服器對這個IP別名的ARP請求做出響應,這樣客戶端發給這個IP的請求包首先會到代理伺服器。然後代理伺服器將這個請求包的目的MAC地址填寫為實際伺服器的MAC地址(透過某種排程演算法決定目的伺服器),由於目標伺服器也具有這個IP別名,因此,轉發過來的資料包能夠被實際的伺服器接收並處理。最後由於資料包的源IP地址還是客戶端請求的IP地址,因此,實際的伺服器將透過交換機直接將響應包轉發給客戶端而無需透過代理伺服器。
Linux下可以透過LVS-DR實現直接路由方式
IP隧道
共享檔案系統
對於一些簡單的提供檔案下載的服務(包括html中靜態資源等),自然要考慮利用叢集來減壓,但是如何使這些資源在叢集中的主機上同步呢。
NFS
一種方案是讓這些主機從同一個地方取資料。比如採用NFS(Network File System),基於PRC。這種方式簡單易行,但是由於NFS伺服器本身的磁碟吞吐率,或者併發處理能力以及頻寬等問題,往往很有侷限性。
冗餘分發
另一個方案就是在主機上冗餘儲存資源,這樣主機無需訪問共享檔案系統,只需讀取本地磁碟上的資源即可。但是這也帶來了一個同步的問題,如何同步這些資料呢:
主動分發式,還分為單級分發和多級分發,分發可以藉助SCP、SFTP、HTTP擴充套件協議WebDAV
單級分發:透過一次分發,就達到目的,這樣的方案簡單易行,但是效能瓶頸會出現在磁碟壓力和網路頻寬,難以擴充套件
多級分發:透過多次分發,才達到目的地,這樣的方案能夠分散磁碟壓力和網路頻寬壓力,而且容易擴充套件,壞處是成本高
被動同步式容易理解,可以使用rsync,rsync同步時是根據最後更新時間進行判定是否需要同步的條件的,因此,如果一個資料夾中有的檔案數量太多的話,rsync掃描的時間就很長了,可以透過給資料夾設定最後更新時間,併合理的規劃檔案目錄,來加快rsync的掃描時間。即使不使用rsync,自己開發同步程式也可以藉助這樣的思想來提升效能。
分散式檔案系統
分散式檔案系統工作在使用者程序層面上,它是一個管理檔案的平臺,內部維護冗餘,檢索,追蹤、排程等工作,通常包含一個物理層面的組織結構和邏輯層面的組織結構。物理層面的組織結構由分散式檔案系統自行維護,邏輯層面的組織結構面向使用者。其中"追蹤器"起到了關鍵的作用。
MogileFS就是一個開源分散式檔案系統,用Perl編寫,包含追蹤器、儲存節點、管理工具,它使用MySQL分散式檔案系統的所有資訊、使用WebDAV實現檔案複製。其他著名的還有Hadoop。
每個檔案由一個key定義,需要讀取檔案時,指定一個key,追蹤器會返回一個實際的路徑,在訪問這個地址即可獲得檔案。甚至可以將這個key對應的path用分散式快取快取起來,這樣可以減少追蹤器的查詢開銷,但這樣也會失去分散式檔案系統的排程策略的優越性。另外,可以利用支援reproxy的反向代理伺服器(比如:Perlbal)讓路徑重定向的工作由反向代理伺服器完成。
資料庫擴充套件
1.主從複製,讀寫分離
這種方式是指利用資料庫的複製或映象功能,同時在多臺資料庫上儲存相同的資料,並且將讀操作和寫操作分開,寫操作集中在一臺主資料庫上,讀操作集中在多臺從資料庫上,對於讀取比寫更多的站點適合使用這種方式。如果不想在應用程式層面維護這種分離對映,那麼可以使用資料庫反向代理來自動完成對讀寫的分離。
2.垂直分割槽
對於不需要進行聯合查詢的資料表可以分散到不同的資料庫伺服器上,這稱為垂直分割槽;當然每個分割槽自身也可以使用讀寫分離。
3.水平分割槽
將同一個表的記錄拆分到不同的表甚至是伺服器上,稱為水平分割槽,這往往需要一個穩定的演算法來保證讀取時能正確從不同的伺服器上取得資料,比如簡單的對ID取模、範圍劃分、亦或者是儲存對映關係。 也可以使用類似代理的產品spock。
快取
構建高效能web站點時,拋開基礎架構(資料庫分割槽的問題也包括在基礎架構中了),在應用程式、編碼層面主要要考慮的問題就是快取的設計,合理的快取設計可以使提供動態網頁服務的網站效能大幅度提高。當然,在架構階段設計快取解決方案,絕非簡單的技術問題,需要從業務出發,再結合各種技術。下面按照一次HTTP請求的順序,對每個環節的快取設計從技術角度進行討論。
1.客戶端快取
可以利用客戶端瀏覽器的快取機制,來減少瀏覽器對服務端的請求次數(當然在服務端進行圖片等資源合併,並結合css圖片定位技術,也可以減少HTTP請求),利用好HTTP的快取協商,可以設計出靈活的客戶端快取方案。在HTTP頭中下面的內容與快取協商有關:
Last-Modified:動態頁面透過主動推送該值,暗示瀏覽器在下次請求同一個url的時候,優先使用If-Modified-Since值與服務端進行快取協商,如果快取沒有過期,那麼服務端可以不用重新計算動態網頁,透過返回304通知瀏覽器。網站的靜態資源往往使用這種方法。但是該方法有一個缺點:有時,檔案的最後更改時間雖然改了,但是內容卻沒有變,這樣無法充分發揮瀏覽器快取的能力。
ETag:Web伺服器為每個url生成一個雜湊值,增加在HTTP頭的ETag標記中,瀏覽器會優先使用If-None-Match加上這個雜湊值來協商快取過期。透過對靜態檔案的內容進行md5變換,可以生成雜湊值,這樣可以彌補Last-Modified的不足。但是隨之帶來的是服務端md5變換的計算開銷。
Expires:上述兩種方式,雖然可以使服務端多少避免了反覆的動態網頁解析和計算,但瀏覽器還是必須透過HTTP請求來進行協商,並沒有真正意義上減少請求的次數。透過在HTTP頭中新增Expires標記可以明確的告知瀏覽器過期形式,瀏覽器會徹底減少請求的次數。
2.反向代理快取
在web伺服器前端,還有反向代理伺服器快取。反向代理伺服器本質上就是代理伺服器,只是將網路的請求轉發給內網的web伺服器處理,他們都工作在應用層,能夠理解HTTP協議。正向代理伺服器具有HTTP快取、HTTP過濾等功能,反向代理伺服器同樣具有HTTP快取的能力,而且還具備一定程度上的安全性。一切HTTP友好的動態程式同樣能夠很好的在反向代理伺服器上實現快取。重量級的squid、輕量級的varnish、甚至是Nginx這樣的web伺服器軟體,都可以勝任反向代理服務。
上述的代理伺服器軟體產品,透過各種配置可以快取基於HTTP協議的web響應。
3.Web伺服器快取
Web伺服器有可能支援基於url的快取(基於key-value對),這類似反向代理快取。快取通常可以透過配置儲存在記憶體或磁碟上,在快取有效期的問題上,通常是基於HTTP協議中的頭部資訊判斷。但是使用這樣的機制需要注意:
動態程式會可能變得依賴於特定的web伺服器 注意編寫面向HTTP快取友好的動態程式,會使你的動態程式更有生命力 web 伺服器還具有快取檔案描述符(類似控制代碼)的能力,這樣可以減少檔案的open操作,同樣是一種減少系統呼叫的措施,這對於一些小檔案有些效果,因為檔案越小花在open上的開銷將越來越佔有重要的比例。
4.應用程式快取
應用程式本身可以對動態內容進行快取,這可以體現在三個層面:
動態指令碼快取:每次指令碼解析都需要消耗一定的時間,為了加快這個速度,一些伺服器端指令碼語言都支援動態指令碼的預編譯,比如我們熟悉的ASP.NET、JSP都有所謂的中間語言,解析這些中間語言會快很多。
應用程式自身實現快取:應用程式根據特定的業務需求獨立設計快取。
在快取設計的具體技術上有以下幾點:
快取在記憶體,這種方式的優點就是減少了磁碟的讀寫,但是記憶體有限,單機不易擴充套件。
快取在分散式快取,這種方式的優點是減少了磁碟讀寫,並提高了可靠性和擴充套件性,但是由於需要網路IO,效能稍遜於單機快取。
區域性頁面快取,對於一些頁面無法完整快取的,可以考慮區域性快取。
靜態化,將網站靜態化可以極大的提高效能,因為使用者的請求不需要動態指令碼處理。
伺服器系統能力的制約因素:
這部分內容對於所有的伺服器(無論是代理伺服器、web伺服器還是其他),都具有普遍適用的意義。
多程序、多執行緒的選擇和排程:程序切換和執行緒切換都需要一定的系統開銷,通常使用多執行緒模型的web伺服器軟體比使用多程序,具備更優的效能。
系統呼叫:一些需要從使用者模式切換到核心模式的函式呼叫可以稱為系統呼叫,比如:開啟檔案。系統呼叫會有一定程度上的開銷,減少系統呼叫是可以加快處理速度的程式設計細節。
TCP連結保持:可以透過保持TCP連結來減少服務端和客戶端之間的建立和關閉TCP連結的操作。HTTP中的Connection:Keep-Alive就有這樣的功能
IO模型:由於CPU的速度遠遠比IO快,IO延遲往往成為效能瓶頸,因此,IO模型十分重要。
各種IO模型:
PIO:CPU直接干預磁碟和記憶體的資料互動,即無論是資料從記憶體到磁碟還是磁碟到記憶體都要經過CPU暫存器。這樣的模型,可想而知,CPU有很多時間都需要等待慢速裝置。
DMA(Direct Memory Access):CPU透過向DMA控制器傳送指令來控制處理資料,資料處理完之後通知CPU,這可以很大程度上釋放CPU資源。
同步阻塞I/O:對於程序來說,一些系統呼叫為了同步IO,會不同程度上阻塞程序,比如accept、send、read等。
同步非阻塞I/O:對於程序來說,一些系統呼叫可以在呼叫完之後立即返回,告知程序IO是否就緒,避免阻塞程序。
多路I/O就緒通知:對於同步非阻塞I/O的方式,程序仍然需要輪詢檔案描述符(控制代碼)來得知哪些IO就緒了,而多路I/O就緒通知將這個過程改成回撥通知。
記憶體對映:將檔案與記憶體的某塊地址空間相對映,這樣可以想寫記憶體一樣寫檔案。當然這種方式本質上跟寫檔案沒有什麼區別。
直接I/O:在使用者程序地址空間和磁碟中間通常都會有作業系統管轄的核心緩衝區,當寫入檔案時,一般是寫入這個緩衝區,然後由一些延遲策略來寫入磁碟。這樣做可以提高寫效率。但是對於諸如資料庫這樣的應用來說,往往希望自己管理讀寫快取,避免核心緩衝區的無畏記憶體浪費。Linux的open函式支援O_DIRECT引數來進行直接IO。
sendfile:如果web伺服器想傳送一個檔案,將會經歷如下過程:開啟檔案,從磁碟中讀取檔案內容(這通常涉及到核心緩衝區資料複製到使用者程序),然後程序透過socket傳送檔案內容(這通常設計到使用者程序資料複製到網絡卡核心緩衝區),可以看到重複的資料複製是可以避免的。sendfile可以支援直接從檔案核心緩衝區複製到網絡卡核心緩衝區。
元件分離
DNS負載均衡
不同的web內容分佈到不同的伺服器上,並劃分子域,利用DNS將請求自然轉移到不同的伺服器上。主要可以分為兩大內容:
1.動態內容,CPU、IO密集型
2.靜態內容,IO密集型
透過在DNS中配置多個A記錄,將請求轉移到叢集中不同的伺服器,這有助於具有地域性問題的大型web站點上,DNS可以使使用者就近訪問相應的web伺服器。像BIND這樣的DNS服務軟體提供豐富的排程策略。但是,如果叢集中的主機出現故障的話,需要更新DNS快取,這通常需要一定的時間。另外,客戶端也可以透過設定host來繞開DNS排程。
跨域共享cookie:將cookie的範圍擴大到父域。
HTTP重定向
透過使客戶端重定向,來分散和轉移請求壓力,比如一些下載服務通常都有幾個映象伺服器。
分散式快取
無法使用頁面級別快取時,需要考慮直接快取資料,比如使用memcached作為快取。此時,需要考慮併發寫memcached的問題。 另外,當memcached橫向規模擴大,伺服器數量增加時,需要一種對應演算法,能夠使應用程式知道應該連結哪個memcached伺服器(比如,取模運算)。分散式快取能夠自動重建快取,不必擔心down機。
負載均衡
負載均衡就是將請求分散,這涉及到應當如何設計排程策略,以讓叢集發揮最大的效能。當叢集中的主機能力相當時應當儘量平均排程,能力不均時應當能者多勞。隨著問題的複雜,要時刻關注排程的效能,不要讓排程成為效能瓶頸。
反向代理負載均衡
反向代理伺服器工作在HTTP層,類似代理伺服器,與普通的代理伺服器不同的是,伺服器在代理的後端,而不是客戶端在代理的後端,這類似於NAT,只是NAT工作在網路層。同樣是負載均衡,反向代理伺服器強調"轉發"而不是"轉移",因為它不僅要轉發客戶端的請求,還要轉發服務端的響應。可以用作反向代理伺服器的軟體有Nginx、lighttp、Apache,另外目前也有一些專業的代理轉發裝置能夠工作在應用層,例如A10。
黏滯會話:對於啟動session儲存使用者資訊,或者後端伺服器使用動態內容快取的應用,必須將使用者在一段會話中的的請求保持在同一臺伺服器上。代理伺服器一般支援類似的配置。然而,儘量不要使應用過於本地化,比如可以使用cookie儲存使用者資料,或者分散式Session或分散式快取。
IP負載均衡
字面上看,便是利用網路層進行請求轉發,類似NAT閘道器。然而,使用閘道器轉發在頻寬上可能出現瓶頸,因為出口只有一個,所以出口的頻寬要求較高。Linux中的Netfilter模組可以透過iptables的配置。比如:對網路埠8001的請求轉發給內網某臺伺服器,而對網路埠8002的請求轉發給內網另一臺伺服器。這種方式簡單易行,但是無法對排程做太多配置。LVS-NAT同樣是Linux中的在網路層進行轉發的方式,與Netfilter不同,它支援一些動態排程演算法,比如最小連結、帶權重的最小連結、最短期望延遲等。
直接路由
這種方式的工作原理略微複雜:
首先每臺伺服器都需要設定一個IP別名,這個IP別名是面向客戶端的一個虛擬IP,只有代理伺服器對這個IP別名的ARP請求做出響應,這樣客戶端發給這個IP的請求包首先會到代理伺服器。然後代理伺服器將這個請求包的目的MAC地址填寫為實際伺服器的MAC地址(透過某種排程演算法決定目的伺服器),由於目標伺服器也具有這個IP別名,因此,轉發過來的資料包能夠被實際的伺服器接收並處理。最後由於資料包的源IP地址還是客戶端請求的IP地址,因此,實際的伺服器將透過交換機直接將響應包轉發給客戶端而無需透過代理伺服器。
Linux下可以透過LVS-DR實現直接路由方式
IP隧道
共享檔案系統
對於一些簡單的提供檔案下載的服務(包括html中靜態資源等),自然要考慮利用叢集來減壓,但是如何使這些資源在叢集中的主機上同步呢。
NFS
一種方案是讓這些主機從同一個地方取資料。比如採用NFS(Network File System),基於PRC。這種方式簡單易行,但是由於NFS伺服器本身的磁碟吞吐率,或者併發處理能力以及頻寬等問題,往往很有侷限性。
冗餘分發
另一個方案就是在主機上冗餘儲存資源,這樣主機無需訪問共享檔案系統,只需讀取本地磁碟上的資源即可。但是這也帶來了一個同步的問題,如何同步這些資料呢:
主動分發式,還分為單級分發和多級分發,分發可以藉助SCP、SFTP、HTTP擴充套件協議WebDAV
單級分發:透過一次分發,就達到目的,這樣的方案簡單易行,但是效能瓶頸會出現在磁碟壓力和網路頻寬,難以擴充套件
多級分發:透過多次分發,才達到目的地,這樣的方案能夠分散磁碟壓力和網路頻寬壓力,而且容易擴充套件,壞處是成本高
被動同步式容易理解,可以使用rsync,rsync同步時是根據最後更新時間進行判定是否需要同步的條件的,因此,如果一個資料夾中有的檔案數量太多的話,rsync掃描的時間就很長了,可以透過給資料夾設定最後更新時間,併合理的規劃檔案目錄,來加快rsync的掃描時間。即使不使用rsync,自己開發同步程式也可以藉助這樣的思想來提升效能。
分散式檔案系統
分散式檔案系統工作在使用者程序層面上,它是一個管理檔案的平臺,內部維護冗餘,檢索,追蹤、排程等工作,通常包含一個物理層面的組織結構和邏輯層面的組織結構。物理層面的組織結構由分散式檔案系統自行維護,邏輯層面的組織結構面向使用者。其中"追蹤器"起到了關鍵的作用。
MogileFS就是一個開源分散式檔案系統,用Perl編寫,包含追蹤器、儲存節點、管理工具,它使用MySQL分散式檔案系統的所有資訊、使用WebDAV實現檔案複製。其他著名的還有Hadoop。
每個檔案由一個key定義,需要讀取檔案時,指定一個key,追蹤器會返回一個實際的路徑,在訪問這個地址即可獲得檔案。甚至可以將這個key對應的path用分散式快取快取起來,這樣可以減少追蹤器的查詢開銷,但這樣也會失去分散式檔案系統的排程策略的優越性。另外,可以利用支援reproxy的反向代理伺服器(比如:Perlbal)讓路徑重定向的工作由反向代理伺服器完成。
資料庫擴充套件
1.主從複製,讀寫分離
這種方式是指利用資料庫的複製或映象功能,同時在多臺資料庫上儲存相同的資料,並且將讀操作和寫操作分開,寫操作集中在一臺主資料庫上,讀操作集中在多臺從資料庫上,對於讀取比寫更多的站點適合使用這種方式。如果不想在應用程式層面維護這種分離對映,那麼可以使用資料庫反向代理來自動完成對讀寫的分離。
2.垂直分割槽
對於不需要進行聯合查詢的資料表可以分散到不同的資料庫伺服器上,這稱為垂直分割槽;當然每個分割槽自身也可以使用讀寫分離。
3.水平分割槽
將同一個表的記錄拆分到不同的表甚至是伺服器上,稱為水平分割槽,這往往需要一個穩定的演算法來保證讀取時能正確從不同的伺服器上取得資料,比如簡單的對ID取模、範圍劃分、亦或者是儲存對映關係。 也可以使用類似代理的產品spock。
快取
構建高效能web站點時,拋開基礎架構(資料庫分割槽的問題也包括在基礎架構中了),在應用程式、編碼層面主要要考慮的問題就是快取的設計,合理的快取設計可以使提供動態網頁服務的網站效能大幅度提高。當然,在架構階段設計快取解決方案,絕非簡單的技術問題,需要從業務出發,再結合各種技術。下面按照一次HTTP請求的順序,對每個環節的快取設計從技術角度進行討論。
1.客戶端快取
可以利用客戶端瀏覽器的快取機制,來減少瀏覽器對服務端的請求次數(當然在服務端進行圖片等資源合併,並結合css圖片定位技術,也可以減少HTTP請求),利用好HTTP的快取協商,可以設計出靈活的客戶端快取方案。在HTTP頭中下面的內容與快取協商有關:
Last-Modified:動態頁面透過主動推送該值,暗示瀏覽器在下次請求同一個url的時候,優先使用If-Modified-Since值與服務端進行快取協商,如果快取沒有過期,那麼服務端可以不用重新計算動態網頁,透過返回304通知瀏覽器。網站的靜態資源往往使用這種方法。但是該方法有一個缺點:有時,檔案的最後更改時間雖然改了,但是內容卻沒有變,這樣無法充分發揮瀏覽器快取的能力。
ETag:Web伺服器為每個url生成一個雜湊值,增加在HTTP頭的ETag標記中,瀏覽器會優先使用If-None-Match加上這個雜湊值來協商快取過期。透過對靜態檔案的內容進行md5變換,可以生成雜湊值,這樣可以彌補Last-Modified的不足。但是隨之帶來的是服務端md5變換的計算開銷。
Expires:上述兩種方式,雖然可以使服務端多少避免了反覆的動態網頁解析和計算,但瀏覽器還是必須透過HTTP請求來進行協商,並沒有真正意義上減少請求的次數。透過在HTTP頭中新增Expires標記可以明確的告知瀏覽器過期形式,瀏覽器會徹底減少請求的次數。
2.反向代理快取
在web伺服器前端,還有反向代理伺服器快取。反向代理伺服器本質上就是代理伺服器,只是將網路的請求轉發給內網的web伺服器處理,他們都工作在應用層,能夠理解HTTP協議。正向代理伺服器具有HTTP快取、HTTP過濾等功能,反向代理伺服器同樣具有HTTP快取的能力,而且還具備一定程度上的安全性。一切HTTP友好的動態程式同樣能夠很好的在反向代理伺服器上實現快取。重量級的squid、輕量級的varnish、甚至是Nginx這樣的web伺服器軟體,都可以勝任反向代理服務。
上述的代理伺服器軟體產品,透過各種配置可以快取基於HTTP協議的web響應。
3.Web伺服器快取
Web伺服器有可能支援基於url的快取(基於key-value對),這類似反向代理快取。快取通常可以透過配置儲存在記憶體或磁碟上,在快取有效期的問題上,通常是基於HTTP協議中的頭部資訊判斷。但是使用這樣的機制需要注意:
動態程式會可能變得依賴於特定的web伺服器 注意編寫面向HTTP快取友好的動態程式,會使你的動態程式更有生命力 web 伺服器還具有快取檔案描述符(類似控制代碼)的能力,這樣可以減少檔案的open操作,同樣是一種減少系統呼叫的措施,這對於一些小檔案有些效果,因為檔案越小花在open上的開銷將越來越佔有重要的比例。
4.應用程式快取
應用程式本身可以對動態內容進行快取,這可以體現在三個層面:
動態指令碼快取:每次指令碼解析都需要消耗一定的時間,為了加快這個速度,一些伺服器端指令碼語言都支援動態指令碼的預編譯,比如我們熟悉的ASP.NET、JSP都有所謂的中間語言,解析這些中間語言會快很多。
應用程式自身實現快取:應用程式根據特定的業務需求獨立設計快取。
在快取設計的具體技術上有以下幾點:
快取在記憶體,這種方式的優點就是減少了磁碟的讀寫,但是記憶體有限,單機不易擴充套件。
快取在分散式快取,這種方式的優點是減少了磁碟讀寫,並提高了可靠性和擴充套件性,但是由於需要網路IO,效能稍遜於單機快取。
區域性頁面快取,對於一些頁面無法完整快取的,可以考慮區域性快取。
靜態化,將網站靜態化可以極大的提高效能,因為使用者的請求不需要動態指令碼處理。
伺服器系統能力的制約因素:
這部分內容對於所有的伺服器(無論是代理伺服器、web伺服器還是其他),都具有普遍適用的意義。
多程序、多執行緒的選擇和排程:程序切換和執行緒切換都需要一定的系統開銷,通常使用多執行緒模型的web伺服器軟體比使用多程序,具備更優的效能。
系統呼叫:一些需要從使用者模式切換到核心模式的函式呼叫可以稱為系統呼叫,比如:開啟檔案。系統呼叫會有一定程度上的開銷,減少系統呼叫是可以加快處理速度的程式設計細節。
TCP連結保持:可以透過保持TCP連結來減少服務端和客戶端之間的建立和關閉TCP連結的操作。HTTP中的Connection:Keep-Alive就有這樣的功能
IO模型:由於CPU的速度遠遠比IO快,IO延遲往往成為效能瓶頸,因此,IO模型十分重要。
各種IO模型:
PIO:CPU直接干預磁碟和記憶體的資料互動,即無論是資料從記憶體到磁碟還是磁碟到記憶體都要經過CPU暫存器。這樣的模型,可想而知,CPU有很多時間都需要等待慢速裝置。
DMA(Direct Memory Access):CPU透過向DMA控制器傳送指令來控制處理資料,資料處理完之後通知CPU,這可以很大程度上釋放CPU資源。
同步阻塞I/O:對於程序來說,一些系統呼叫為了同步IO,會不同程度上阻塞程序,比如accept、send、read等。
同步非阻塞I/O:對於程序來說,一些系統呼叫可以在呼叫完之後立即返回,告知程序IO是否就緒,避免阻塞程序。
多路I/O就緒通知:對於同步非阻塞I/O的方式,程序仍然需要輪詢檔案描述符(控制代碼)來得知哪些IO就緒了,而多路I/O就緒通知將這個過程改成回撥通知。
記憶體對映:將檔案與記憶體的某塊地址空間相對映,這樣可以想寫記憶體一樣寫檔案。當然這種方式本質上跟寫檔案沒有什麼區別。
直接I/O:在使用者程序地址空間和磁碟中間通常都會有作業系統管轄的核心緩衝區,當寫入檔案時,一般是寫入這個緩衝區,然後由一些延遲策略來寫入磁碟。這樣做可以提高寫效率。但是對於諸如資料庫這樣的應用來說,往往希望自己管理讀寫快取,避免核心緩衝區的無畏記憶體浪費。Linux的open函式支援O_DIRECT引數來進行直接IO。
sendfile:如果web伺服器想傳送一個檔案,將會經歷如下過程:開啟檔案,從磁碟中讀取檔案內容(這通常涉及到核心緩衝區資料複製到使用者程序),然後程序透過socket傳送檔案內容(這通常設計到使用者程序資料複製到網絡卡核心緩衝區),可以看到重複的資料複製是可以避免的。sendfile可以支援直接從檔案核心緩衝區複製到網絡卡核心緩衝區。