編者按:高可用架構(gòu)推出2015年度案例系列文章,分享在架構(gòu)領(lǐng)域具有典型意義的年度案例,本文根據(jù)張開濤分享的大型電商網(wǎng)站應(yīng)對雙十一的架構(gòu)案例記錄。轉(zhuǎn)載請注明高可用架構(gòu)公眾號ArchNotes。
京東商品詳情頁技術(shù)方案在之前《億級商品詳情頁架構(gòu)演進(jìn)技術(shù)解密》這篇文章已經(jīng)為大家揭秘了,接下來為大家揭秘下雙十一抗下幾十億流量的商品詳情頁統(tǒng)一服務(wù)架構(gòu)。 這次雙十一整個(gè)商品詳情頁在流量增長數(shù)倍的情況下,沒有出現(xiàn)不可用的情況,服務(wù)非常穩(wěn)定。 統(tǒng)一服務(wù)提供了:促銷和廣告詞合并服務(wù)、庫存狀態(tài)/配送至服務(wù)、延保服務(wù)、試用服務(wù)、推薦服務(wù)、圖書相關(guān)服務(wù)、詳情頁優(yōu)惠券服務(wù)、今日抄底服務(wù)等服務(wù)支持。 其實(shí)就是一個(gè)大的網(wǎng)關(guān),提供各種服務(wù)。但區(qū)別于一般網(wǎng)關(guān),它還提供業(yè)務(wù)邏輯的處理。這些服務(wù)中有我們自己做的服務(wù)實(shí)現(xiàn),而有些是簡單做下代理或者接口做了合并輸出到頁面,我們聚合這些服務(wù)到一個(gè)系統(tǒng)的目的是打造服務(wù)閉環(huán),優(yōu)化現(xiàn)有服務(wù)。并為未來需求做準(zhǔn)備,跟著自己的方向走,而不被別人亂了我們的方向。這樣一起盡在自己的掌控之中,想怎么玩就看心情了。 大家在頁面中看到的c./c0./c1./cd.jd.com請求都是統(tǒng)一服務(wù)的入口。我們分別為有狀態(tài)服務(wù)和無狀態(tài)服務(wù)提供了不同的域名,而且域名還做了分區(qū)。 為什么需要統(tǒng)一服務(wù)? 商品詳情頁雖然只有一個(gè)頁面,但是依賴的服務(wù)眾多,我們需要把控好入口,一統(tǒng)化管理。 這樣的好處:
有了它,所有入口都在我們服務(wù)中,我們可以更好的監(jiān)控和思考我們頁面的服務(wù),讓我們能運(yùn)籌于帷幄之中,決勝于千里之外。 在設(shè)計(jì)一個(gè)高度靈活的系統(tǒng)時(shí),要想著當(dāng)出現(xiàn)問題時(shí)怎么辦:是否可降級、不可降級怎么處理、是否會發(fā)送滾雪球問題、如何快速響應(yīng)異常。完成了系統(tǒng)核心邏輯只是保證服務(wù)能工作,服務(wù)如何更好更有效或者在異常情況下能正常工作也是我們要深入思考和解決的問題。 整體架構(gòu) 整體流程
如上是整個(gè)邏輯流程,可以看到我們在Nginx這一層做了很多前置邏輯處理,以此來減少后端壓力,另外我們Redis集群分機(jī)房部署,如下圖所示: 即數(shù)據(jù)會寫一個(gè)主集群,然后通過主從方式把數(shù)據(jù)復(fù)制到其他機(jī)房,而各個(gè)機(jī)房讀自己的集群;此處沒有在各個(gè)機(jī)房做一套獨(dú)立的集群來保證機(jī)房之間沒有交叉訪問,這樣做的目的是保證數(shù)據(jù)一致性。 在這套新架構(gòu)中,我們可以看到Nginx+Lua已經(jīng)是我們應(yīng)用的一部分,我們在實(shí)際使用中,也是把它做為項(xiàng)目開發(fā),做為應(yīng)用進(jìn)行部署。 我們主要遵循如下幾個(gè)原則設(shè)計(jì)系統(tǒng)架構(gòu):
兩種讀服務(wù)架構(gòu)模式
可以看到Nginx應(yīng)用和Redis單獨(dú)部署,這種方式是一般應(yīng)用的部署模式,也是我們統(tǒng)一服務(wù)的部署模式,此處會存在跨機(jī)器、跨交換機(jī)或跨機(jī)柜讀取Redis緩存的情況,但是不存在跨機(jī)房情況,因?yàn)槭褂萌萜骰惶米霾豢鐧C(jī)柜的應(yīng)用了。通過主從把數(shù)據(jù)復(fù)制到各個(gè)機(jī)房。如果對性能要求不是非??量蹋梢钥紤]這種架構(gòu),比較容易維護(hù)。
可以看到Nginx應(yīng)用和Redis集群部署在同一臺機(jī)器,這樣好處可以消除跨機(jī)器、跨交換機(jī)或跨機(jī)柜,甚至跨機(jī)房調(diào)用。如果本地Redis集群不命中, 還是回源到Tomcat集群進(jìn)行取數(shù)據(jù)。此種方式可能受限于TCP連接數(shù),可以考慮使用unix domain socket套接字減少本機(jī)TCP連接數(shù)。如果單機(jī)內(nèi)存成為瓶頸(比如單機(jī)內(nèi)存最大256GB),就需要路由機(jī)制來進(jìn)行Sharding,比如按照商品尾號Sharding,Redis集群一般采用樹狀結(jié)構(gòu)掛主從部署。 本地緩存 我們把Nginx作為應(yīng)用部署,因此大量使用Nginx共享字典作為本地緩存,Nginx+Lua架構(gòu)中,使用HttpLuaModule模塊的shared dict做本地緩存( reload不丟失)或內(nèi)存級Proxy Cache,提升緩存帶來的性能并減少帶寬消耗。 之前的詳情頁架構(gòu)也是采用這種緩存。另外使用一致性哈希(如商品編號/分類)做負(fù)載均衡內(nèi)部對URL重寫提升命中率。 在緩存數(shù)據(jù)時(shí)采用了維度化存儲緩存數(shù)據(jù),增量獲取失效緩存數(shù)據(jù)(比如10個(gè)數(shù)據(jù),3個(gè)沒命中本地緩存,只需要取這3個(gè)即可)。維度如商家信息、店鋪信息、商家評分、店鋪頭、品牌信息、分類信息等;比如本地緩存30分鐘,調(diào)用量減少差不多3倍。 另外使用一致性哈希+本地緩存,如庫存數(shù)據(jù)緩存5秒,平常命中率:本地緩存25%;分布式Redis28%;回源47%。一次普通秒殺活動命中率:本地緩存 58%;分布式Redis 15%;回源27%。而某個(gè)服務(wù)使用一致哈希后命中率提升10%。對URL按照規(guī)則重寫作為緩存KEY,去隨機(jī),即頁面URL不管怎么變都不要讓它成為緩存不命中的因素。 多級緩存 對于讀服務(wù),在設(shè)計(jì)時(shí)會使用多級緩存來盡量減少后端服務(wù)壓力,在統(tǒng)一服務(wù)系統(tǒng)中,設(shè)計(jì)了四級緩存,如下圖所示:
統(tǒng)一入口/服務(wù)閉環(huán) 在《億級商品詳情頁架構(gòu)演進(jìn)技術(shù)解密》中已經(jīng)講過了數(shù)據(jù)異構(gòu)閉環(huán)的收益,在統(tǒng)一服務(wù)中也遵循這個(gè)設(shè)計(jì)原則,此處主要做了兩件事情
有些是查庫/緩存然后做一些業(yè)務(wù)邏輯,有些是http接口調(diào)用然后進(jìn)行簡單的數(shù)據(jù)邏輯處理;還有一些就是做了下簡單的代理,并監(jiān)控接口服務(wù)質(zhì)量。 引入Nginx接入層 在設(shè)計(jì)系統(tǒng)時(shí)需要把一些邏輯盡可能前置以此來減輕后端核心邏輯的壓力。另外如服務(wù)升級/服務(wù)降級能非常方便的進(jìn)行切換,在接入層做了如下事情:
服務(wù)有兩種類型的接口:一種是用戶無關(guān)的接口,另一種則是用戶相關(guān)的接口。因此使用了兩種類型的域名c./c0./c1.和cd.jd.com。 當(dāng)請求cd.jd.com會帶著用戶cookie信息到服務(wù)端。在服務(wù)器上會進(jìn)行請求頭的處理,用戶無關(guān)的所有數(shù)據(jù)通過參數(shù)傳遞,在接入層會丟棄所有的請求頭(保留gzip相關(guān)的頭)。 而用戶相關(guān)的會從cookie中解出用戶信息然后通過參數(shù)傳遞到后端;也就是后端應(yīng)用從來就不關(guān)心請求頭及Cookie信息,所有信息通過參數(shù)傳遞。 有些是查庫/緩存然后做一些業(yè)務(wù)邏輯,有些是http接口調(diào)用然后進(jìn)行簡單的數(shù)據(jù)邏輯處理;還有一些就是做了下簡單的代理,并監(jiān)控接口服務(wù)質(zhì)量。 請求進(jìn)入接入層后,會對參數(shù)進(jìn)行校驗(yàn),如果參數(shù)校驗(yàn)不合法直接拒絕這次請求。對每個(gè)請求的參數(shù)進(jìn)行了最嚴(yán)格的數(shù)據(jù)校驗(yàn)處理,保證數(shù)據(jù)的有效性。 如圖所示,我們對關(guān)鍵參數(shù)進(jìn)行了過濾,如果這些參數(shù)不合法就直接拒絕請求。另外還會對請求的參數(shù)進(jìn)行過濾然后重新按照固定的模式重新拼裝URL調(diào)度到后端應(yīng)用。此時(shí)URL上的參數(shù)是固定的而且是有序的,可以按照URL進(jìn)行緩存 緩存前置 很多緩存都前置到了接入層,來進(jìn)行熱點(diǎn)數(shù)據(jù)的削峰,而且配合一致性哈??赡芴嵘彺娴拿新省T诰彺鏁r(shí)按照業(yè)務(wù)來設(shè)置緩存池,減少相互之間的影響和提升并發(fā),使用Lua讀取共享字典來實(shí)現(xiàn)本地緩存。 業(yè)務(wù)邏輯前置 在接入層直接實(shí)現(xiàn)了一些業(yè)務(wù)邏輯,原因是當(dāng)在高峰時(shí)出問題,可以在這一層做一些邏輯升級。 后端是Java應(yīng)用,當(dāng)修復(fù)邏輯時(shí)需要上線,而一次上線可能花費(fèi)數(shù)十秒時(shí)間啟動應(yīng)用。重啟應(yīng)用后Java應(yīng)用JIT的問題會存在性能抖動的問題??赡芤?yàn)橹貑⒃斐煞?wù)一直啟動不起來的問題。而在Nginx中做這件事情,改完代碼推送到服務(wù)器,重啟只需要秒級,而且不存在抖動的問題。這些邏輯都是在Lua中完成。 降級開關(guān)前置 降級開關(guān)分為這么幾種:
另外還可以根據(jù)服務(wù)重要程度來使用超時(shí)自動降級機(jī)制。使用init_by_lua_file初始化開關(guān)數(shù)據(jù),共享字典存儲開關(guān)數(shù)據(jù)。提供API進(jìn)行開關(guān)切換(switch_get(“stock.api.not.call”) ~= “1”)??梢詫?shí)現(xiàn):秒級切換開關(guān)、增量式切換開關(guān)(可以按照機(jī)器組開啟,而不是所有都開啟)、功能切換開關(guān)、細(xì)粒度服務(wù)降級開關(guān)、非核心服務(wù)可以超時(shí)自動降級。 比如雙十一期間有些服務(wù)出問題了,進(jìn)行過大服務(wù)和小服務(wù)的降級操作,這些操作對用戶來說都是無感知的。有一個(gè)后臺可以直接控制開關(guān),切換非常方便。 AB測試 對于服務(wù)升級,最重要的就是能做AB測試,然后根據(jù)AB測試的結(jié)果來看是否切新服務(wù)。這一塊算是家常便飯。有了接入層非常容易進(jìn)行這種AB測試;不管是上線還是切換都非常容易。 可以動態(tài)改Nginx配置、推送然后reload。也可以在Lua中根據(jù)請求的信息調(diào)用不同的服務(wù)或者upstream分組即可完成AB測試。因?yàn)橛辛薒ua,可以實(shí)現(xiàn)復(fù)雜邏輯的編程,比如根據(jù)商品屬性進(jìn)行切換。 灰度發(fā)布/流量切換 對于一個(gè)靈活的系統(tǒng)來說,能隨時(shí)進(jìn)行灰度發(fā)布和流量切換是非常重要的一件事情。比如驗(yàn)證新服務(wù)器是否穩(wěn)定,或者驗(yàn)證新的架構(gòu)是否比老架構(gòu)更優(yōu)秀,有時(shí)候只有在線上跑著才能看出是否有問題。 在接入層也是通過配置或Lua代碼來完成這些事情。靈活性非常好,可以設(shè)置多個(gè)upstream分組,然后根據(jù)需要切換分組即可。 監(jiān)控服務(wù)質(zhì)量 對于一個(gè)系統(tǒng)最重要的是要有雙眼睛能盯著系統(tǒng)來盡可能早的發(fā)現(xiàn)問題。在接入層會對請求進(jìn)行代理,記錄status、request_time、response_time來監(jiān)控服務(wù)質(zhì)量,比如根據(jù)調(diào)用量、狀態(tài)碼是否是200、響應(yīng)時(shí)間來告警。 這些都是通過Nginx子請求記錄的。 限流 系統(tǒng)中存在的主要限流邏輯是:
前端業(yè)務(wù)邏輯后置 前端JS應(yīng)該盡可能少的業(yè)務(wù)邏輯和一些切換邏輯,因?yàn)榍岸薐S一般推送到CDN。假設(shè)邏輯出問題了,需要更新代碼上線,推送到CDN然后失效各個(gè)邊緣CDN節(jié)點(diǎn)或者通過版本號機(jī)制在服務(wù)端模板中修改版本號上線。這兩種方式都存在效率問題,假設(shè)處理一個(gè)緊急故障用這種方式處理完了可能故障也恢復(fù)了。 因此我們的觀點(diǎn)是前端JS只拿數(shù)據(jù)展示,所有或大部分邏輯交給后端去完成,即靜態(tài)資源CSS/JS CDN,動態(tài)資源JSONP。前端JS瘦身,業(yè)務(wù)邏輯后置。這種方案可能在我們的場景下更適用。 在雙十一期間某些服務(wù)出問題了,不能更新商品信息。此時(shí)秒殺商品如果不打標(biāo)就不能購買。因此我們在服務(wù)端完成了這件事情,整個(gè)處理過程只需要幾十秒就能搞定,避免了商品不能被秒殺的問題。而如果在JS中完成需要耗費(fèi)非常長的時(shí)間,因?yàn)镴S在客戶端還有緩存時(shí)間,而且一般緩存時(shí)間非常長。這也是詳情頁動態(tài)化后能靈活的應(yīng)對各種問題。 前端接口服務(wù)端聚合 商品詳情頁上依賴的服務(wù)眾多,一個(gè)類似的服務(wù)需要請求多個(gè)不相關(guān)的服務(wù)接口,造成前端代碼臃腫,判斷邏輯眾多。而我無法忍受這種現(xiàn)狀,我想要的結(jié)果就是前端異步請求我的一個(gè)API,我把相關(guān)數(shù)據(jù)準(zhǔn)備好發(fā)過去,前端直接拿到數(shù)據(jù)展示即可。 所有或大部分邏輯在服務(wù)端完成而不是在客戶端完成。因此在接入層使用Lua協(xié)程機(jī)制并發(fā)調(diào)用多個(gè)相關(guān)服務(wù)然后最后把這些服務(wù)進(jìn)行了合并。比如推薦服務(wù):最佳組合、推薦配件、優(yōu)惠套裝。 通過 http://c./recommend?methods=accessories,suit,combination&sku=1159330&cat=6728,6740,12408&lid=1&lim=6 進(jìn)行請求獲取聚合的數(shù)據(jù)。這樣原來前端需要調(diào)用三次的接口只需要一次就能吐出所有數(shù)據(jù)。我們對這種請求進(jìn)行了API封裝,如下圖所示 : 比如庫存服務(wù),判斷商品是否有貨需要判斷:1、主商品庫存狀態(tài)、2、主商品對應(yīng)的套裝子商品庫存狀態(tài)、主商品附件庫存狀態(tài)及套裝子商品附件庫存狀態(tài)。 套裝商品是一個(gè)虛擬商品,是多個(gè)商品綁定在一起進(jìn)行售賣的形式。如果這段邏輯放在前段完成,需要多次調(diào)用庫存服務(wù),然后進(jìn)行組合判斷,這樣前端代碼會非常復(fù)雜,凡是涉及到調(diào)用庫存的服務(wù)都要進(jìn)行這種判斷。因此我們把這些邏輯封裝到服務(wù)端完成。另外對這些關(guān)系數(shù)據(jù)進(jìn)行了異構(gòu)存儲,減少與其他系統(tǒng)進(jìn)行交互帶來的未知性能開銷。這樣只需要讀取自己的Redis就能拿到關(guān)系。 前端請求 http://c0./stock?skuId=1856581&venderId=0&cat=9987,653,655&area=1_72_2840_0&buyNum=1&extraParam={%22originid%22:%221%22}&ch=1&callback=getStockCallback 然后服務(wù)端計(jì)算整個(gè)庫存狀態(tài),而前端不需要做任何調(diào)整。 在服務(wù)端使用Lua協(xié)程并發(fā)的進(jìn)行庫存調(diào)用,如下圖所示 : 另外接口也會吐出相關(guān)的附件/套裝的庫存狀態(tài),方便前臺萬一需要時(shí)使用。 另外比如今日抄底服務(wù),調(diào)用接口太多,如庫存、價(jià)格、促銷等都需要調(diào)用,因此也使用這種機(jī)制把這幾個(gè)服務(wù)在接入層合并為一個(gè)大服務(wù),對外暴露。 http://c./today?skuId=1264537&area=1_72_2840_0&promotionId=182369342&cat=737,752,760&callback=jQuery9364459&_=1444305642364 整個(gè)詳情頁的未來就是首屏盡量一個(gè)大服務(wù)提供所有數(shù)據(jù),各子接口設(shè)置超時(shí)時(shí)間,在超時(shí)后,單獨(dú)發(fā)送一次請求查詢相關(guān)數(shù)據(jù)。 目前合并的主要有:促銷和廣告詞合并、配送至相關(guān)服務(wù)合并。 服務(wù)隔離 服務(wù)隔離的目的是防止因?yàn)槟承┓?wù)抖動而造成整個(gè)應(yīng)用內(nèi)的所有服務(wù)不可用。 可總結(jié)為:
應(yīng)用內(nèi)線程池隔離,采用了Servlet3異步化,并為不同的請求按照重要級別分配線程池,這些線程池是相互隔離的。也提供了監(jiān)控接口以便發(fā)現(xiàn)問題及時(shí)進(jìn)行動態(tài)調(diào)整,該實(shí)踐可以參考我博客的《商品詳情頁系統(tǒng)的Servlet3異步化實(shí)踐》。目前Java系統(tǒng)也全面升級為JDK8+Servlet3。 部署/分組隔離,意思是為不同的消費(fèi)方提供不同的分組,不同的分組之間不相互影響,以免因?yàn)榇蠹沂褂猛粋€(gè)分組導(dǎo)致有些人亂用導(dǎo)致整個(gè)分組服務(wù)不可用。 拆應(yīng)用隔離,如果一個(gè)服務(wù)調(diào)用量巨大,那便可以把這個(gè)服務(wù)單獨(dú)拆出去,做成一個(gè)應(yīng)用,減少因其他服務(wù)上線或者重啟導(dǎo)致影響本應(yīng)用。 其他 對于http 304一些服務(wù)都設(shè)置了,如延保,設(shè)置了一個(gè)容忍時(shí)間,如果在這段時(shí)間內(nèi)重復(fù)請求,會直接返回304,而不查緩存或回源處理。 超時(shí)時(shí)間/重試,對于一個(gè)系統(tǒng)來說,超時(shí)時(shí)間是必須設(shè)置的,主要有如TCP連接/讀/寫超時(shí)時(shí)間、連接池超時(shí)時(shí)間,還有相關(guān)的重試時(shí)機(jī)/次數(shù)。對于一個(gè)網(wǎng)絡(luò)服務(wù),需要根據(jù)實(shí)際情況設(shè)置TCP連接/讀/寫超時(shí)時(shí)間,如果不設(shè)置很可能會在網(wǎng)絡(luò)出問題時(shí)服務(wù)直接掛斷,比如連接Redis都設(shè)置在150ms以內(nèi)。 還有如果使用Nginx,還需要考慮Nginx超時(shí)時(shí)間、upstream超時(shí)時(shí)間和業(yè)務(wù)超時(shí)時(shí)間。假設(shè)Nginx超時(shí)時(shí)間是5s、upstream超時(shí)時(shí)間是6s,而業(yè)務(wù)的超時(shí)時(shí)間是8s,那么假設(shè)業(yè)務(wù)處理了7s,那么upstream超時(shí)時(shí)間到了需要進(jìn)行下一個(gè)upstream的重試,但是Nginx總的超時(shí)時(shí)間是5s,所以就無法重試了 連接池也需要設(shè)置獲取連接的等待時(shí)間,如果不設(shè)置會有很多線程一直在等待,之前自己實(shí)現(xiàn)連接池時(shí),會去發(fā)現(xiàn)是否網(wǎng)絡(luò)錯誤,如果網(wǎng)絡(luò)錯誤就沒必要等待連接了,而立即失敗即可。 還需要考慮服務(wù)失敗重試時(shí)機(jī),比如是立即重試,還是指數(shù)式重試;還有重試次數(shù),是一次性重試三次還是指數(shù)式等待時(shí)間后進(jìn)行一次次的重試。比如HttpClient(DefaultHttpRequestRetryHandler)默認(rèn)會立即進(jìn)行三次重試。 CDN緩存評價(jià)使用了版本化。對于版本化的意思是對于一些內(nèi)容可以通過版本化機(jī)制設(shè)置更長的超時(shí)時(shí)間,而且固定URL順序,這樣相同的URL請求只要版本不變就會更有效的命中CDN數(shù)據(jù)。 版本可以通過消息推送機(jī)制通知相關(guān)系統(tǒng)。比如評價(jià)數(shù)據(jù)的版本化,可以每隔幾分鐘推送變更了版本的商品更新版本號,還要注意消息排重。另外為了發(fā)現(xiàn)是哪臺服務(wù)器出現(xiàn)了問題,都要在響應(yīng)響應(yīng)頭記錄服務(wù)器真實(shí)IP。這樣出問題后,直接定位到哪臺服務(wù)器出問題了。還有如對于整個(gè)系統(tǒng)無法保證整個(gè)系統(tǒng)不出現(xiàn)臟數(shù)據(jù),所以需要提供刷臟數(shù)據(jù)的接口,以便出現(xiàn)問題時(shí)進(jìn)行數(shù)據(jù)的更新或刪除。 Q&A Q1 : 邏輯由前段向后端遷移后,后端壓力對比上升了多少?還是降低了? 我個(gè)人從來不認(rèn)為后端會有壓力,我們的某服務(wù)響應(yīng)時(shí)間在10-100ms之間,8CPU容器壓測在10000~20000qps。另外后端是比較容易水平擴(kuò)展的。 Q2 : 獲取多維度數(shù)據(jù)時(shí),部分維度需要回源,這時(shí)候邏輯上會阻塞。是如何做到大并發(fā)下的高吞吐?基于事件驅(qū)動網(wǎng)絡(luò)IO?協(xié)程? Nginx的非阻塞IO+Lua協(xié)程;另外我們使用Nginx共享字典,按照不同的維度開辟共享字典,對于大流量的可以做sharding,目前沒有做sharding,只是按照維度分離共享字典,防止LRU和鎖競爭。 Q3 : 多級緩存如何保證一致性? 1、緩存數(shù)據(jù)時(shí)間都非常短;2、有些可以持久化的數(shù)據(jù)是通過消息通知變更推送;3、不能保證100%沒問題,也提供了清理接口進(jìn)行清理。 Q4 : 接入層的本地緩存用啥存的也是redis么? ngx_lua提供的共享字典,目前使用中沒有遇到任何問題,reload不丟,減少了部署Redis的繁瑣。也考慮過Java堆外緩存使用Local Redis或Local Nginx。 Q5 : '另外對這些關(guān)系數(shù)據(jù)進(jìn)行了異構(gòu)存儲,減少與其他系統(tǒng)進(jìn)行交互帶來的未知性能開銷 這樣只需要讀取自己的Redis就能拿到關(guān)系'這個(gè)異構(gòu)存儲是怎么維護(hù)和更新的? 數(shù)據(jù)都是KV存儲,復(fù)雜的數(shù)據(jù)通過Redis Lua腳本減少交互。異構(gòu)數(shù)據(jù)都是通過消息對接的。 Q6 : 請問商品數(shù)量這種變化相對較快的數(shù)據(jù)如何緩存的,如何防止超賣,特別對于熱門商品? 超賣通過庫存系統(tǒng)的服務(wù)進(jìn)行控制,詳情頁是前端展示庫存,主要是展示商品、價(jià)格、庫存都是通過消息通知變更的。 Q7 :秒殺預(yù)熱,開始,結(jié)束后:幾個(gè)階段的庫存,價(jià)格相關(guān)信息如果管理?如何防止超賣和缺貨? 秒殺是單獨(dú)的服務(wù)實(shí)現(xiàn),跟詳情頁是分開實(shí)現(xiàn);另外據(jù)我知道的 1、秒殺是和主交易流程隔離的,防止相互影響;2、庫存扣減通過Redis。 Q8 :聚合服務(wù)的超時(shí)時(shí)間如何設(shè)置?根據(jù)所有依賴服務(wù)的超時(shí)進(jìn)行設(shè)置? 幾個(gè)點(diǎn):1、Nginx proxy timeout;2、客戶端timeout,如HttpClient;3、每個(gè)服務(wù)都是單獨(dú)設(shè)置超時(shí)時(shí)間。 Q9 :服務(wù)的超時(shí)時(shí)間統(tǒng)一管理么,某個(gè)業(yè)務(wù)超時(shí)時(shí)間變化可能影響很大,怎么控制? 根據(jù)業(yè)務(wù)去控制,不同的業(yè)務(wù)要求不一樣;比如庫存/配送至依賴的商家配送時(shí)效,正常100ms以內(nèi),如果超過這個(gè)時(shí)間,排除網(wǎng)絡(luò)問題,相關(guān)系統(tǒng)肯定出問題了,可以進(jìn)行優(yōu)雅的降級。 Q10 :用戶鑒權(quán)也是用ngx_lua共享字典做的么?用戶信息這種敏感信息如果在接入暴露應(yīng)該會產(chǎn)生安全隱患,但不在接入做放到后面核心域又會拖慢返回速度? 我們封裝了一個(gè)API,專門在接入層對一些服務(wù)進(jìn)行鑒權(quán),不對用戶信息的輸出和暴露,這樣能更好的控制而且可以盡早的發(fā)現(xiàn)非法請求;拖慢速度的情況,因?yàn)樵诮尤雽域?yàn)證,這個(gè)就不是問題了。 Q11 :秒殺的商品庫存 跟正常商品庫存是分開的嗎? 對,包括整個(gè)交易流程也是分離的,可以走下流程就看到了,而且必須按照正常用戶步驟去訪問才可以。 參考文章:億級商品詳情頁架構(gòu)演進(jìn)技術(shù)解密 | 高可用架構(gòu)系列 |
|