在設計一個系統(tǒng)之前,我們需要確認我們的業(yè)務場景是什么樣的。我會帶大家假設一個場景。
場景
我們將在現(xiàn)場銷售100片以下嬰兒紙尿褲,然后根據(jù)之前秒殺活動的數(shù)據(jù)和經驗,估計有10萬人會搶到這100片紙尿褲。(南極人賺錢?。?/p>
一聽就完蛋了,我們的服務器怎么能頂?shù)米?!說實話Mokup Frames(樣機效果圖生成軟件),直接打DB肯定會掛。不過別著急,有暖男敖丙,我們開始之前應該想想會出現(xiàn)什么問題?
高并發(fā)問題:
是的,高并發(fā)是我們甚至不必考慮的一點。這么多人瞬間進來。這什么時候不是高并發(fā)?
是的,秒殺的特點是時間極短,瞬間用戶數(shù)大。
正常的店鋪營銷,是用極低的價格,加上短信和APP的精準推送,吸引特別多的用戶參與到這個秒殺中,讓業(yè)務難以發(fā)展。
大家都知道,如果營銷到位,價格誘人,幾十萬的流量根本不是問題,感覺3-4W的QPS還是可以承受單機Redis的,但是不管有多高,沒辦法。好吧,那么這個數(shù)據(jù)對于一款熱銷產品來說,可能不僅僅是秒殺。
當大量的請求進來時,我們需要考慮的點很多,比如緩存雪崩、緩存擊穿、緩存穿透等。我之前提到的幾點都是可能的。如果出現(xiàn)問題,將很難掛起DB。失敗的用戶體驗差,事件的熱度沒了,最終歸咎于發(fā)展。
超賣:
任何秒殺的東西都怕被超賣。我這里只是以尿布為例。如果換成100臺華為,商家可以通過賣100臺的預算來盈利,也可以造勢。結果,您編寫了錯誤的程序并賣出了 200 個以上。,你不發(fā)貨,用戶投訴你,平臺封你的店鋪,你發(fā)貨的時候血虧。你該怎么辦?
(看敖丙的文章沒問題,我不怕)
最終只能殺了一個開發(fā)者祭天平息。秒殺的價格已經很低了,基本上也不是很賺錢。如果是超賣的話會很嚇人,所以超賣也是一個很關鍵的點。
惡意請求:
你的價格這么低,我搶了就賣,不賺錢?就算不賣,我也不虧。用戶知道,你知道,其他別有用心的人(黑客、黃?!┮脖仨氈?。
這很簡單,我知道你什么時候要抓住它,我會造幾十臺機器,做一些腳本,我還模擬了大約 100,000 人的請求。這是否意味著我基本上有80%的成功率?.
實際情況可能遠不止于此,因為機器請求的速度往往比人手的速度要快得多。在貴州敖丙,我每年回家秒搶高鐵票。不知道黃牛有沒有功勞。,我要diss你,黃牛。買不到周杰倫演唱會的票,我也diss你。
提示:據(jù)科普,我從八卦中了解到,黃牛的搶票系統(tǒng)比國內很多小公司的系統(tǒng)要高很多。建筑設計是一流的。我使用頂級匹配的服務和頂級匹配的架構設計。還想看演唱會?還想回家嗎?
但是沒有黃牛我很難回家。我們云南、桂川和我有太多的孩子要回家過年555!
鏈接曝光:
前幾個問題大家可能都很好理解。看到這里,可能有的朋友會比較疑惑。什么是鏈接曝光?
相信各位開發(fā)同學都熟悉這個畫面。稍微懂一點的可以打開谷歌的開發(fā)者模式,然后看看你的網(wǎng)頁代碼。有些有 URL,但是當我寫 VUE 時,它是一個事件。觸發(fā)然后調用文件中的接口查看源代碼,但是我可以點擊查看你的請求地址,但是你好像可以在秒殺之前把按鈕變灰。
不管是什么,都有危險。除了外面的所有東西,你已經屏蔽了它。你這東西賣得太便宜了,太誘人了。你能保證發(fā)展不會受到誘惑嗎?開發(fā)者知道地址,被殺時提前請求。. . (開發(fā):TM怎么又是我)
數(shù)據(jù)庫:
每秒幾萬甚至幾十萬的QPS( per )直接打到數(shù)據(jù)庫,基本上數(shù)據(jù)庫一定要被打倒,而且你的服務不僅僅是秒殺,還涉及到其他業(yè)務,你沒有降級,限流、保險絲等,其他的掛在一起。如果是小公司,整個網(wǎng)站可能會因為 404 崩潰。
反正不管怎么秒殺,別掛別的,對,掛了也殺不了程序員。
程序員:我好難!
問題都列出來了,那么如何設計,如何解決這些問題,就是接下來要考慮的,對癥下藥。服務單一職責:
設計一個能承受高并發(fā)的系統(tǒng),我覺得還是一個單一的職責。
什么意思,大家都知道現(xiàn)在設計是微服務的設計思路,然后采用分布式部署的方式。
也就是說我們下單的時候有一個訂單服務,一個用戶登錄管理之類的用戶服務,那我們?yōu)槭裁床唤o秒殺開一個服務呢,我們把秒殺的代碼業(yè)務邏輯一起。
為他單獨建立一個數(shù)據(jù)庫。當前的互聯(lián)網(wǎng)架構部署都是子數(shù)據(jù)庫。同樣是訂單服務對應訂單數(shù)據(jù)庫。我們還為他建立了他自己的秒殺數(shù)據(jù)庫。
至于桌子,這取決于你如何設計它。索引應該設置在應該設置索引的地方。記得在構建后使用 SQL 執(zhí)行計劃。(不懂的也可以,我講MySQL章節(jié))
單一職責的好處是即使秒殺失敗,秒殺庫崩潰,服務掛掉,也不會影響其他服務。(強制高可用)
尖峰鏈接與鹽:
上面我們提到,如果鏈接提前暴露,可能會有人直接訪問url,提前殺掉。然后另一個朋友想說我應該檢查時間。那我告訴你,知道鏈接地址比手動點擊頁面還是有很大優(yōu)勢的。
知道url,然后通過程序不斷獲取最新的北京時間,可以達到毫秒級。我將在 00 毫秒時請求它。我敢說,你手動點的成功率肯定高很多,而且我可以在一毫秒內發(fā)送 N 個請求,說不定你賣 100 個產品,我會全部拿走。
如何避免這種情況?
很簡單,讓 URL 動態(tài)化,連寫代碼的人都不知道??梢杂肕D5等加密算法加密一個隨機字符串制作URL,然后通過前端代碼獲取URL,通過后臺驗證。
至于暖男,我準備了一個簡單的url加密給大家試試。你為什么不喜歡它?
Redis 集群:
之前不是說單機上的Redis受不了嗎?再找?guī)讉€兄弟很容易。第二個殺手是多讀少寫。是不是馬上就想起了我之前跟大家提過的,Redis集群,主從同步,讀寫分離,我們還搞了一些哨兵,開啟持久化,直接無敵高可用!
Nginx:
想必大家對 Nginx 都不陌生。這個東西是高性能的web服務器,能處理幾萬并發(fā)不是夢,而我們只能處理幾百個并發(fā)。很簡單,負載均衡,每個服務多少次?數(shù)百人,然后做更多,在高峰期間租用更多的流量機器。
提示:據(jù)我所知,去年春節(jié)期間極度掃描,國內一家大型工廠租用了亞洲所有服務器。小公司也喜歡在雙十一期間購買流量機器來頂住壓力。
您認為您的集群可以做得比這更好嗎?
還需要使用惡意請求攔截。一般來說,單個用戶的請求數(shù)量過于夸張,不像人工請求必須在網(wǎng)關層進行攔截。否則,如果請求太多,他搶不到是一回事,服務器壓力很大。它可能會占用網(wǎng)絡帶寬、使服務器崩潰、破壞緩存等。
資源靜態(tài):
尖峰通常是特定的產品和頁面模板?,F(xiàn)在,前后端一般是分開的,所以頁面一般不經過后端,但是前端也需要自己的服務器,所以可以提前放入cdn服務器。把所有的東西都放進去,反正把所有能提高效率的步驟都做好,減少真正秒殺時對服務器的壓力。
按鈕控制:
有沒有注意到,在秒殺之前,按鈕一般都是灰色的,只有時間到了才能點擊?
這是因為怕大家在最后幾秒的時間里瘋狂的去請求服務器,然后基本秒殺前服務器就掛了。
這時候就需要前端的配合,定時向你的后端服務器請求,獲取最新的北京時間,然后給按鈕一個當時的可用狀態(tài)。
按鈕可以點擊后,一定要灰顯幾秒,否則啟動后他會繼續(xù)點擊。你敢說你秒殺的時候不是這樣嗎?
限制:
限流這里我覺得應該分為前端限流和后端限流。
前端限流:這個很簡單。一般秒殺不會讓你一直點擊。一般點擊一次或兩次,幾秒后即可繼續(xù)點擊。這也是保護服務器的一種手段。
后端限流:秒殺必須涉及訂單生成、支付等后續(xù)操作安卓秒搶注冊機,但只有幸運的成功者才會進入那一步。一旦100個產品售罄,如果發(fā)出false,前端會直接秒殺結束,然后你的后端也會關閉后續(xù)無效請求的干預。
提示:真正的限流還會包括限流元件的加入,比如阿里的等,這里??就不展開了,只說物理限流。
庫存預熱:
秒殺的本質是搶庫存。每個秒殺用戶來找你,你去數(shù)據(jù)庫查庫存,查庫存,然后扣掉庫存。拋開性能因素不談,你不會覺得它對業(yè)務開發(fā)人員如此繁瑣和不友好。而且數(shù)據(jù)庫受不了。
發(fā)展:你tm終于想我一次了。
那該怎么辦?
我們都知道數(shù)據(jù)庫受不了,但是他哥的非關系型數(shù)據(jù)庫Redis卻受得了!
這并不容易。在我們開始秒殺之前,你需要通過定時任務或者運維同學提前將貨物的庫存加載到Redis中,這樣整個過程都在Redis中完成,然后等待秒殺的引入,然后異步修改它。庫存剛剛好。
但是Redis有一個問題。上面我們說我們使用主從,也就是我們會讀取庫存,然后判斷,然后在有庫存的時候減少庫存。正常情況下是沒有問題的,但是高并發(fā)的問題就很嚴重了。大的。
我不會在這里畫圖。本來想畫畫的。想了很久,覺得用語言表達可能更好。
產品多次!?。±?,僅剩 1 個庫存。我們有高并發(fā)。4臺服務器一起查詢,發(fā)現(xiàn)還有1臺。然后大家都以為自己搶到了,所以都扣掉庫存,結果變成A-3夢幻卡通花紋筆刷,是的只有一臺是真的搶到了,其他都超賣了. 該怎么辦?
盧阿:
上一篇文章簡單地提到了他,所以今天我就稍微多一點的篇幅說一下。
Lua腳本功能是Reids在2.6版本中最大的亮點。通過對 Lua 環(huán)境的內置支持,Redis 解決了長期無法高效處理 CAS(check-and-set)命令的問題。組合多個命令可以輕松實現(xiàn)以前難以或不可能有效實現(xiàn)的模式。
Lua腳本類似于Redis事務,具有一定的原子性,不會被其他命令打斷,可以完成一些Redis事務性操作。這是關鍵。
知道了原理,我們就寫一個腳本,把判斷庫存扣減庫存的操作寫在一個腳本里,發(fā)給Redis來做。當它達到0時,它之后的一切都是False,對吧?如果它失敗了,你修改一個開關并直接阻止它。所有的請求,然后做剩下的。
限流&退化&熔斷&隔離:
你為什么要這樣做?不怕一萬,以防萬一,實在受不了了,就限流,受不了的就屏蔽一些,但不能說不,降級,降級或者掛了,F(xiàn)use,至少不要影響其他系統(tǒng),隔離,你是獨立的,但是你會調用其他系統(tǒng),你要死了,不要拖累你的兄弟們。
削峰填谷:
一提到這個詞,很多朋友就知道了。是的,MQ,如果你買的東西少,你直接請求100個請求來改變數(shù)據(jù)庫。我覺得沒問題,但是一秒殺一萬,那十萬呢?服務器掛了,程序員又要承擔責任了。
提示:可能有朋友說我們的業(yè)務達不到這個規(guī)模安卓秒搶注冊機,所以沒必要。但是我想說,我們在寫代碼的時候,不應該寫有邏輯漏洞的代碼。至少在未來,公司規(guī)模會越來越大,其他人一看就不需要改代碼。乍一看,代碼的作者是敖丙?某物!
你可以把它放到消息隊列中,然后花一點點去改變庫存,但是單項修改就足夠了。我這里說的是多個物品在某個時間點一起被殺死的場景,比如雙十一。
總結
至此,我想我已經基本說了應該考慮的點和相應的解決方案。我不知道有沒有我沒有考慮過的,但即使我不考慮我的設計,我也應該能夠支持它?;钜粋€完整的秒殺過程。
最后畫一個完整的流程圖給大家收尾!
提示:這個鏈接比較簡單。繪制所有細節(jié)太復雜了。我已經提到了上面所有的注意點。大家來看看吧。真正的秒殺比我的簡單,還有更多的我復雜N倍。老電商老板干得很高級。如果有機會我可以和你討論。這只是一個采訪。我會給你一些想法,以便你理解更關鍵的點。
這一章腦細胞死了很多,也考慮了很多點,終于出來了,忍不住贊嘆自己!
(這章真不是白賣的,每次看都看不慣。你要不要白賣我?你好不壞,但我很喜歡)