引言
由於最近LZ負責的業務系統頻繁宕機,導致LZ疲於本命,上一個星期(因為現在是周一了,0.0)連續加了五天班,其中還包括周末,就是為了出一套應急方案。宕機的根本原因,現在已經漸漸真正的明晰了,最早的一次是因為消息發送與數據庫操作的順序錯誤導致的數據庫宕機,目前已經通過順序的調換解決了數據庫的壓力。然而數據庫的問題解決之后,則開始是應用服務器出問題。最近頻繁宕機的原因是連接數經常爆滿,完全無法應付nginx的瘋狂攻擊。
經過與公司領導與業務人員的交流,最終才知道,由於業務模式的變化,導致LZ負責的業務系統用戶劇增。因為高並發的原因,LZ的系統現在已經頻繁的不堪重負,數次被壓垮,提升服務器的響應速度已經迫在眉睫。最終定制的解決方案是比較主流的解決方案,也就是將前端采用集群部署,而后台與其它業務系統的交互,則會采用獨立的服務器處理。不過這個方案並不能一解燃眉之急,集群的部署和前端與后台的分割都需要不短的時間進行部署和測試,因此只能安排到這周一(其實就是今天,0.0)才開始推行。
為了避免在系統向集群轉換的過程中再出現問題,LZ與各位領導商量之后就制定了一個簡單的應急方案,而LZ則擔任了編寫應急方案的職責。最近的幾天,LZ就是在忙於編寫這個應急方案(除此之外,還要邊應付業務人員和其它同事對系統的不滿,0.0),怎一個焦頭爛額可以形容。
並發的意識
應急方案采用的方式是,增加用戶數限制以及優先級的功能。用戶數限制的功能類似於火車站之前的搶登陸,簡單的說,就是這個系統只能若干個(比如1000個)用戶登陸,后面來的用戶將被拒絕登陸。優先級的功能,則是指在人數達到一定數目時,需要將某些角色的用戶踢掉,讓另外一部分角色的人優先使用系統(這是因為那些被踢的角色查詢的數據量較大且對系統的依賴性不高,容易給服務器造成不必要的壓力)。這個功能LZ一開始覺得挺簡單的,但是當LZ真寫起來的時候,才發現真的沒有想象中的那么簡單。
最開始的難度在於用戶數的精確統計,因為用戶數與session數量並沒有直接關系,要想統計精確的用戶數,必須按照用戶名去重。這一點在找到適合的監聽器(LZ使用的監聽器是存在於web服務器中的HttpSessionAttributeListener,但這不一定適用於所有項目)之后被解決,方案就是記錄每個用戶的session數量,為0則代表該用戶已退出,否則代表正在使用系統(也就是會算作1個用戶)。
然而最難的地方卻不是用戶數的精確統計,而是並發所導致的難度。因為用戶數是在高並發的情況下統計的,因此必須考慮並發的情況,在代碼中添加適當的同步,即要保證統計數據的正確性,又得保證足夠的性能。如果因為這個數量統計而影響性能,那就與這個功能的意義背道而馳了。
這還是LZ第一次在高並發的情況下編程(以前其實也有,只是由於功能並不核心,所以從未考慮過性能,只是無腦的在方法上使用synchronized),每寫一句代碼,都要考慮如果有成百上千個線程同時運行會如何。這每一句代碼似乎都成了美女,每一個都可能有成百上千個大爺翻牌,而且還是同時,因此到底如何同時伺候N(N>1)個大爺,自然就成了一個問題。這也算是LZ第一次在編程的過程中,真真切切的產生了並發的意識。
書中自有黃金屋
或許是老天開眼,也或許是LZ運氣尚且說的過去,在這之前的幾個星期,LZ剛買了一本關於並發編程的書籍,一直都沒有看。這下可好了,剛好派上用場,因此拿起這本書不到兩天,LZ就一口氣讀了將近一半。最終也算是臨時抱佛腳,將這個應急方案給應付過去了。
由於LZ只是為了應付當前的情況,所以並沒有細讀,盡管讀的過程中都讀懂了,也與作者有深深的共鳴,但過后其實印象並不深。不過這足以讓LZ度過當前的難關了,在編寫這段並發代碼時,LZ主要采取了以下幾種小技巧(基於書中的思想)。
1、將原本存在於監聽器和過濾器的屬性全部提出,使得兩者不再需要考慮線程安全的問題。
2、提出的屬性放在單獨的兩個類(session有效列表和session無效列表,無效列表其實就是可能要被踢的session)當中,這兩個類都是單例,並確保這兩個類是線程安全的。
3、由於需要遍歷一個裝滿session的集合實現踢人的功能,因此采用備份的方式。如此一來,在遍歷session並使得session失效時,並不會鎖住失效session的列表,這樣可以極大的提高性能(前提是限制的用戶數並不高)。
4、在監聽器與過濾器使用這兩個單例類時,堅決杜絕各種競態條件或者復合操作。
5、重構現有的代碼,讓需要同步的地方集中在一起,減少性能的損耗。
這算是LZ從書中緊急領略的幾個方式,今天LZ的應急方案已經正式上線,是否能頂住高並發還有待考量。不過LZ是領略了書中的一部分真諦才下的手,而非一時臆測,因此還是有一定把握的。(希望明早一上班不會被業務部門炮轟,0.0)
黃金屋的誘惑
由於這次事件,LZ已經徹底愛上了這本並發編程(前幾天還說愛上了深入理解那本書,有點花心啊)。這本書並不厚,才200多頁,LZ用了一天半不到的時間看了80頁,准備直接趁熱打鐵,等看完這本書之后,再繼續深入理解和設計模式這兩本書。因此最近的計算機系統原理系列可能要延遲一下了。
本文寫的十分匆忙(今晚其實剛加過班,0.0),因此難免漏洞百出,各位猿友如果遇到過類似高並發的問題,可以暢述己見,讓LZ等一屆屌絲得以一窺天機。時間已經不早了,各位猿友明早見分曉吧。