多併發是網站的基本要求,大型網站的併發量甚至會達到數萬,單臺服務器的併發用戶也會達到數百,例如淘寶的雙十一、商務網站的促銷活動。
一、多少線程合適
多併發又可以分爲CPU密集型和IO密集型。
(1)CPU密集型即需要非常多的CPU計算資源,如有多顆CPU核心,可以讓每一顆CPU都參與計算,從而不浪費服務器資源,CPU密集型的典型例子,例如文件排序、圖形搜索、動態規劃等需要複雜的科學計算的情況,CPU密集型需要減少線程數,減少線程的上下文切換導致的資源損耗。
(2)IO密集型即需要大量的數據讀寫,例如網絡傳輸、數據庫讀寫等,大部分的網站、企業應用系統都屬於IO密集型,當發生IO讀寫的時候,由於IO操作時間很長(受限於硬盤的讀寫速度和網絡傳輸速度),線程會處於等待狀態,此時CPU空閒,這時CPU可以調度其他線程進行處理,IO密集型需要增加線程數,這樣在IO處理的時候,可以去做其他事情,以提高併發量。
對於多併發,目前主要採用多進程和多線程兩種模式,例如一臺服務器上有多個應用服務器,然後多個應用服務器接收多個線程併發執行,因爲線程之間的切換也有成本,所以也有觀點認爲應該採用多進程的模式。總之,進程數和線程數應該要根據實際情況進行綜合選擇。
那麼,到底啓動多少線程合適呢?根據以上分析,線程數應該和CPU核心數量成正比,而和IO阻塞時間成反比,因此有兩個公式可供參考:
(1)線程數=[任務執行時間/(任務執行時間-IO等待時間)] * CPU內核數
或
(2)線程數 = CPU核心數/(1-阻塞係數)
這個阻塞係數一般爲0.8~0.9之間,也可以取0.8或者0.9。
如果是CPU密集型,則線程數不超過CPU內核,如果是IO密集型,則應該增加線程數,提高併發量。
當發現系統運行慢的時候,不是盲目去加CPU加內存或者加硬盤,應該充分了解系統資源的使用情況,從而決定如何升級系統配置,例如在Linux環境下用TOP指令觀察CPU、硬盤的使用情況:
Tasks: 29 total, 1 running, 28 sleeping,0 stopped, 0 zombie Cpu(s): 0.3% us, 1.0% sy, 0.0% ni, 98.7%id, 0.0% wa, 0.0% hi, 0.0% si
0.3% us反應CPU使用情況,0.0% wa 則大致體現出當前的磁盤io請求是否頻繁。如果 wa的數量比較大,說明等待輸入輸出的的io比較多,另外還可以通過交互命令H瞭解各個線程對CPU的使用情況。
在Mysql中還可以通過show processlist命令查看高導致磁盤頻繁讀寫的查詢語句。
二、多層次連接池
從以上分析,線程也不應***,而且線程之間的切換需要成本,而且線程數過多,對資源搶佔卻不釋放,會出現死鎖的情況,在數據庫操作中經常會碰到這樣情況。所以應該通過連接池,控制線程數量,並減少線程之間的切換,對於一個多層次的網站結構,有Web服務器、應用服務器、緩存服務器、數據庫服務器。
可以在不同服務器之間通過多個連接池進行互聯,從而控制併發數和線程資源。
三、異步消息實現高併發
在不使用消息隊列的情況,所以請求都是併發操作,會對數據庫造成很大的壓力,也導致系統響應能力下降。
使用消息隊列,則用戶可以馬上得到響應,數據寫入消息隊列,由消費者從隊列獲取數據,異步寫入數據,從而提高用戶響應速度。如果消息隊列滿,則可以即使反饋給用戶等待,而不是導致系統崩潰,該做法體現在淘寶雙十一活動的前10分鐘,當時用戶併發操作量激增,如果沒有使用消息隊列,則系統崩潰,使用消息隊列從而實現併發高峯的削平,消息隊列也可以應用於公交車到站提醒、促銷等多用戶高併發的場景。
當前主要的消息隊列有:IBM MQ、RabbitMQ、ActiveMQ等。
四、使用集羣提高併發處理能力
使用負載均衡可以將併發訪問分散到多臺服務器上處理,避免單臺服務器壓力過大,提高系統響應能力。
可以採用重定向負載均衡、DNS負載均衡、反向代理負載均衡、IP負載均衡等模式
五、多線程模式下的線程安全
併發線程,容易產生線程衝突,因此需要解決線程安全問題
1、講對象設計爲無狀態,這樣不會出現狀態不一致情況,例如Servlet就是無狀態,因此是線程安全
2、使用ThreadLocal的局部對象,這樣可以減少線程之間併發操作對象
3、併發使用鎖,通過鎖實現順序操作,避免併發修改,但是鎖會帶來系統性能的下降,java的concurrent包下的ConcurrentHashMap、ConcurrentLinkedQueue和CopyOnWriteArrayList等等,都是線程安全的。
除此之外,還可以考慮採用更爲複雜的分佈式結構:分佈式服務,例如SOA,分佈式緩存,例如memcache、redis等,網絡上的加速,例如CDN等,硬件上的升級,例如固態硬盤等。