淺談高併發
最近接到個任務,業務場景是需要處理高併發。
原諒我第一時間想到的居然是前段時間阮一峰的部落格系統遭到了DDoS攻擊,因為在我的理解中,它們的原理是想通的,都是伺服器在一定時間內無法處理所有的並行任務,導致部分請求異常,甚至會像阮一峰的部落格一樣崩潰。
之前不太有接觸過高併發的機會,所以並沒有什麼實際經驗,倒是之前做的專案中有秒殺功能的實現做過一定的處理,當時的處理就是多利用快取進行優化和減少一些沒必要的後端請求,但是因為是創業公司,所以並沒有多少過多的流量,即便是秒殺,所以也沒有進行更進一步的優化了,業務需求不需要,自己也沒有過多去思考這個問題了。
其實剛開始我還是有些想法,利用HTTP頭部,強快取(cache-control)、協商快取(last-modified和Etag)、開啟HTTP2,尤其是HTTP2應該能將效能提升不少吧,但是這些方案大多都需要後端支援,那麼前端能做什麼呢,倒是還真沒好好思考和總結一下。
架構搭建之前首先要把需求理解透徹,所以去谷歌搜尋了一波,首先看幾個名詞:
- QPS:每秒鐘請求或者查詢的數量,在網際網路領域,指每秒響應請求數(指HTTP請求)
- 吞吐量:單位時間內處理的請求數量(通常由QPS與併發數決定)
- 響應時間:從請求發出到收到響應花費的時間,例如系統處理一個HTTP請求需要100ms,這個100ms就是系統的響應時間
- PV:綜合瀏覽量(Page View),即頁面瀏覽量或者點選量,一個訪客在24小時內訪問的頁面數量,同一個人瀏覽你的網站同一頁面,只記作一次PV
- UV:獨立訪問(UniQue Visitor),即一定時間範圍內相同訪客多次訪問網站,只計算為1個獨立訪客
- 頻寬:計算頻寬大小需關注兩個指標,峰值流量和頁面的平均大小
再看幾張圖:
正常訪問:

高併發:

客戶端精簡與攔截:

那麼怎麼淺顯的解釋下高併發呢?把伺服器比作水箱,水箱與外界連線換水有三根水管,正常情況下都能正常進行換水,但是突然一段時間大量的水需要流通,水管的壓力就承受不了了。再簡單點:洪澇災害、早晚高峰、中午12點的大學食堂,大概都是這個原理吧。這些現實問題怎麼解決的呢,高併發是不是也可以借鑑一下呢?
- 洪澇災害:修固堤岸(增強伺服器效能)
- 早晚高峰:多選擇其他路線(分流,和分配伺服器線路),不是一定需要就避開早晚高峰(減少客戶端請求)
- 中午12點的大學食堂:學校多開幾個食堂(靜態資源與後端api分到不同伺服器)
回到高併發的問題上,我認為解決方案主要有這些:
- 靜態資源合併壓縮
- 減少或合併HTTP請求(需權衡,不能為了減少而減少)
- 使用CDN,分散伺服器壓力
- 利用快取過濾請求
後來發現如果要把優化做到很好,雅虎35條軍規中很多條對解決高併發也都是有效的。
- ofollow,noindex">雅虎前端優化的35條軍規
回到業務上,本次業務是助力免單。設計圖沒有幾張,擔心涉及商業資訊就不放圖了,因為要求是多頁面,我將業務分成三個頁面:
- 首頁,檢視活動資訊頁
- 檢視自己活動程序頁,包括活動結束,開始活動,活動進行中和助力失敗幾個狀態
- 幫助他人助力頁,包括幫他助力和自己也要助力兩個狀態
簡單分析了一下,需要的資料有:
{ // 這個活動的id,防止多個助力活動同時發起,本地儲存混亂的問題 id:'xxxxxxxxxx', // 結束時間,這個時間一般是固定的,也可以放到本地儲存,不需要多次請求,過了時間可以clear這個 endTime:'xxxxxxxx', // 需要助力的人數 needFriendsNumber:3, // 直接購買的價格 directBuyPrice: 9.9, // 自己的資訊,在幫助別人和發起助力時需要自己的資訊 userInfo:{ id:'xxxxxxxxx', avatar:'xxxxxxxxx' }, // 幫助過我的人列表,顯示幫助我的頁面需要用,根據需求看,這個列表人數不會太多,也可以放到本地儲存 helpMeList:[{ id:'xxxxxxxxx', avatar:'xxxxxxx' },{ id:'xxxxxxxxx', avatar:'xxxxxxx' } ... ], // 幫助別人的列表,可以放到本地儲存中,在進入給別人助力時不用再發起請求,幫助過別人後加到陣列中 helpOtherList:[{ id:'xxxxxxxxx', avatar:'xxxxxxx' },{ id:'xxxxxxxxx', avatar:'xxxxxxx' } ... ] } 複製程式碼
嗯,貌似都可以藉助本地儲存實現減少請求的目的,5M的localStrong應該也夠用。這樣算來除了助力他人和第一次獲取基本資訊還有獲取助力名單,貌似也不需要其他的額外的請求了。精簡請求這個方面目前就是這樣了,因為還沒有完全寫完,所以還有沒考慮到的就要到寫實際業務的時候碰到再處理了。
壓縮資源的話webpack在build的時候已經做過了。
然後就是靜態資源上傳到七牛cdn,具體實現思路是在npm run build之後,執行額外的upload.js,伺服器部署的時候只需要部署三個html檔案就可以了。 package中:
"build": "node build/build.js && npm run upload", 複製程式碼
拋開與網站伺服器的Http請求,第一次開啟首頁:

之後:

原理大概是這樣,效果也還是不錯,自己的伺服器只需要執行必要的介面任務就行了,不需要負責靜態資源的傳輸

最後總結一下,採取了的措施有:
- 利用快取,精簡請求
- 合併壓縮
- 靜態資源上傳cdn
最主要的措施大概也只有這幾個,還有的比如設定cache-control和etags應該也有效果,但是因為需要後端支援,所以暫時沒有考慮。
做到的優化很少,差的也還很遠,任重而道遠,繼續努力吧。
參考: