HI!,你好,我是zane,zanePerfor是一款我開發的一個前端效能監控平臺,現在支援web瀏覽器端和微信小程式端。
我定義為一款完整,高效能,高可用的前端效能監控系統,這是未來會達到的目的,現今的架構也基本支援了高可用,高效能的部署。實際上還不夠,在很多地方還有優化的空間,我會持續的優化和升級。
開源不易,如果你也熱愛技術,擁抱開源,希望能小小的支援給個star。
專案的github地址:github.com/wangweiange…
專案開發文件說明:blog.seosiwei.com/performance…
zanePerfor應用理論上能夠支援千萬級以上pv專案,但實際情況需要依賴於伺服器和資料庫的效能,以下項儘可能的從各種配置來提升應用的效能。
一:相關專案配置項說明
config.default.js 配置說明複製程式碼
1、servers叢集模式下伺服器之間主要通過內網進行通訊,因此在這裡hostname我們需要配置成內網IP,做如下更改即可
// 叢集配置(一般預設即可)
config.cluster = {
listen: {
port: config.port,
hostname: address.ip(), // 此處替換127.0.0.1
ip: address.ip(),
},
};複製程式碼
2、實時統計任務在大流量專案下時間儘可能的長一些,即能減輕資料庫的壓力也能提升實時統計的準確性 (定時任務時間間隔建議5-20分鐘之間)
// 執行pvuvip定時任務的時間間隔 預設每分鐘定時執行一次 (可更改)
config.pvuvip_task_minute_time = '0 */1 * * * *';
// 更改為
config.pvuvip_task_minute_time = '0 */10 * * * *';複製程式碼
3、上報和消費資料方式選擇redis
// 上報原始資料使用redis儲存、kafka儲存、還是使用mongodb儲存
config.report_data_type = 'redis'; // redis mongodb kafka複製程式碼
4、在資料庫效能足夠強悍的情況下,每次定時任務的時間儘量短,消費的資料儘量多,訊息佇列池儘量不做限制
config.redis_consumption = {
// 定時任務執行時間
task_time: '*/10 * * * * *',
// 每次定時任務消費執行緒數(web端)
thread_web: 2000,
// 每次定時任務消費執行緒數(wx端)
thread_wx: 2000,
// 訊息佇列池限制數, 0:不限制 number: 限制條數,高併發時服務優雅降級方案
total_limit_web: 0,
total_limit_wx: 0,
};複製程式碼
task_time 消費訊息定時任務間隔
thread_wx 每次消費資料條數
total_limit_wx 限制訊息佇列中總條數
以上配置表示:每10秒鐘消費2000條資料,不對上報資料條數做限制 (如果定時任務設定了type: 'all',消費資料會成倍數增加)。
5、解析使用者IP使用redis方式,並關閉檔案快取(備註:流量大時,本地檔案儲存的資料會比較大,每次載入會比較耗時)
// 解析使用者ip地址為城市是使用redis還是使用mongodb
config.ip_redis_or_mongodb = 'redis'; // redus mongodb
// 檔案快取ip對應地理位置(檔名)
config.ip_city_cache_file = {
isuse: false, // 是否開啟本地檔案快取(資料量太大時建議不開啟)
web: 'web_ip_city_cache_file.txt',
wx: 'wx_ip_city_cache_file.txt',
};複製程式碼
6、mongodb叢集模式下url連結需要更改為內網ip,連線池可以稍微調大一些
// mongodb 服務
const dbclients = {
db3: {
// 叢集分片
url: 'mongodb://192.168.1.10:30000/performance',
options: {
autoReconnect: true,
poolSize: 50,
},
},
};複製程式碼
二:HTTP層面說明(以下內容已在程式中實現)
1、設定Connection,關閉keep-alive
為什麼要關閉?
- 在高併發專案下,tcp保持連線時間不要太長,因此nginx的keepalive_timeout儘量設定的更短
- 同一域下請求頻率低、請求次數少的http連結儘量減少tcp連結時間,因此keepalive_timeout儘量設定低
而對於本應用來說,上面兩條都已滿足,因此關閉keep-alive選項。
// node服務實現:
ctx.set('Connection', 'close');
// nginx服務實現:
http {
keepalive_timeout: 0;
}複製程式碼
2、返回空body資訊
- 上報介面接收到請求就儘快返回狀態碼,邏輯處理放到後面處理
- body返回內容儘量簡短或者為空
// 程式碼實現:
async wxReport() {
const { ctx } = this;
ctx.set('Access-Control-Allow-Origin', '*');
ctx.set('Connection', 'close');
ctx.status = 200;
// 後續邏輯處理...
}複製程式碼
三:node單機叢集
使用node.js的Cluster 模組開啟多程式,儘可能的榨乾伺服器資源,利用上多核 CPU 的併發優勢。同時也保證單機服務的穩定性。
egg.js提供多程式模型和程式間通訊。
開啟方式:
// 應用package.json
// 案例中開啟兩個worker程式,預設會開啟伺服器cpu核數個worker程式
"scripts": {
"start": "egg-scripts start --daemon --workers=2 --title=performance",
}複製程式碼
四:Mongodb叢集搭建
高流量,高併發專案少不了mongodb叢集的搭建,關於mongodb叢集搭建請參考以下兩篇文章:
zanePerfor前端效能監控平臺高可用之Mongodb叢集分片架構
zanePerfor前端效能監控平臺高可用之Mongodb副本集讀寫分離架構
案例:現在有3臺伺服器,內網ip分別為:(10.1.0.86、10.1.0.97、10.1.0.70),現在我們來搭建由三臺伺服器組建的叢集。
1、分別在每臺伺服器上建立如下3個檔案
kdir -p /data/mongod/s0
mkdir -p /data/mongod/log
mkdir -p /data/mongod/c0複製程式碼
2、分別在每臺伺服器上啟動Shard Server
mongod --dbpath /data/mongod/s0 --logpath /data/mongod/log/s0.log --fork --smallfiles --port 27020 --bind_ip 10.1.0.86 --shardsvr
mongod --dbpath /data/mongod/s0 --logpath /data/mongod/log/s0.log --fork --smallfiles --port 27020 --bind_ip 10.1.0.97 --shardsvr
mongod --dbpath /data/mongod/s0 --logpath /data/mongod/log/s0.log --fork --smallfiles --port 27020 --bind_ip 10.1.0.70 --shardsvr複製程式碼
3、分別在每臺伺服器上啟動Config Server
mongod --dbpath /data/mongod/c0 --logpath /data/mongod/log/c0.log --fork --smallfiles --port 27100 --bind_ip 10.1.0.86 --replSet rs1 --configsvr
mongod --dbpath /data/mongod/c0 --logpath /data/mongod/log/c0.log --fork --smallfiles --port 27100 --bind_ip 10.1.0.97 --replSet rs1 --configsvr
mongod --dbpath /data/mongod/c0 --logpath /data/mongod/log/c0.log --fork --smallfiles --port 27100 --bind_ip 10.1.0.70 --replSet rs1 --configsvr複製程式碼
4、配置副本集
// 進入97的mongo
mongo --port 27100 --host 10.1.0.97
// 使用admin賬戶
use admin
// 初始化副本集
rs.initiate({_id:"rs1",members:[{_id:0,host:"10.1.0.97:27100"},{_id:1,host:"10.1.0.86:27100"},{_id:2,host:"10.1.0.70:27100"}]})
// 檢視副本集狀態
rs.status()複製程式碼
5、啟動Route Process服務
mongos --logpath /data/mongod/log/mongo.log --port 30000 --bind_ip 10.1.0.97 --fork --configdb rs1/10.1.0.97:27100,10.1.0.86:27100,10.1.0.70:27100複製程式碼
6、配置Sharding分片
// 進入路由伺服器
mongo --port 30000 --host 10.1.0.97
// 新增分片
sh.addShard("10.1.0.97:27020")
sh.addShard("10.1.0.86:27020")
sh.addShard("10.1.0.70:27020")
// 檢視分片資訊
sh.status();複製程式碼
7、設定分片資料庫與片鍵
//指定需要分片的資料庫
sh.enableSharding("performance")
//建立索引(需要對片建建立索引)
db.wx_ajaxs_wx3feeea844b1d03ffs.ensureIndex({"path":1})
//設定分片(對performance資料庫的wx_ajaxs_wx3feeea844b1d03ffs表按照path欄位以hashed的方式分片)
sh.shardCollection("performance.wx_ajaxs_wx3feeea844b1d03ffs", { "path": "hashed"})
//檢視分片資訊
sh.status()複製程式碼
其他表分片重複以上步驟即可, 至此一個簡單的3臺伺服器叢集搭建完畢。
備註:應用中對瀏覽器端 | 微信小程式端 預設使用 _id欄位 進行分片, 儘量不要更改。
五:servers負載均衡
servers負載:即把所有上報的請求分發到不同的servers進行處理,減小單個servers服務的壓力,也減輕了單個伺服器的壓力。
zanePerfor做到了一套程式碼多服務部署的方案
應用保證了多服務同時執行時,task任務不重複執行
此處servers負載均衡採用nginx方案,ng配置如下:
upstream ps-servers {
server 10.1.0.86:7001 weight=1 max_fails=4 fail_timeout=5;
server 10.1.0.97:7001 weight=1 max_fails=4 fail_timeout=5;
server 10.1.0.70:7001 weight=2 max_fails=4 fail_timeout=5;
}複製程式碼
引數說明:
weight=number:設定伺服器的權重,預設情況下為1max_conns=number:限制到代理伺服器的同時活動連線的最大數量,預設為0
max_fails=number:伺服器通訊的失敗嘗試次數
fail_timeout=number:伺服器通訊失敗超時時間,預設為10
backup:將伺服器標記為備份伺服器。它將在主伺服器不可用時傳遞請求
down:將伺服器標記為永久不可用
六:redis叢集
應用中有很多功能都會依賴於redis的快取能力,因此一臺高效能的redis或者redis叢集顯得很有必要。
官方如此介紹redis的強勁效能:效能極高 – Redis能讀的速度是110000次/s,寫的速度是81000次/s ,理論上來說單臺redis就能滿足絕大部分的應用。
至於redis是否需要叢集的支援,需要根據各個應用的情況來決定,博主暫時用的單臺redis,500w PV內暫未遇見效能問題。
至於redis叢集的搭建此處暫不做介紹,暫時掛個官網參考連結:redis.io/topics/clus…
zanePerfor在高流量專案下的架構配置建議實踐說明(完)。