大型網站技術架構,4網站的高性能架構之應用服務器性能優化

4.3 應用服務器性能優化

應用服務器就是處理網站業務的服務器,網站的業務代碼都部署在這裏,是網站開發最複雜,變化最多的地方,優化手段主要有緩存、集羣、異步等。算法

 

4.3.1 分佈式緩存

 網站性能優化第必定律:優先考慮使用緩存優化性能。數據庫

 

一、緩存的基本原理

緩存速度快,減小訪問時間編程

緩存的數據是通過計算獲得的,減小計算的時間緩存

 

哈希取餘數據長度安全

一致性哈希算法性能優化

 

緩存主要用來存放那些讀寫比很高、不多變化的數據服務器

如商品的類目信息,熱門詞的搜索列表信息,熱門商品信息等。網絡

 

網站數據,80%的訪問落在20%的數據上,所以利用Hash表和內存的告訴訪問特性,將這20%的數據緩存起來,可很好地改善系統性能,提升數據讀取數據,下降存儲訪問壓力。數據結構

 

二、合理使用緩存

緩存濫用:過度依賴低可用的緩存系統,不恰當地使用緩存的數據訪問特性等。多線程

 

頻繁修改的數據

數據讀寫比至少在2:1以上,緩存纔有意義。

寫入一次緩存,在數據更新前至少讀取兩次。

 

數據不一致與髒讀

 

先更新數據,再刪除緩存

 

緩存可用性

 

緩存承擔系統大部分壓力時,緩存的大量失效會致使,訪問所有落到數據庫,形成數據庫宕機,乃至整個系統不可用,這種狀況稱爲緩存雪崩。

緩存熱備是一種方案,可是緩存根本就不應被當作一個可靠的數據源來使用。

 

經過分佈式緩存服務器集羣,將緩存數據分佈到集羣多臺服務器上可在必定程度上改善緩存的可用性。

當一臺緩存服務器宕機的時候,只有部分緩存數據失效,從新從數據庫加載這部分數據不會對數據庫產生很大影響。

 

產品在設計之初就須要一個明確的定位:什麼是產品要實現的功能,什麼不是產品提供的特性。

在產品漫長的生命週期中,會有形形色色的困難和誘惑來改變產品的發展方向,左右搖擺、什麼都想作的產品,最後有可能成爲一個失去生命力的四不像。

 

緩存預熱

 

緩存中存放的是熱點數據,熱點數據又是緩存系統利用LRU對不斷訪問的數據篩選淘汰出來的,這個過程須要花費較長的時間。

新啓動的系統,須要緩存預熱,加載熱點數據。

對於一些元數據如城市列表、類目信息,能夠在啓動時加載數據庫中所有數據到緩存進行預熱。

 

緩存穿透

 

若是由於不恰當的業務、或者惡意攻擊持續高併發地請求某個不存在的數據,因爲緩存沒有保存該數據,全部的請求都會落到數據庫上,會對數據庫形成很大壓力,甚至崩潰。一個簡單的對策是將不存在的數據也緩存起來(其value值爲null)

 

三、分佈式緩存架構

分佈式緩存指緩存部署在多個服務器組成的集羣中,以集羣方式提供緩存服務,有兩種架構:

JBoss Cache:須要更新同步的分佈式緩存,不算真正的分佈式,只能算法是集羣熱備。全部服務器保存相同的緩存數據,當某臺服務器有緩存數據更新的時候,會通知集羣中其餘機器更新緩存數據或清除緩存數據。

緩存的數據受限於單一服務器的內存空間,不具備伸縮性。能夠聯繫前面的軟件架構要素分析。

緩存的數據量很大時,這種架構沒法應對。

 

Memcached採用一種集中式的緩存集羣管理,也被稱爲互不通訊的分佈式架構方式。

緩存和應用分離部署,應用程序經過一致性Hash等路由算法選擇緩存服務器遠程訪問緩存數據,緩存服務器之間不通訊,緩存集羣的規模能夠很容易地實現擴容,具備良好的可伸縮性。

 

四、Memcached

 

 簡單的通訊協議

遠程通訊須要考慮兩方面要素:

一、通訊協議:TCP/UDP/HTTP

二、序列化協議:數據傳輸的兩端,必須使用彼此但是別的數據序列化方式才能使通訊得以完成,如XML、JSON等文本序列化協議,或者Google Protobuffer等二進制序列化協議。

 

Memcached使用TCP協議(UDP也支持)通訊,其序列化協議則是一套基於文本的自定義協議,很是簡單,以一個命令關鍵字開頭,後面是一組命令操做數。例如讀取一個數據的命令協議是get<key>。

 

豐富的客戶端程序

高性能的網絡通訊,Memcached服務端通訊模塊基於Libevent,一個支持異步非阻塞,支持事件觸發的網絡通訊庫。穩定的長鏈接特性。

 

高效的內存管理

內存碎片化管理比較難,經常使用的方法好比壓縮、複製等。

 

Memcached使用的是固定空間分配。

Memcached將內存空間分爲一組slab,每一個slab裏又包含一組chunk,同一個slab裏每一個chunk的大小是固定的,擁有相同大小chunk的slab被組織在一塊兒,叫作slab_class。

 

 存儲數據時,根據數據的Size大小,尋找一個大於Size的最小chunk將數據寫入。

這種內存管理方式避免了內存碎片管理的問題,內存的分配和釋放都是以chunk爲單位的。

Memcached採用LRU算法釋放最近最久未被訪問的數據佔用的空間,釋放的chunk被標記爲未用,等待下一個合適大小數據的寫入。

 

這種方式會帶來內存浪費的問題,即數據只能存入一個比它大的chunk裏,而一個chunk只能存一個數據,其餘空間被浪費了。

啓動參數配置不合理,浪費會更加驚人,發現沒有緩存多少數據,內存空間就用盡了。

 

互不通訊的服務器集羣架構

 一致性哈希算法路由成爲數據存儲伸縮性架構設計的經典範式。

集羣內服務器互不通訊使得集羣能夠作到幾乎無限制的線性伸縮。

 

4.3.2 異步操做

 使用子消息隊列將調用異步化,可改善網站的擴展性。還能夠改善網站系統的性能。

 

使用消息隊列後,用戶請求的數據發送給消息隊列後當即返回,再由消息隊列的消費者進程從消息隊列中獲取數據,異步寫入數據庫。

 

因爲消息隊列服務器處理速度遠快於數據庫(消息隊列服務器也比數據庫具備更好的伸縮性),所以用戶的響應延遲可獲得有效改善。

 

消息隊列具備很好的削峯做用——即經過異步處理,將短期高併發產生的事務消息存儲在消息隊列中,從而削平高峯期併發事務。

 

電商業務促銷活動中,合理使用消息隊列,可有效抵禦促銷活動剛開始大量涌入的訂單對系統形成的衝擊。

 

 

須要注意的是,因爲數據寫入消息隊列後當即返回給用戶,數據在後續的業務校驗、寫數據庫等操做可能失敗,所以在使用消息隊列進行業務異步處理後,須要適當修改業務流程進行配合,如訂單提交後,訂單數據寫入消息隊列,不能當即返回用戶訂單提交成功,須要在消息隊列的訂單消費者進程真正處理完該訂單,甚至商品出庫後,再經過電子郵件或SMS消息通知用戶訂單成功,以避免交易糾紛。

 

任何能夠晚點作的事情都應該晚點再作。

 

4.3.3 使用集羣

使用負載均衡技術爲一個應用構建一個由多臺服務器組成的服務器集羣,將併發請求分發到多臺服務器上處理,避免單一服務器因負載壓力過大而響應緩慢,使用戶請求具備更好的響應延遲特性

 

4.3.4 代碼優化

一、多線程

從資源利用角度,使用多線程的緣由主要有兩個:IO阻塞與多CPU。

當前線程處理IO的時候,會被阻塞釋放CPU以等待IO操做完成,因爲IO操做須要時間較長,這時CPU能夠調度其餘線程進行處理。

另:使用現代操做系統提供的諸如epoll等系統調用,能夠實現異步非阻塞IO,能夠同時處理更多的IO請求

多CPU時代充分利用系統CPU資源

 

最佳啓動線程數和CPU內核數量成正比,和IO阻塞時間成反比。

若是任務都是CPU計算型任務,那麼線程數最多不超過CPU核數;

若是是IO密集型任務,等待磁盤操做,網絡響應,那麼多啓動線程有助於提升任務併發度,提升系統吞吐能力,改善系統性能。

 

多線程編程須要注意線程安全問題,即多線程併發對某個資源進行修改,致使數據混亂。

 

對網站而言,無論有沒有進行多線程編程,工程師寫的每一行代碼都會被多線程執行,由於用戶請求是併發提交的,也就是說,全部的資源——對象、內存、文件、數據庫,乃至另外一個線程均可能被多線程併發訪問。

 

編程上,解決線程安全的主要手段有以下幾點:

將對象設計爲無狀態對象

無狀態是指對象自己不存儲狀態信息(對象無成員變量,或者成員變量也是無狀態對象),這樣多線程併發訪問的時候就不會出現狀態不一致,Java Web開發中經常使用的Servlet對象就設計爲無狀態對象,能夠被應用服務器多線程併發調用處理用戶請求。

而Web開發中經常使用的貧血模型對象都是些無狀態對象。不過從面向對象設計的角度看,無狀態對象是一種不良設計。

使用局部對象(使用局部變量,線程本地變量ThreadLocal)

方法內部建立的對象的生命週期是隨着方法的進入退出,方法是線程線程私有的,除非對象在方法內部建立後,被有意識的傳遞給其餘線程,不然不會出現對象被多線程併發訪問的情形。

併發訪問資源時使用鎖

使用鎖將併發操做轉化爲順序操做,從而避免資源被併發修改。鎖致使線程同步順序執行,可能會對系統性能產生嚴重影響。

二、資源複用

系統運行時,要儘可能減小那些開銷很大的系統資源的建立和銷燬,好比數據庫鏈接、網絡通訊鏈接、線程、複雜對象等。從編程角度,資源複用主要有兩種模式:單例和對象池。

單例模式:

 

Spring的單例

 

對象池模式經過複用對象實例,減小對象建立和資源消耗。

數據庫鏈接池對象

線程池對象

池管理方式:

 

三、數據結構

在不一樣場景中合理使用恰當的數據結構,靈活組合各類數據結構改善數據讀寫和計算特性可極大優化程序的性能。

 

什麼場景使用什麼數據結構,優化了哪些方面呢?上面這句話是捕捉不到這些信息的。

 

舉個例子:緩存使用的數據結構是hash表,hash表的讀寫性能依賴HashCode的隨機性,越隨機衝突越少,讀寫性能越高。

time33算法+md5

 

四、垃圾回收

爲何要理解GC和調優?

由於Java Web應用運行在JVM等具備垃圾回收功能的環境中,垃圾回收對系統的性能產生巨大影響。理解垃圾回收機制有助於程序優化和參數調優,以及編寫內存安全的代碼。

 

JVM將內存劃分爲堆和棧。

 

棧用於存儲線程上下文信息,如方法參數、局部變量

 

堆存儲對象,對象的建立、釋放垃圾收集都在堆上進行。

 

JVM的垃圾收集是分代收集,堆空間分爲年輕代和老年代,年輕代分爲Eden區、From區和To區。

 

對象建立都在Eden區,當Eden區滿,就觸發一次YGC,將還被使用的對象複製到From區,回收Eden區。

當Eden區再滿了,再次觸發YGC,將Eden區和From區還在被使用的對象複製到To區,下一次YGC則是將Eden區和To區還被使用的對象複製到From區。

 

通過屢次YGC,某些對象會在From區和To區複製屢次,若是超過某個閾值還未被釋放,則將該對象複製到老年代。

 

若是老年代用完,就會觸發Full GC,即所謂的全量回收,全量回收會對整個系統產生較大影響,由於應根據系統業務特色和對象生命週期,合理設置年輕代和老年代的大小,儘可能減小Full GC。

相關文章
相關標籤/搜索