自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

剛入職,就被要求設計百萬人抽獎系統(tǒng)

開發(fā) 架構 開發(fā)工具
今天分享一個美團二面的面試題:如何設計一個百萬人抽獎系統(tǒng)?

[[429398]]

圖片來自 包圖網(wǎng)

架構從來不是設計出來的,而是演進而來的,從一個幾百人的抽獎系統(tǒng)到幾萬人,再到百萬人,不斷增加新的東西。

最后總結歸納一套設計思想,也是萬能模板,這樣面試官問任何高并發(fā)系統(tǒng),只需從這幾個方向去考慮就可以了。

下面我將按照由淺入深的方式進行講解:

V0:單體架構

如果現(xiàn)在讓你實現(xiàn)幾十人的抽獎系統(tǒng),簡單死了吧,直接重拳出擊!

兩貓一豚走江湖,中獎入庫,調通知服務,查庫通知,完美!

相信大家學 Java 時可能都做過這種案例,思考一下存在什么問題?

  • 單體服務,一著不慎滿盤皆輸
  • 抽了再抽,一個人就是一支軍隊
  • 惡意腳本,沒有程序員中不了的獎

接下來就聊聊怎么解決這些問題?

V1:負載均衡

當一臺服務器的單位時間內的訪問量越大時,服務器壓力就越大,大到超過自身承受能力時,服務器就會崩潰。

為了避免服務器崩潰,讓用戶有更好的體驗,我們通過負載均衡的方式來分擔服務器壓力。

負載均衡就是建立很多很多服務器,組成一個服務器集群,當用戶訪問網(wǎng)站時,先訪問一個中間服務器,好比管家,由他在服務器集群中選擇一個壓力較小的服務器,然后將該訪問請求引入該服務器。

如此一來,用戶的每次訪問,都會保證服務器集群中的每個服務器壓力趨于平衡,分擔了服務器壓力,避免了服務器崩潰的情況。

負載均衡是用「反向代理」的原理實現(xiàn)的。具體負載均衡算法及其實現(xiàn)方式我們下文再續(xù)。

負載均衡雖然解決了單體架構一著不慎滿盤皆輸?shù)膯栴},但服務器成本依然不能保護系統(tǒng)周全,我們必須想好一旦服務器宕機,如何保證用戶的體驗。

即如何緩解開獎一瞬間時的大量請求。

V2:服務限流

限流主要的作用是保護服務節(jié)點或者集群后面的數(shù)據(jù)節(jié)點,防止瞬時流量過大使服務和數(shù)據(jù)崩潰(如前端緩存大量實效),造成不可用。還可用于平滑請求。

在上一小節(jié)我們做好了負載均衡來保證集群的可用性,但公司需要需要考慮服務器的成本,不可能無限制的增加服務器數(shù)量,一般會經過計算保證日常的使用沒問題。

限流的意義就在于我們無法預測未知流量,比如剛提到的抽獎可能遇到的:

  • 重復抽獎
  • 惡意腳本

其他一些場景:

  • 熱點事件(微博)
  • 大量爬蟲

這些情況都是無法預知的,不知道什么時候會有 10 倍甚至 20 倍的流量打進來,如果真碰上這種情況,擴容是根本來不及的(彈性擴容都是虛談,一秒鐘你給我擴一下試試)。

明確了限流的意義,我們再來看看如何實現(xiàn)限流。

①防止用戶重復抽獎

重復抽獎和惡意腳本可以歸在一起,同時幾十萬的用戶可能發(fā)出幾百萬的請求。

如果同一個用戶在 1 分鐘之內多次發(fā)送請求來進行抽獎,就認為是惡意重復抽獎或者是腳本在刷獎,這種流量是不應該再繼續(xù)往下請求的,在負載均衡層給直接屏蔽掉。

可以通過 Nginx 配置 ip 的訪問頻率,或者在在網(wǎng)關層結合 Sentinel 配置限流策略。用戶的抽獎狀態(tài)可以通過 Redis 來存儲,后面會說。

②攔截無效流量

無論是抽獎還是秒殺,獎品和商品都是有限的,所以后面涌入的大量請求其實都是無用的。

舉個例子,假設 50 萬人抽獎,就準備了 100 臺手機,那么 50 萬請求瞬間涌入,其實前 500 個請求就把手機搶完了,后續(xù)的幾十萬請求就沒必要讓他再執(zhí)行業(yè)務邏輯,直接暴力攔截返回抽獎結束就可以了。

同時前端在按鈕置灰上也可以做一些文章。那么思考一下如何才能知道獎品抽完了呢,也就是庫存和訂單之前的數(shù)據(jù)同步問題。

③服務降級和服務熔斷

有了以上措施就萬無一失了嗎,不可能的。所以在服務端還有降級和熔斷機制。

有好多人容易混淆這兩個概念,通過一個小例子讓大家明白(請允許一條幻想一下??)。

假設現(xiàn)在一條粉絲數(shù)突破 100 萬,沖上微博熱搜,粉絲甲和粉絲乙都打開微博觀看,但甲看到了一條新聞發(fā)布會的內容,乙卻看到”系統(tǒng)繁忙“,過了一會,乙也能看到內容了。

在上述過程中,首先是熱點時間造成大量請求,發(fā)生了服務熔斷,為了保證整個系統(tǒng)可用,犧牲了部分用戶乙。

乙看到的“系統(tǒng)繁忙“就是服務降級(Fallback),過了一會又恢復訪問,這也是熔斷器的一個特性(Hystrix)。

V3:同步狀態(tài)

接著回到上一節(jié)的問題,如何同步抽獎狀態(tài)?這不得不提到 Redis,被廣泛用于高并發(fā)系統(tǒng)的緩存數(shù)據(jù)庫。

我們可以基于 Redis 來實現(xiàn)這種共享抽獎狀態(tài),它非常輕量級,很適合兩個層次的系統(tǒng)的共享訪問。

當然其實用 ZooKeeper 也是可以的,在負載均衡層可以基于 ZK 客戶端監(jiān)聽某個 Znode 節(jié)點狀態(tài)。

一旦抽獎結束,抽獎服務更新 ZK 狀態(tài),負載均衡層會感知到。

V4:線程優(yōu)化

對于線上環(huán)境,工作線程數(shù)量是一個至關重要的參數(shù),需要根據(jù)自己的情況調節(jié)。

眾所周知,對于進入 Tomcat 的每個請求,其實都會交給一個獨立的工作線程來進行處理,那么 Tomcat 有多少線程,就決定了并發(fā)請求處理的能力。

但是這個線程數(shù)量是需要經過壓測來進行判斷的,因為每個線程都會處理一個請求,這個請求又需要訪問數(shù)據(jù)庫之類的外部系統(tǒng),所以不是每個系統(tǒng)的參數(shù)都可以一樣的,需要自己對系統(tǒng)進行壓測。

但是給一個經驗值的話,Tomcat 的線程數(shù)量不宜過多。因為線程過多,普通服務器的 CPU 是扛不住的,反而會導致機器 CPU 負載過高,最終崩潰。

同時,Tomcat 的線程數(shù)量也不宜太少,因為如果就 100 個線程,那么會導致無法充分利用 Tomcat 的線程資源和機器的 CPU 資源。

所以一般來說,Tomcat 線程數(shù)量在 200~500 之間都是可以的,但是具體多少需要自己壓測一下,不斷的調節(jié)參數(shù),看具體的 CPU 負載以及線程執(zhí)行請求的一個效率。

在 CPU 負載尚可,以及請求執(zhí)行性能正常的情況下,盡可能提高一些線程數(shù)量。

但是如果到一個臨界值,發(fā)現(xiàn)機器負載過高,而且線程處理請求的速度開始下降,說明這臺機扛不住這么多線程并發(fā)執(zhí)行處理請求了,此時就不能繼續(xù)上調線程數(shù)量了。

V5:業(yè)務邏輯

好了,現(xiàn)在該研究一下怎么做抽獎了,抽獎邏輯怎么做?

在負載均衡那個層面,已經把比如 50 萬流量中的 48 萬都攔截掉了,但是可能還是會有 2 萬流量進入抽獎服務。

因為抽獎活動都是臨時服務,可以阿里云租一堆機器,也不是很貴,Tomcat 優(yōu)化完了,服務器的問題也解決了,還剩啥呢?

MySQL,是的,你的 MySQL 能抗住 2 萬的并發(fā)請求嗎?答案是很難,怎么辦呢?

把 MySQL給替換成 Redis,單機抗 2 萬并發(fā)那是很輕松的一件事情。而且 Redis 的一種數(shù)據(jù)結構 set 很適合做抽獎,可以隨機選擇一個元素并剔除。

V6:流量削峰

由上至下,還剩中獎通知部分沒有優(yōu)化。思考這個問題:假設抽獎服務在 2 萬請求中有 1 萬請求抽中了獎品,那么勢必會造成抽獎服務對禮品服務調用 1 萬次。

那也要和抽獎服務同樣處理嗎?其實并不用,因為發(fā)送通知不要求及時性,完全可以讓一萬個請求慢慢發(fā)送,這時就要用到消息中間件,進行限流削峰。

也就是說,抽獎服務把中獎信息發(fā)送到 MQ,然后通知服務慢慢的從 MQ 中消費中獎消息,最終完成完禮品的發(fā)放,這也是我們會延遲一些收到中獎信息或者物流信息的原因。

假設兩個通知服務實例每秒可以完成 100 個通知的發(fā)送,那么 1 萬條消息也就是延遲 100 秒發(fā)放完畢罷了。同樣對 MySQL 的壓力也會降低,那么數(shù)據(jù)庫層面也是可以抗住的。

看一下最終結構圖:

答題模板

所謂答題模板,就是高并發(fā)問題的幾個思考方向和解決方案。

①單一職責

一個基本的設計思想,回想高中物理的串聯(lián)和并聯(lián),串聯(lián)一滅全滅,并聯(lián)各自有一個通路。

一樣的道理,高內聚,低耦合。微服務之所以興起就是因為把復雜的功能進行拆分,即使網(wǎng)站崩了,無法下單,但是瀏覽功能依然健康,而不是所有服務引起連鎖反應,像雪崩一樣,全面癱瘓。

②URL 動態(tài)加密

這說的是防止惡意訪問,有些爬蟲或者刷量腳本會造成大量的請求訪問你的接口,你更加不知道他會傳什么參數(shù)給你,所以我們定義接口時一定要多加驗證,因為不止是你的朋友調你的接口,敵人也有可能。

③靜態(tài)資源——CDN

CDN 全稱內容分發(fā)網(wǎng)絡,是建立并覆蓋在承載網(wǎng)之上,由分布在不同區(qū)域的邊緣節(jié)點服務器群組成的分布式網(wǎng)絡。

通俗的講,就是把經常訪問又費時的資源放在你附近的服務器上。淘寶的圖片訪問,有 98% 的流量都走了 CDN 緩存。只有 2% 會回源到源站,節(jié)省了大量的服務器資源。

但是,如果在用戶訪問高峰期,圖片內容大批量發(fā)生變化,大量用戶的訪問就會穿透 CDN,對源站造成巨大的壓力。所以,對于圖片這種靜態(tài)資源,盡可能都放入 CDN。

④服務限流

在上面已有講解,可分為前端限流和后端限流:

  • 前端:按鈕禁用,ip 黑名單
  • 后端:服務熔斷,服務降級,權限驗證

⑤數(shù)據(jù)預熱

可以采用定時任務(elastic-job)實時查詢 Druid,把熱點數(shù)據(jù)放入 Redis 緩存中。

思考一個問題:比如現(xiàn)在庫存只剩下 1 個了,我們高并發(fā)嘛,4 個服務器一起查詢了發(fā)現(xiàn)都是還有 1 個,那大家都覺得是自己搶到了,就都去扣庫存,那結果就變成了 -3,是的只有一個是真的搶到了,別的都是超賣的。咋辦?

回答:可以用 CAS+LUA 腳本實現(xiàn)。Lua 腳本是類似 Redis 事務,有一定的原子性,不會被其他命令插隊,可以完成一些 Redis 事務性的操作。這點是關鍵。

寫一個腳本把判斷庫存扣減庫存的操作都寫在一個腳本丟給 Redis 去做,那到 0 了后面的都 Return False 了是吧,一個失敗了你修改一個開關,直接擋住所有的請求。

⑥削峰填谷

精通一個中間件會給你加分很多,消息隊列已經逐漸成為企業(yè) IT 系統(tǒng)內部通信的核心手段。

它具有低耦合、可靠投遞、廣播、流量控制、最終一致性等一系列功能,成為異步 RPC 的主要手段之一。

當今市面上有很多主流的消息中間件,如老牌的 ActiveMQ、RabbitMQ,炙手可熱的 Kafka,阿里巴巴自主開發(fā) RocketMQ 等。

最原始的 MQ,生產者先將消息投遞一個叫做「隊列」的容器中,然后再從這個容器中取出消息,最后再轉發(fā)給消費者,僅此而已。

今天就學這么多,相信大家都對高并發(fā)系統(tǒng)有了初步的認識,面試官問起來也不至于無話可說,但是想要學好任重而道遠!

作者:一條 IT

編輯:陶家龍

出處:轉載自公眾號一條 coding(ID:a18741865729)

 

責任編輯:武曉燕 來源: 一條 coding
相關推薦

2019-06-19 10:57:48

新人入職代碼

2024-07-29 08:01:32

2021-07-19 08:41:49

藍屏用戶Bug

2019-11-25 21:53:48

代碼算法BUG

2023-04-03 07:12:07

2011-12-14 20:01:39

蘋果

2020-11-03 07:48:47

當AI入職FBI

2022-05-11 11:37:21

跳槽保密協(xié)議字節(jié)

2023-11-03 12:05:43

2022-05-10 09:38:46

加密貨幣詐騙網(wǎng)絡安全

2023-07-06 09:01:33

2022-03-16 09:27:29

ICT

2024-07-24 20:01:03

2015-08-17 11:46:50

2016-11-18 13:15:02

廣電電視推流網(wǎng)

2017-07-17 09:54:43

代碼C語言功能

2020-01-19 09:12:07

入職阿里技術

2010-07-06 16:53:55

入職

2021-07-14 06:57:54

人工智能AI劉慈欣

2017-01-15 07:55:19

Swift蘋果離職
點贊
收藏

51CTO技術棧公眾號