最近接到個任務,業務場景是須要處理高併發。html
原諒我第一時間想到的竟然是前段時間阮一峯的博客系統遭到了DDoS攻擊,由於在個人理解中,它們的原理是想通的,都是服務器在必定時間內沒法處理全部的並行任務,致使部分請求異常,甚至會像阮一峯的博客同樣崩潰。前端
以前不太有接觸太高併發的機會,因此並無什麼實際經驗,卻是以前作的項目中有秒殺功能的實現作過必定的處理,當時的處理就是多利用緩存進行優化和減小一些不必的後端請求,可是由於是創業公司,因此並無多少過多的流量,即使是秒殺,因此也沒有進行更進一步的優化了,業務需求不須要,本身也沒有過多去思考這個問題了。node
其實剛開始我仍是有些想法,利用HTTP頭部,強緩存(cache-control)、協商緩存(last-modified和Etag)、開啓HTTP2,尤爲是HTTP2應該能將性能提高很多吧,可是這些方案大多都須要後端支持,那麼前端能作什麼呢,卻是還真沒好好思考和總結一下。webpack
架構搭建以前首先要把需求理解透徹,因此去谷歌搜索了一波,首先看幾個名詞:web
再看幾張圖:express
正常訪問:npm
高併發:segmentfault
客戶端精簡與攔截:後端
那麼怎麼淺顯的解釋下高併發呢?把服務器比做水箱,水箱與外界鏈接換水有三根水管,正常狀況下都能正常進行換水,可是忽然一段時間大量的水須要流通,水管的壓力就承受不了了。再簡單點:洪澇災害、遲早高峯、中午12點的大學食堂,大概都是這個原理吧。這些現實問題怎麼解決的呢,高併發是否是也能夠借鑑一下呢?api
回到高併發的問題上,我認爲解決方案主要有這些:
後來發現若是要把優化作到很好,雅虎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",
複製代碼
const qiniu = require('qiniu')
const fs = require('fs')
const path = require('path')
var rm = require('rimraf')
var config = require('../config')
const cdnConfig = require('../config/app.config').cdn
const {
ak, sk, bucket
} = cdnConfig
const mac = new qiniu.auth.digest.Mac(ak, sk)
const qiniuConfig = new qiniu.conf.Config()
qiniuConfig.zone = qiniu.zone.Zone_z2
const doUpload = (key, file) => {
const options = {
scope: bucket + ':' + key
}
const formUploader = new qiniu.form_up.FormUploader(qiniuConfig)
const putExtra = new qiniu.form_up.PutExtra()
const putPolicy = new qiniu.rs.PutPolicy(options)
const uploadToken = putPolicy.uploadToken(mac)
return new Promise((resolve, reject) => {
formUploader.putFile(uploadToken, key, file, putExtra, (err, body, info) => {
if (err) {
return reject(err)
}
if (info.statusCode === 200) {
resolve(body)
} else {
reject(body)
}
})
})
}
const publicPath = path.join(__dirname, '../dist')
// publicPath/resource/client/...
const uploadAll = (dir, prefix) => {
const files = fs.readdirSync(dir)
files.forEach(file => {
const filePath = path.join(dir, file)
const key = prefix ? `${prefix}/${file}` : file
if (fs.lstatSync(filePath).isDirectory()) {
return uploadAll(filePath, key)
}
doUpload(key, filePath)
.then(resp => {
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
})
console.log(resp)
})
.catch(err => console.error(err))
})
}
uploadAll(publicPath)
複製代碼
拋開與網站服務器的Http請求,第一次打開首頁:
以後:
原理大概是這樣,效果也仍是不錯,本身的服務器只須要執行必要的接口任務就好了,不須要負責靜態資源的傳輸
作了一個限定,5秒內刷新頁面只獲取一次列表數據,避免高頻刷新帶給服務器的壓力
async init() {
try {
const store = JSON.parse(util.getStore('hopoActiveInfo'))
// 避免高頻刷新增長服務器壓力
if (store && (new Date() - new Date(store.getTime)) < 5000) {
this.basicInfo = store
} else {
this.basicInfo = await getActiveInfo()
this.basicInfo.getTime = new Date()
}
util.setStore(this.basicInfo, 'hopoActiveInfo')
this.btn.noPeopleAndStart.detail[0].text = `${ this.basicInfo.directBuyPrice } 元直接購買`
this.computedStatus()
} catch (error) {
console.log(error)
}
},
複製代碼
對於全部的數據和接口設置響應頭,利用express模擬,若是兩次請求間隔小於5秒,直接返回304,不須要服務器進行處理
app.all('*', function(req, res, next){
res.set('Cache-Control','public,max-age=5')
if ((new Date().getTime() - req.headers['if-modified-since'] )< 5000) {
// 檢查時間戳
res.statusCode = 304
res.end()
}
else {
var time =(new Date()).getTime().toString()
res.set('Last-Modified', time)
}
next()
})
複製代碼
最後總結一下,採起了的措施有:
最主要的措施大概也只有這幾個,作到的優化不多,差的也還很遠,任重而道遠,繼續努力吧。
參考: