開發與維運

某口罩項目架構演進記錄&優化經驗分享

作者:牧原

項目背景:

疫情初期某地政府決定發放一批免費口罩面向該市市民,該市市民均可免費預約領取,預約時間為早上9點-12點,因此該場景為限時搶購類型場景,會面臨非常大的定時超大流量超大併發問題,在該項目的落地過程中,涉及的架構演變,做了一些記錄和思考
1,原始架構圖示&分析(2月2號晚上22點左右的原始架構)
image.png

1.1 客戶端走https協議直接訪問ecs
1.2 ECS上使用nginx自建https監聽
1.3 Nginx反代tomcat,Nginx處理靜態文件,tomcat處理動態請求
1.4 程序先去redis查緩存,如未命中則去數據庫查詢數據,同時redis與mysql之間的數據同步靠程序控制

優點:易管理,易部署
缺點:性能差,無擴展性,存在單點風險

事實證明:該應用一經上線立刻被打掛了(未知原因預約頁面洩露,導致還未到預約時間即被打掛)

2,我方介入後的二代架構(24點左右找的我們,早上9點要開服,時間太緊,任務太重,程序不能動的情況下,幾十萬的併發架構如何做?2月3號早上9點左右的架構,4號也恢復了這個架構)
image.png

2.1 接入slb,通過鏡像橫向擴展負載能力
2.2 接入讀寫分離數據庫架構,通過阿里雲數據庫自動進行讀寫分離,自動同步數據
2.3 調整nginx協議
2.4 同架構備集群啟用(域名解析做了兩個A記錄)
2.5 分析訪問日誌發現失敗原因在獲取短信&登陸初始化cookie的點上

優點:增加了高可用性,擴展了負載能力
缺點:對流量預估不足,靜態頁面也在ECS上,因此SLB的出帶寬一度達到最大值 5.xG,併發高達22w+,用戶一度打不開頁面,同時由於新網的限制客戶無法自助添加解析,當晚聯繫不到新網客服導致CDN方案擱淺

3,知恥而後勇的第三代架構(2月4號&2月5號的架構,5號應用)

image.png
3.1 接入CDN 分流超大帶寬
3.2 取消nginx的代理
3.3 做了新程序無法準時上線的災備切換方案(沒想到還真用到了)
3.4 使用虛擬服務器組做新老程序的切換,但是缺點是 一個七層監聽的slb後端只能掛200個機器,再多slb也扛不住了,導致老程序剛承接的時候再度掛掉
3.5 5號使用這個架構上線,7分鐘庫存售罄,且體驗極度流程,絲般順滑,健康同學開發的新程序真是太爽的

優點:CDN負擔靜態資源的流量降低了SLB的出帶寬,壓測的效果也非常理想
缺點:需要多一個獨立的域名在頁面裡面,涉及跨域,4號臨開服之際測試發現入庫&預約短信亂碼返回,緊急切換回了老程序,即二代架構

4,理想架構

image.png
4.1 主域名接入CDN,
4.2 CDN通過設置回源http、https協議去訪問SLB的不同監聽實現新老程序之間的切換,具體實現為回源協議對應
不同監聽,監聽對應不同的程序

優點:靜態加速降低SLB帶寬,動態回源,無跨域問題 ,切換方便
缺點:仍需手工設置,鏡像部署ecs不方便,如果時間充足,可以直接上容器的架構該有多美好呢,一個scale 可以擴出來幾十上百的pod,也可以做節點自動擴容

總結

總結:時間緊任務重,遇到了N多的坑,想起來一個補一個~
1,vcpu購買額度
2,slb後端掛載額度
3,客戶餘額不足欠費停機
4,新網解析需要聯繫客服添加
5,第一次考慮CDN架構的時候未考慮跨域問題
6,新程序開發期間未連接主庫測試,導致上線失敗(主庫亂碼)
7,第一次(3號)被打掛的時候只關注了slb的流量,未詳細分析失敗最多的環節
8,上線前壓測缺失,純靠人工測試功能
9,壓測靠人手一臺jmeter(4號晚上到5號早上引入了PTS進行壓測)
10,3號第一次上線的時候客戶其實默默找了騰訊的人開發了小程序,我們4號因亂碼新程序無法上線導致二次崩潰並得知騰訊那邊挺順利,內心簡直太煎熬了,後來得知騰訊雖然預約的快,但是並沒有考慮數據結構的問題導致預約成功的用戶無法拿到口罩,雖然不應該開心,但是還是忍不住笑了一下(後來騰訊晚阿里雲n個小時候把數據整理出來給到對應的領取口罩的門店解決該問題)
11,突然想起來客戶原始的程序是放在windows上的,windows+爛程序性能真的極差
12,這個“小項目”前後竟然耗費了小十萬,如果一開始就給我們做的話,應該可以減少一半的成本

最後的成果統計(採樣分析,實際數據比這個還大):

image.png
最後上線的三代架構,為了保險起見上了150臺機器,但是根據活動期間的觀察,以及對壓測結果的評估,上50臺機器應該就可以抗住了,從持續5小時一直崩潰被終端用戶罵街,到7分鐘庫存售罄的領導讚賞,雖然經歷了3個通宵的戮戰,依然可以隱隱約約感覺到身心都得到了昇華~

優化參數筆記:

1,參數優化

net.ipv4.tcp_max_tw_buckets = 5000 --> 50000
net.ipv4.tcp_max_syn_backlog = 1024 --> 4096
net.core.somaxconn = 128 --> 4096
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_timestamps = 1(5和6同時開啟可能會導致nat上網環境建聯概率失敗)
net.ipv4.tcp_tw_recycle = 1
/etc/security/limits.conf
* soft nofile 65535
* hard nofile 65535
nginx參數優化
worker_connections  1024-->10240;
worker_processes  1-->16;(根據實際情況設置,可以設置成auto)
worker_rlimit_nofile 1024-->102400;
 listen 80 backlog 511-->65533;
 部分場景也可以考慮nginx開啟長連接來優化短鏈接帶來的開銷

2,架構優化

擴容SLB後端ECS數量,ecs配置統一
nginx反代後端upstream無效端口去除
雲助手批量處理服務,參數優化,添加實例標識
雲監控大盤監控,ECS slb dcdn redis
調整SLB為7層監聽模式,前7後4關閉會話保持導致登錄狀態失效,

3,程序優化

添加gc log,捕捉gc分析問題,設置進程內存
/usr/bin/java -server -Xmx8g -verbose:gc -XX:+PrintGCDetails -Xloggc:/var/log/9052.gc.log -Dserver.port=9052 -jar /home/app/we.*****.com/serverboot-0.0.1-SNAPSHOT.jar
優化短信發送邏輯,登陸先查詢redis免登session,無免登session再允許發送短信驗證碼(降短信的量,優化登陸體驗)
 
jedis連接池優化
maxTotal 8-->20 
acceptcount優化(對標somaxconn)
bug:
springboot1.5帶的jedis2.9.1的redis連接洩漏的問題,導致tomcat 800進程用滿後都無限等待redis連接。
後來進一步調研發現這個問題在2.10.2已經修復,而且2.10.2向後兼容2.9.1

4,數據庫優化

redis公網地址變更為內網地址
 redis session超時設置縮短,用於釋放redis連接
 server.servlet.session.timeout=300s
spring.session.timeout=300s
慢SQL優化(RDS的CloudDBA非常好用喲)
添加只讀實例,自動讀寫分離
優化backlog
添加讀寫分離實例數量

Leave a Reply

Your email address will not be published. Required fields are marked *