亚洲熟女综合色一区二区三区,亚洲精品中文字幕无码蜜桃,亚洲va欧美va日韩va成人网,亚洲av无码国产一区二区三区,亚洲精品无码久久久久久久

超詳細(xì)的秒殺架構(gòu)設(shè)計(jì),運(yùn)維,了解一下

秒殺系統(tǒng)相信很多人見(jiàn)過(guò),比如京東或者淘寶的秒殺,小米手機(jī)的秒殺,那么秒殺系統(tǒng)的后臺(tái)是如何實(shí)現(xiàn)的呢?我們?nèi)绾卧O(shè)計(jì)一個(gè)秒殺系統(tǒng)呢?對(duì)于秒殺系統(tǒng)應(yīng)該考慮哪些問(wèn)題?如何設(shè)計(jì)出健壯的秒殺系統(tǒng)?本文我們就來(lái)探討一下這個(gè)問(wèn)題。
超詳細(xì)的秒殺架構(gòu)設(shè)計(jì),運(yùn)維,了解一下

秒殺應(yīng)該考慮哪些問(wèn)題

超賣(mài)問(wèn)題

分析秒殺的業(yè)務(wù)場(chǎng)景,最重要的有一點(diǎn)就是超賣(mài)問(wèn)題,假如備貨只有100個(gè),但是最終超賣(mài)了200,一般來(lái)講秒殺系統(tǒng)的價(jià)格都比較低,如果超賣(mài)將嚴(yán)重影響公司的財(cái)產(chǎn)利益,因此首當(dāng)其沖的就是解決商品的超賣(mài)問(wèn)題。

高并發(fā)

秒殺具有時(shí)間短、并發(fā)量大的特點(diǎn),秒殺持續(xù)時(shí)間只有幾分鐘,而一般公司都為了制造轟動(dòng)效應(yīng),會(huì)以極低的價(jià)格來(lái)吸引用戶(hù),因此參與搶購(gòu)的用戶(hù)會(huì)非常的多。短時(shí)間內(nèi)會(huì)有大量請(qǐng)求涌進(jìn)來(lái),后端如何防止并發(fā)過(guò)高造成緩存擊穿或者失效,擊垮數(shù)據(jù)庫(kù)都是需要考慮的問(wèn)題。

接口防刷

現(xiàn)在的秒殺大多都會(huì)出來(lái)針對(duì)秒殺對(duì)應(yīng)的軟件,這類(lèi)軟件會(huì)模擬不斷向后臺(tái)服務(wù)器發(fā)起請(qǐng)求,一秒幾百次都是很常見(jiàn)的,如何防止這類(lèi)軟件的重復(fù)無(wú)效請(qǐng)求,防止不斷發(fā)起的請(qǐng)求也是需要我們針對(duì)性考慮的。

秒殺 URL

對(duì)于普通用戶(hù)來(lái)講,看到的只是一個(gè)比較簡(jiǎn)單的秒殺頁(yè)面,在未達(dá)到規(guī)定時(shí)間,秒殺按鈕是灰色的,一旦到達(dá)規(guī)定時(shí)間,灰色按鈕變成可點(diǎn)擊狀態(tài)。這部分是針對(duì)小白用戶(hù)的,如果是稍微有點(diǎn)電腦功底的用戶(hù),會(huì)通過(guò)F12看瀏覽器的network看到秒殺的url,通過(guò)特定軟件去請(qǐng)求也可以實(shí)現(xiàn)秒殺?;蛘咛崆爸烂霘rl的人,一請(qǐng)求就直接實(shí)現(xiàn)秒殺了。這個(gè)問(wèn)題我們需要考慮解決。

數(shù)據(jù)庫(kù)設(shè)計(jì)

秒殺有把我們服務(wù)器擊垮的風(fēng)險(xiǎn),如果讓它與我們的其他業(yè)務(wù)使用在同一個(gè)數(shù)據(jù)庫(kù)中,耦合在一起,就很有可能牽連和影響其他的業(yè)務(wù)。如何防止這類(lèi)問(wèn)題發(fā)生,就算秒殺發(fā)生了宕機(jī)、服務(wù)器卡死問(wèn)題,也應(yīng)該讓他盡量不影響線上正常進(jìn)行的業(yè)務(wù)。

大量請(qǐng)求問(wèn)題

按照「高并發(fā)」的考慮,就算使用緩存還是不足以應(yīng)對(duì)短時(shí)間的高并發(fā)的流量的沖擊。如何承載這樣巨大的訪問(wèn)量,同時(shí)提供穩(wěn)定低時(shí)延的服務(wù)保證,是需要面對(duì)的一大挑戰(zhàn)。我們來(lái)算一筆賬,假如使用的是 Redis 緩存,單臺(tái) Redis 服務(wù)器可承受的 QPS 大概是 4W 左右,如果一個(gè)秒殺吸引的用戶(hù)量足夠多的話,單 QPS 可能達(dá)到幾十萬(wàn),單體 Redis 還是不足以支撐如此巨大的請(qǐng)求量。緩存會(huì)被擊穿,直接滲透到 DB,從而擊垮MySQL,后臺(tái)會(huì)將會(huì)大量報(bào)錯(cuò)。

秒殺系統(tǒng)的設(shè)計(jì)和技術(shù)方案

秒殺系統(tǒng)數(shù)據(jù)庫(kù)設(shè)計(jì)

針對(duì)「數(shù)據(jù)庫(kù)設(shè)計(jì)」提出的秒殺數(shù)據(jù)庫(kù)的問(wèn)題,因此應(yīng)該單獨(dú)設(shè)計(jì)一個(gè)秒殺數(shù)據(jù)庫(kù),防止因?yàn)槊霘⒒顒?dòng)的高并發(fā)訪問(wèn)拖垮整個(gè)網(wǎng)站。這里只需要兩張表,一張是秒殺訂單表,一張是秒殺貨品表。
超詳細(xì)的秒殺架構(gòu)設(shè)計(jì),運(yùn)維,了解一下
其實(shí)應(yīng)該還有幾張表,商品表:可以關(guān)聯(lián)goods_id查到具體的商品信息,商品圖像、名稱(chēng)、平時(shí)價(jià)格、秒殺價(jià)格等,還有用戶(hù)表:根據(jù)用戶(hù)user_id可以查詢(xún)到用戶(hù)昵稱(chēng)、用戶(hù)手機(jī)號(hào),收貨地址等其他額外信息,這個(gè)具體就不給出實(shí)例了。

秒殺 URL 的設(shè)計(jì)

為了避免有程序訪問(wèn)經(jīng)驗(yàn)的人通過(guò)下單頁(yè)面url直接訪問(wèn)后臺(tái)接口來(lái)秒殺貨品,我們需要將秒殺的 URL 實(shí)現(xiàn)動(dòng)態(tài)化,即使是開(kāi)發(fā)整個(gè)系統(tǒng)的人都無(wú)法在秒殺開(kāi)始前知道秒殺的URL 。具體的做法就是通過(guò) md5 加密一串隨機(jī)字符作為秒殺的 URL,然后前端訪問(wèn)后臺(tái)獲取具體的 URL,后臺(tái)校驗(yàn)通過(guò)之后才可以繼續(xù)秒殺。

秒殺頁(yè)面靜態(tài)化

將商品的描述、參數(shù)、成交記錄、圖像、評(píng)價(jià)等全部寫(xiě)入到一個(gè)靜態(tài)頁(yè)面,用戶(hù)請(qǐng)求不需要通過(guò)訪問(wèn)后端服務(wù)器,不需要經(jīng)過(guò)數(shù)據(jù)庫(kù),直接在前臺(tái)客戶(hù)端生成,這樣可以最大可能的減少服務(wù)器的壓力。具體的方法可以使用freemarker模板技術(shù),建立網(wǎng)頁(yè)模板,填充數(shù)據(jù),然后渲染網(wǎng)頁(yè)。

單體 Redis 升級(jí)為集群 Redis

秒殺是一個(gè)讀多寫(xiě)少的場(chǎng)景,使用 Redis 做緩存再合適不過(guò)。不過(guò)考慮到緩存擊穿問(wèn)題,我們應(yīng)該構(gòu)建 Redis 集群,采用哨兵模式,可以提升Redis的性能和可用性。

使用 Nginx

Nginx 是一個(gè)高性能 Web 服務(wù)器,它的并發(fā)能力可以達(dá)到幾萬(wàn),而 Tomcat 只有幾百。通過(guò) Nginx 映射客戶(hù)端請(qǐng)求,再分發(fā)到后臺(tái) Tomcat 服務(wù)器集群中可以大大提升并發(fā)能力。

精簡(jiǎn) SQL

典型的一個(gè)場(chǎng)景是在進(jìn)行扣減庫(kù)存的時(shí)候,傳統(tǒng)的做法是先查詢(xún)庫(kù)存,再去update。這樣的話需要兩個(gè)SQL,而實(shí)際上一個(gè)SQL我們就可以完成的??梢杂眠@樣的做法:update miaosha_goods set stock =stock-1 where goods_id ={#goods_id} and version = #{version} and stock>0;這樣的話,就可以保證庫(kù)存不會(huì)超賣(mài)并且一次更新庫(kù)存,還有注意一點(diǎn)這里使用了版本號(hào)的樂(lè)觀鎖,相比較悲觀鎖,它的性能較好。

Redis 預(yù)減庫(kù)存

很多請(qǐng)求進(jìn)來(lái),都需要后臺(tái)查詢(xún)庫(kù)存,這是一個(gè)頻繁讀的場(chǎng)景??梢允褂肦edis來(lái)預(yù)減庫(kù)存,在秒殺開(kāi)始前可以在 Redis 設(shè)值,比如 redis.set(goodsId,100),這里預(yù)放的庫(kù)存為100可以設(shè)值為常量,每次下單成功之后,Integer stock = (Integer)redis.get(goosId); 然后判斷 sock 的值,如果小于常量值就減去1;不過(guò)注意當(dāng)取消的時(shí)候,需要增加庫(kù)存,增加庫(kù)存的時(shí)候也得注意不能大于之間設(shè)定的總庫(kù)存數(shù)(查詢(xún)庫(kù)存和扣減庫(kù)存需要原子操作,此時(shí)可以借助 lua 腳本)下次下單再獲取庫(kù)存的時(shí)候,直接從Redis里面查就可以了。

接口限流

秒殺最終的本質(zhì)是數(shù)據(jù)庫(kù)的更新,但是有很多大量無(wú)效的請(qǐng)求,我們最終要做的就是如何把這些無(wú)效的請(qǐng)求過(guò)濾掉,防止?jié)B透到數(shù)據(jù)庫(kù)。限流的話,需要入手的方面很多:

前端限流
首先第一步就是通過(guò)前端限流,用戶(hù)在秒殺按鈕點(diǎn)擊以后發(fā)起請(qǐng)求,那么在接下來(lái)的5秒是無(wú)法點(diǎn)擊(通過(guò)設(shè)置按鈕為disable)。這一小舉措開(kāi)發(fā)起來(lái)成本很小,但是很有效。

同一個(gè)用戶(hù)xx秒內(nèi)重復(fù)請(qǐng)求直接拒絕

具體多少秒需要根據(jù)實(shí)際業(yè)務(wù)和秒殺的人數(shù)而定,一般限定為10秒。具體的做法就是通過(guò)Redis的鍵過(guò)期策略,首先對(duì)每個(gè)請(qǐng)求都從 String value = redis.get(userId);如果獲取到這個(gè) value 為空或者為 null,表示它是有效的請(qǐng)求,然后放行這個(gè)請(qǐng)求。如果不為空表示它是重復(fù)性請(qǐng)求,直接丟掉這個(gè)請(qǐng)求。如果有效,采用redis.setexpire(userId,value,10).value 可以是任意值,一般放業(yè)務(wù)屬性比較好,這個(gè)是設(shè)置以 userId 為 key,10秒的過(guò)期時(shí)間(10秒后,key對(duì)應(yīng)的值自動(dòng)為null)。

令牌桶算法限流

接口限流的策略有很多,我們這里采用令牌桶算法。令牌桶算法的基本思路是每個(gè)請(qǐng)求嘗試獲取一個(gè)令牌,后端只處理持有令牌的請(qǐng)求,生產(chǎn)令牌的速度和效率我們都可以自己限定,Guava 提供了 RateLimter 的 API 供我們使用。以下做一個(gè)簡(jiǎn)單的例子,注意需要引入Guava:
超詳細(xì)的秒殺架構(gòu)設(shè)計(jì),運(yùn)維,了解一下
上面代碼的思路就是通過(guò)RateLimiter來(lái)限定我們的令牌桶每秒產(chǎn)生1個(gè)令牌(生產(chǎn)的效率比較低),循環(huán)10次去執(zhí)行任務(wù)。acquire會(huì)阻塞當(dāng)前線程直到獲取到令牌,也就是如果任務(wù)沒(méi)有獲取到令牌,會(huì)一直等待。那么請(qǐng)求就會(huì)卡在我們限定的時(shí)間內(nèi)才可以繼續(xù)往下走,這個(gè)方法返回的是線程具體等待的時(shí)間。執(zhí)行如下:
超詳細(xì)的秒殺架構(gòu)設(shè)計(jì),運(yùn)維,了解一下
可以看到任務(wù)執(zhí)行的過(guò)程中,第1個(gè)是無(wú)需等待的,因?yàn)橐呀?jīng)在開(kāi)始的第1秒生產(chǎn)出了令牌。接下來(lái)的任務(wù)請(qǐng)求就必須等到令牌桶產(chǎn)生了令牌才可以繼續(xù)往下執(zhí)行。如果沒(méi)有獲取到就會(huì)阻塞(有一個(gè)停頓的過(guò)程)。不過(guò)這個(gè)方式不太好,因?yàn)橛脩?hù)如果在客戶(hù)端請(qǐng)求,如果較多的話,直接后臺(tái)在生產(chǎn)token就會(huì)卡頓(用戶(hù)體驗(yàn)較差),它是不會(huì)拋棄任務(wù)的,我們需要一個(gè)更優(yōu)秀的策略:如果超過(guò)某個(gè)時(shí)間沒(méi)有獲取到,直接拒絕該任務(wù)。接下來(lái)再來(lái)個(gè)案例:
超詳細(xì)的秒殺架構(gòu)設(shè)計(jì),運(yùn)維,了解一下
其中用到了tryAcquire方法,這個(gè)方法的主要作用是設(shè)定一個(gè)超時(shí)的時(shí)間,如果在指定的時(shí)間內(nèi)預(yù)估(注意是預(yù)估并不會(huì)真實(shí)的等待),如果能拿到令牌就返回true,如果拿不到就返回false。然后我們讓無(wú)效的直接跳過(guò),這里設(shè)定每秒生產(chǎn)1個(gè)令牌,讓每個(gè)任務(wù)嘗試在0.5秒獲取令牌,如果獲取不到,就直接跳過(guò)這個(gè)任務(wù)(放在秒殺環(huán)境里就是直接拋棄這個(gè)請(qǐng)求)。程序?qū)嶋H運(yùn)行如下:超詳細(xì)的秒殺架構(gòu)設(shè)計(jì),運(yùn)維,了解一下
只有第1個(gè)獲取到了令牌,順利執(zhí)行了,下面的基本都直接拋棄了,因?yàn)?.5秒內(nèi),令牌桶(1秒1個(gè))來(lái)不及生產(chǎn)就肯定獲取不到返回false了。
這個(gè)限流策略的效率有多高呢?假如我們的并發(fā)請(qǐng)求是400萬(wàn)瞬間的請(qǐng)求,將令牌產(chǎn)生的效率設(shè)為每秒20個(gè),每次嘗試獲取令牌的時(shí)間是0.05秒,那么最終測(cè)試下來(lái)的結(jié)果是,每次只會(huì)放行4個(gè)左右的請(qǐng)求,大量的請(qǐng)求會(huì)被拒絕,這就是令牌桶算法的優(yōu)秀之處。

異步下單

為了提升下單的效率,并且防止下單服務(wù)的失敗。需要將下單這一操作進(jìn)行異步處理。最常采用的辦法是使用隊(duì)列,隊(duì)列最顯著的三個(gè)優(yōu)點(diǎn):異步、削峰、解耦。這里可以采用 RabbitMQ,在后臺(tái)經(jīng)過(guò)了限流、庫(kù)存校驗(yàn)之后,流入到這一步驟的就是有效請(qǐng)求。然后發(fā)送到隊(duì)列里,隊(duì)列接受消息,異步下單。下完單,入庫(kù)沒(méi)有問(wèn)題可以用短信通知用戶(hù)秒殺成功。假如失敗的話,可以采用補(bǔ)償機(jī)制,重試。

服務(wù)降級(jí)

假如在秒殺過(guò)程中出現(xiàn)了某個(gè)服務(wù)器宕機(jī),或者服務(wù)不可用,應(yīng)該做好后備工作。之前的博客里有介紹通過(guò)Hystrix進(jìn)行服務(wù)熔斷和降級(jí),可以開(kāi)發(fā)一個(gè)備用服務(wù),假如服務(wù)器真的宕機(jī)了,直接給用戶(hù)一個(gè)友好的提示返回,而不是直接卡死,服務(wù)器錯(cuò)誤等生硬的反饋。

總結(jié)

秒殺流程圖:
超詳細(xì)的秒殺架構(gòu)設(shè)計(jì),運(yùn)維,了解一下
這就是我設(shè)計(jì)出來(lái)的秒殺流程圖,當(dāng)然不同的秒殺體量針對(duì)的技術(shù)選型都不一樣,這個(gè)流程可以支撐起幾十萬(wàn)的流量,如果是成千萬(wàn)破億那就得重新設(shè)計(jì)了。比如數(shù)據(jù)庫(kù)的分庫(kù)分表、隊(duì)列改成用Kafka、Redis增加集群數(shù)量等手段。通過(guò)本次設(shè)計(jì)主要是要表明的是我們?nèi)绾螒?yīng)對(duì)高并發(fā)的處理,并開(kāi)始嘗試解決它,在工作中多思考、多動(dòng)手能提升我們的能力水平,加油!如果本篇博客有任何錯(cuò)誤,請(qǐng)麻煩指出來(lái),不勝感激。
來(lái)源:https://www.cnblogs.com/wyq178/p/11261711.html

文章轉(zhuǎn)載:高效運(yùn)維
(版權(quán)歸原作者所有,侵刪)

相關(guān)新聞

歷經(jīng)多年發(fā)展,已成為國(guó)內(nèi)好評(píng)如潮的Linux云計(jì)算運(yùn)維、SRE、Devops、網(wǎng)絡(luò)安全、云原生、Go、Python開(kāi)發(fā)專(zhuān)業(yè)人才培訓(xùn)機(jī)構(gòu)!