PHP電商網站高並發的秘訣之電商秒殺活動

PHP電商網站高並發的秘訣之電商秒殺活動:活動方在有限的時間段內(通常是M分鐘到H小時不等的時間)給出指定數量O個P商品的大減價搶購名額。

這類秒殺活動一般都會出現如下情況

第一、在某一時間內QPS超過系統負載;

第二、架構不合理導致系統的其它與秒殺活動不相關的模塊變得異常緩慢;

第三、少數用戶重復搶到名額;

第四、最終搶到的名額數量超過庫存數量;

第五、伺服器宕機後恢復遲緩導致大量用戶流入競爭對手的網站;

第六、機器人流量占用瞭網站訪問導致真實用戶訪問遲緩。

解決方案都是人想出來的,隻是時間問題罷瞭。

解決方案背景:LNMP技術棧

第一、六個問題:

舍即是得:既然指定時間內,秒殺活動的QPS達到峰值Peak1,那麼在秒殺活動並發測試的時候我們應該首先得到這個值得平均范圍,然後取其中的極小值(min),這樣就可以通過nginx的ngx_http_limit_req_module和ngx_http_limit_conn_module兩個模塊來限制,nginx的配置如下:

http {

#geot和map兩段用於處理限速白名單,map段映射名單到$limit,處於geo內的IP將被映射為空值,否則為其IP地址。

#limit_conn_zone和limit_req_zone指令對於鍵為空值的將會被忽略,從而實現對於列出來的IP不做限制

geo $whiteiplist {

default 1;

127.0.0.1 0;

121.199.16.249 0;

}

mapwhiteiplistwhiteiplistlimit {

1 $binary_remote_addr;

0 "";

}

#limit_conn_zone定義每個IP的並發連接數量

#設置一個緩存區保存不同key的狀態,大小10m。使用$limit來作為key,以此限制每個源IP的鏈接數

limit_conn_zone $limit zone=perip:10m;

#limit_req_zone定義每個IP的每秒請求數量

#設置一個緩存區reqps保存不同key的狀態,大小10m。這裡的狀態是指當前的過量請求數。

#$limit為空值則不限速,否則對應的IP進行限制每秒5個連接請求。

limit_req_zone $limit zone=reqps:10m rate=5r/s;

server {

listen 80;

server_name https://www.yoururl.com;

#隻對PHP的秒殺頁面的請求進行限速

location ~ [^/]miaosha\.php(/|$)

{

#對應limit_conn_zone塊

#限制每IP的PHP頁面請求並發數量為5個

limit_conn perip 5;

#對應limit_req_zone塊

#限制每IP的每秒的PHP頁面請求次數為上面定義的rate的值:每秒5個請求,不延遲

limit_req zone=reqps nodelay;

}

}

}

上面的這段nginx配置其實是對單個IP進行限制,效果是有的,但不夠明顯。

2.過濾無效請求:

前端生成簽名字符串,例如通過crypto.js,對當前unix時間戳time,產生隨機字符串nonce,還有一個key必須是用戶填寫好驗證碼後主機返回給瀏覽器客戶端一個token名稱的cookie字段值(有一個過期時間)結合混淆算法生成的,最後然後經過自定義的簽名算法在前端生成簽名字符串signature,最後在發送搶購表單時帶上以上4個字段信息,當請求到達nginx之後,我們使用nginx的lua模塊編寫lua腳本驗證signature的正確性,並且限定以上token的過期時間為30秒,且客戶端返回過來的time參數必須跟伺服器的unix時間戳相差不超過5秒鐘,否則直接在nginx的lua層面上直接屏蔽掉該請求,這裡面就不得不說Openresty技術瞭,感興趣的小夥伴可以去深入研究一下。

3.概率性丟棄超負載的請求:

既然我們已經在前期並發測試的時候獲得瞭一個峰值參數PeakMin,我們應該盡量保證所有的有效秒殺請求不大於這個值,首先我們得獲得當前nginx的總連接數CurrentConnectionCount,當QPS達到PeakMin的時候,我們測算出來的連接數是PeakMinConnectionCount,那麼我們使用nginx的lua模塊獲取這個值,在系統負載達到0.8*PeakMinConnectionCount的時候,我們就對超出的部分90%的丟棄率,返回一個未能秒殺中的提示,並把用戶對此次活動的秒殺結果寫入memcached緩存進行記錄,當系統負載達到PeakMinConnectionCount時,我們直接100%丟棄請求,前端根據狀態碼是5XX來給出用戶未能秒殺中的消息提示,當然我想說的是這裡必須保證用戶的體驗是正常的。

第二個問題:

分功能模塊設計系統:

一個成熟的電商系統,一般會分成很多相對獨立的模塊,比如產品中心,會員中心,訂單中心,物流中心,配置中心,搜索中心等大模塊,這些大模塊之間的庫表數據通常是低耦合的,因此還可以把這些大模塊分割成很多子功能模塊,這樣就可以讓整個電商系統的模塊彼此的影響最大化縮小,其中的分佈式服務端架構包含瞭很多架構實踐,在這裡就不細講瞭。

第三個問題:

1.緩存key原子驗證:

同一個用戶重復搶到名額這個問題比較簡單,最有可能是用戶(機器人程式)在非常短的時間內(假設是0.01秒)提交瞭2次以上的並發請求,以ProductId+ActivityId+UserId命名的key寫入用戶成功秒殺的記錄值,利用memcached的add原子性來寫入信息,如果add出錯則證明已經add過一次,那就返回。

第四個問題:

1.樂觀鎖:

memcached有一個很不錯的CAS檢查機制,就是二話不說我先搶到一個一個名額,到真的要保存的數據的時候我再看看CAS值是否跟一開始的時候一樣,如果不一樣就不操作返回沒有秒殺到的消息提示,否則就減掉一個有效秒殺名額,直到保存秒殺庫存的key為0即止。

第五個問題:

1.冷熱多備份:

不管是應用伺服器,緩存伺服器,伺服器伺服器,消息隊列伺服器等,都應該有自己的多備份,尤其是伺服器伺服器與緩存伺服器更是直接影響瞭系統數據層面的東西,有條件的還需要做好異地多活,多數據中心等架構設施。

2.自動化運維:

多使用批量管理與配置工具,docker等技術。

You May Also Like