一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

【年度案例】雙十一大型電商統(tǒng)一服務(wù)架構(gòu)實(shí)戰(zhàn)

 半夏陌涼 2015-11-29

編者按:高可用架構(gòu)推出2015年度案例系列文章,分享在架構(gòu)領(lǐng)域具有典型意義的年度案例,本文根據(jù)張開濤分享的大型電商網(wǎng)站應(yīng)對雙十一的架構(gòu)案例記錄。轉(zhuǎn)載請注明高可用架構(gòu)公眾號ArchNotes。


張開濤

2014年加入京東,主要負(fù)責(zé)商品詳情頁、詳情頁統(tǒng)一服務(wù)架構(gòu)與開發(fā)工作,設(shè)計(jì)并開發(fā)了多個(gè)億級訪問量系統(tǒng)。工作之余喜歡寫技術(shù)博客,有《跟我學(xué)Spring》、《跟我學(xué)Spring MVC》、《跟我學(xué)Shiro》、《跟我學(xué)Nginx+Lua開發(fā)》等系列教程,博客訪問量500萬。開濤之前在高可用架構(gòu)群分享《億級商品詳情頁架構(gòu)演進(jìn)技術(shù)解密》,受到高度關(guān)注,文章閱讀數(shù)超過1.8萬,關(guān)注本群公眾號,回復(fù)29可查看。


京東商品詳情頁技術(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)化管理。


這樣的好處:

  • 統(tǒng)一管理和監(jiān)控,出問題可以統(tǒng)一降級

  • 可以把一些相關(guān)接口合并輸出,減少頁面的異步加載請求

  • 一些前端邏輯后移到服務(wù)端,前端只做展示,不進(jìn)行邏輯處理。


有了它,所有入口都在我們服務(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)




整體流程

  1. 請求首先進(jìn)入Nginx,Nginx調(diào)用Lua進(jìn)行一些前置邏輯處理,如果前置邏輯不合法直接返回;然后查詢本地緩存,如果命中直接返回?cái)?shù)據(jù)。

  2. 如果本地緩存不命中數(shù)據(jù),則查詢分布式Redis集群,如果命中數(shù)據(jù),則直接返回。

  3. 如果分布式Redis集群不命中,則會調(diào)用Tomcat進(jìn)行回源處理;然后把結(jié)果異步寫入Redis集群,并返回。


如上是整個(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)模式

  • 本地緩存

  • 多級緩存

  • 統(tǒng)一入口/服務(wù)閉環(huán)

  • 引入接入層

  • 前端業(yè)務(wù)邏輯后置

  • 前端接口服務(wù)端聚合

  • 服務(wù)隔離


兩種讀服務(wù)架構(gòu)模式

  • 讀取分布式Redis數(shù)據(jù)架構(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ù)。


  • 讀取本地Redis數(shù)據(jù)架構(gòu)


可以看到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ì)了四級緩存,如下圖所示:



  • 首先在接入層,會使用Nginx本地緩存,這種前端緩存主要目的是抗熱點(diǎn);根據(jù)場景來設(shè)置緩存時(shí)間。

  • 如果Nginx本地緩存不命中,接著會讀取各個(gè)機(jī)房的分布式從Redis緩存集群,該緩存主要是保存大量離散數(shù)據(jù),抗大規(guī)模離散請求,比如使用一致性哈希來構(gòu)建Redis集群,即使其中的某臺機(jī)器出問題,也不會出現(xiàn)雪崩的情況。

  • 如果從Redis集群不命中,Nginx會回源到Tomcat;Tomcat首先讀取本地堆緩存,這個(gè)主要用來支持在一個(gè)請求中多次讀取一個(gè)數(shù)據(jù)或者該數(shù)據(jù)相關(guān)的數(shù)據(jù)。而其他情況命中率是非常低的,或者緩存一些規(guī)模比較小但用的非常頻繁的數(shù)據(jù),如分類,品牌數(shù)據(jù);堆緩存時(shí)間設(shè)置為Redis緩存時(shí)間的一半。

  • 如果Java堆緩存不命中,會讀取主Redis集群,正常情況該緩存命中率非常低,不到5%。讀取該緩存的目的是防止前端緩存失效之后的大量請求的涌入,導(dǎo)致后端服務(wù)壓力太大而雪崩。默認(rèn)開啟了該緩存,雖然增加了幾毫秒的響應(yīng)時(shí)間,但是加厚了防護(hù)盾,使服務(wù)更穩(wěn)當(dāng)可靠。此處可以做下改善,比如設(shè)置一個(gè)閥值,超過這個(gè)閥值才讀取主Redis集群,比如Guava就有RateLimiter API來實(shí)現(xiàn)。


統(tǒng)一入口/服務(wù)閉環(huán)

在《億級商品詳情頁架構(gòu)演進(jìn)技術(shù)解密》中已經(jīng)講過了數(shù)據(jù)異構(gòu)閉環(huán)的收益,在統(tǒng)一服務(wù)中也遵循這個(gè)設(shè)計(jì)原則,此處主要做了兩件事情


  1. 數(shù)據(jù)異構(gòu),如判斷庫存狀態(tài)依賴的套裝、配件關(guān)系進(jìn)行了異構(gòu),未來可以對商家運(yùn)費(fèi)等數(shù)據(jù)進(jìn)行異構(gòu),減少接口依賴。

  2. 服務(wù)閉環(huán),所有單品頁上用到的核心接口都接入統(tǒng)一服務(wù)。


有些是查庫/緩存然后做一些業(yè)務(wù)邏輯,有些是http接口調(diào)用然后進(jìn)行簡單的數(shù)據(jù)邏輯處理;還有一些就是做了下簡單的代理,并監(jiān)控接口服務(wù)質(zhì)量。


引入Nginx接入層

在設(shè)計(jì)系統(tǒng)時(shí)需要把一些邏輯盡可能前置以此來減輕后端核心邏輯的壓力。另外如服務(wù)升級/服務(wù)降級能非常方便的進(jìn)行切換,在接入層做了如下事情:


  • 數(shù)據(jù)校驗(yàn)/過濾邏輯前置、緩存前置、業(yè)務(wù)邏輯前置

  • 降級開關(guān)前置

  • AB測試

  • 灰度發(fā)布/流量切換

  • 監(jiān)控服務(wù)質(zhì)量

  • 限流


服務(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)分為這么幾種:

  • 接入層開關(guān)和后端應(yīng)用開關(guān)。在接入層設(shè)置開關(guān)的目的是防止降級后流量還無謂的打到后端應(yīng)用。

  • 總開關(guān)是對整個(gè)服務(wù)降級,比如庫存服務(wù)默認(rèn)有貨。

  • 原子開關(guān)是整個(gè)服務(wù)中的其中一個(gè)小服務(wù)降級,比如庫存服務(wù)中需要調(diào)用商家運(yùn)費(fèi)服務(wù),如果只是商家運(yùn)費(fèi)服務(wù)出問題了,此時(shí)可以只降級商家運(yùn)費(fèi)服務(wù)。


另外還可以根據(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)中存在的主要限流邏輯是:

  • 對于大多數(shù)請求按照IP請求數(shù)限流,對于登陸用戶按照用戶限流

  • 對于讀取緩存的請求不進(jìn)行限流,只對打到后端系統(tǒng)的請求進(jìn)行限流

  • 還可以限制用戶訪問頻率,比如使用ngx_lua中的ngx.sleep對請求進(jìn)行休眠處理,讓刷接口的速度降下來或者種植cookie token之類的,必須按照流程訪問。當(dāng)然還可以對爬蟲/刷數(shù)據(jù)的請求返回假數(shù)據(jù)來減少影響。


前端業(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)線程池隔離

  • 部署/分組隔離

  • 拆應(yīng)用隔離


應(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)系列






    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    都市激情小说在线一区二区三区| 国产熟女一区二区不卡| 亚洲欧美黑人一区二区| 欧美日韩亚洲精品在线观看| 色婷婷日本视频在线观看| 国内尹人香蕉综合在线| 国产一区二区三区丝袜不卡| 免费特黄一级一区二区三区| 国产肥女老熟女激情视频一区| 爽到高潮嗷嗷叫之在现观看| 色哟哟国产精品免费视频| 欧美激情视频一区二区三区| 亚洲精品中文字幕在线视频| 91精品国自产拍老熟女露脸 | 久久青青草原中文字幕| 熟女体下毛荫荫黑森林自拍| 最近最新中文字幕免费| 国产精品亚洲一级av第二区| 亚洲性日韩精品一区二区| 国产亚洲中文日韩欧美综合网| 亚洲精品中文字幕熟女| 成年男女午夜久久久精品| 国产中文字幕久久黄色片| 国产日韩欧美在线播放| 国产在线小视频你懂的| 丰满少妇被猛烈撞击在线视频| 国产高清在线不卡一区| 久久精品蜜桃一区二区av| 91欧美日韩中在线视频| 婷婷开心五月亚洲综合| 亚洲丁香婷婷久久一区| 日本欧美三级中文字幕| 亚洲免费黄色高清在线观看| 五月天六月激情联盟网| 午夜精品一区二区av| 欧美日韩精品视频在线| 成人午夜在线视频观看| 国产福利在线播放麻豆| 日韩一区二区免费在线观看| 国产精品伦一区二区三区在线| 黄色av尤物白丝在线播放网址 |