狂攬1592億!京東京麥平臺(tái)618備戰(zhàn)實(shí)踐總結(jié)
近日,京東可謂是好事連連,“谷歌 5.5 億美元投資京東”、“京東 1.2 億美元增持唯品會(huì)股權(quán)”、“一年一度的京東 618 狂歡也終于落下帷幕,累計(jì)下單金額達(dá) 1592 億元”。
京東 1592 億元的驚人戰(zhàn)績(jī)離不開(kāi)強(qiáng)大的技術(shù)支撐,下面來(lái)自京東京麥平臺(tái)的資深架構(gòu)師王新棟給大家揭秘京東年度大考背后的備戰(zhàn)實(shí)踐。
作為技術(shù)研發(fā)人員,我們常戲稱京東每年只干兩件事,一個(gè)是 618,另外一個(gè)是雙 11!
確實(shí)每一次的大促都是一場(chǎng)全兵演練,技術(shù)人員在這場(chǎng)戰(zhàn)斗中,從團(tuán)隊(duì)合作、技術(shù)提升、用戶意識(shí)上都有一個(gè)立體的提高。實(shí)難想象還有比這樣規(guī)模的促銷更好的鍛煉方式。
京麥平臺(tái)是目前京東所有商家唯一使用的一站式店鋪運(yùn)營(yíng)管理平臺(tái),商家借助京麥可以實(shí)現(xiàn)商品發(fā)布、打印訂單、出庫(kù)、訂單消息接收等等一系列的日常工作。
京麥平臺(tái)更是集合了眾多 ISV 廠商為商家提供了更加豐富的功能,更好的賦能于商家。
京麥平臺(tái)的技術(shù)架構(gòu)也在隨著京東業(yè)務(wù)的飛速發(fā)展不斷演化著。從早期的簡(jiǎn)單 Nginx+Tomcat 部署,到現(xiàn)在功能服務(wù)模塊化,獨(dú)立部署,享受著微服務(wù)帶來(lái)的便利,但同時(shí)也給我們大促的備戰(zhàn)帶來(lái)了眾多挑戰(zhàn)。
首先確定自己的備戰(zhàn)思路,梳理核心流程、找出薄弱點(diǎn),對(duì)薄弱點(diǎn)具體優(yōu)化。
同時(shí)協(xié)調(diào)壓測(cè)對(duì)優(yōu)化結(jié)果驗(yàn)證,優(yōu)化和壓測(cè)會(huì)有一個(gè)反復(fù)的過(guò)程,后面我們還要實(shí)際演練,比如降級(jí)開(kāi)關(guān),防止實(shí)際情況發(fā)生的時(shí)候準(zhǔn)備好的功能不可用。
***我們要做一輪培訓(xùn),在工具使用,快速定位問(wèn)題,歷來(lái)的教訓(xùn)總結(jié)都過(guò)一遍。
從心理層面上我們也要輔導(dǎo),大促期間發(fā)生的問(wèn)題都不是小問(wèn)題,研發(fā)人員在這種壓力下處理問(wèn)題有時(shí)候會(huì)變形,我們會(huì)在這方面做一次心理疏解。
這中間我們可以利用幾個(gè)規(guī)則,二八法則,找出 20% 重要核心的功能,比如京麥的登錄、交易、價(jià)格門(mén)消息推送。
在找薄弱點(diǎn)的時(shí)候我們可以試試墨菲定律的幾點(diǎn)規(guī)則,“可能出錯(cuò)的事總會(huì)出錯(cuò)”,“如果你擔(dān)心某種情況發(fā)生,那么它就有可能發(fā)生”。
我們還會(huì)找大家一起列出心中最不可能出現(xiàn)問(wèn)題的系統(tǒng)和功能點(diǎn)來(lái)重點(diǎn)對(duì)待,沒(méi)錯(cuò),是重點(diǎn)對(duì)待。
我們常用的備戰(zhàn)技術(shù),肯定不是重啟,回滾,加機(jī)器,我個(gè)人總結(jié)有:
- 分離技術(shù)
- 緩存技術(shù)
- SQL 優(yōu)化
- 快速失敗
- 降級(jí)限流
下面我們來(lái)逐一介紹。
分離技術(shù)
話說(shuō)“天下大事,合久必分,分久必合”,但在軟件架構(gòu)領(lǐng)域,一直是一個(gè)分的趨勢(shì)。
比如京麥平臺(tái)有提供 ISV 的服務(wù)、提供京麥自有客戶端的服務(wù)、提供其他平臺(tái)的服務(wù),同時(shí)還有監(jiān)控服務(wù),日志服務(wù),消息服務(wù)等。
這需要將業(yè)務(wù)服務(wù)隔離部署,線下分析和線上運(yùn)行隔離,同時(shí)還可以采取線程隔離,比如 JSF 里面為每個(gè)不同重要的服務(wù)分別一個(gè)線程組的方式。
數(shù)據(jù)庫(kù)層面上主從分離,京麥平臺(tái)的業(yè)務(wù)都是讀多寫(xiě)少,可以充分利用分庫(kù)的資源。
還可以再將數(shù)據(jù)庫(kù)細(xì)分,按照主要業(yè)務(wù)拆分不同的數(shù)據(jù)庫(kù),結(jié)合 RPC 使用,更大降低數(shù)據(jù)庫(kù)層面的耦合程度。
緩存技術(shù)
如果軟件里面真的有一種銀彈的話,我認(rèn)為就是緩存,當(dāng)你性能優(yōu)化遇到瓶頸的時(shí)候,當(dāng)你想抗量的時(shí)候,你都會(huì)想到緩存。
這里有一個(gè)鐵律,那就是,對(duì)外暴露的接口一定不能直達(dá)數(shù)據(jù)庫(kù)。我們常用的緩存是 Redis + JVMcache。
計(jì)算 Redis 穿透率的時(shí)候我們可以,通過(guò) UMP 來(lái)實(shí)現(xiàn),在一個(gè)方法的總?cè)肟诼顸c(diǎn),比如統(tǒng)計(jì)出 1 分鐘調(diào)用 M 次,在請(qǐng)求 Redis 的入口埋點(diǎn),統(tǒng)計(jì)出 1 分鐘調(diào)用 N次,計(jì)算***率:N/M。
在使用 Redis 的時(shí)候要注意含有大數(shù)值的 key,常常量一上來(lái)會(huì)造成 Redis 集群的熱點(diǎn)訪問(wèn),直接將單一節(jié)點(diǎn)打死。
這樣的情況下我們就要拆分這樣的大 key。同時(shí)將緩存 DB 化,就是不設(shè)置超時(shí)時(shí)間,這樣全部用 Redis 來(lái)抗量。
本地緩存這塊我們常用的有 Guava-cache,通過(guò)本地緩存我們可以做三級(jí)防護(hù),或者做托底數(shù)據(jù)等。
如果數(shù)據(jù)“尺寸較小”、“高頻的讀取操作”、“變更操作較少”使用這種嵌入式緩存將非常合適。
SQL優(yōu)化
每次大促前我們都要將系統(tǒng)中性能慢的 SQL 抓出來(lái),而且這種工作投入產(chǎn)出比極高,也就是可以花費(fèi)較小代價(jià)帶來(lái)極大的性能收益。
SQL 性能問(wèn)題,大多數(shù)情況下是沒(méi)有索引引起的,這可能是后續(xù)業(yè)務(wù)變化迅速,上線前代碼 review 的遺漏,需要這個(gè)時(shí)候統(tǒng)一過(guò)一遍。
還有就是索引使用錯(cuò)誤,比如索引字段是字符串類型,但是程序中請(qǐng)求 DB 的時(shí)候傳的是 long 類型,索引失效,表中數(shù)量過(guò)多,做了一次全表掃描,性能會(huì)很差。
還有時(shí)候我們添加索引的時(shí)候要看區(qū)分度,計(jì)算索引區(qū)分度的方法是不重復(fù)的索引值/總記錄數(shù),值越接近 1,說(shuō)明區(qū)分度越高,查詢的時(shí)候 MySQL 就會(huì)過(guò)濾掉更多的行數(shù)據(jù)。
還有,添加索引***結(jié)合 MySQL 執(zhí)行計(jì)劃來(lái)判斷。有時(shí)候做了過(guò)多的 join 操作,比如超過(guò) 3 張表以上,我們就要想著去拆解這些 SQL 語(yǔ)句。
再就是數(shù)據(jù)庫(kù)層面我可以把歷史數(shù)據(jù)轉(zhuǎn)出,減少數(shù)據(jù)量來(lái)達(dá)到提高查詢速度的目的。
快速失敗
快速失敗策略實(shí)際上是一種自我保護(hù)措施,比如調(diào)用第三方接口超時(shí),如果超時(shí)時(shí)間設(shè)置過(guò)長(zhǎng),訪問(wèn)量大的時(shí)候,就會(huì)導(dǎo)致請(qǐng)求線程積壓;如果此時(shí)有線程隔離還好,若剛好沒(méi)有,那么訪問(wèn)量一上來(lái)就會(huì)迅速導(dǎo)致 CPU 飆高。
京麥平臺(tái)的特點(diǎn)之一,會(huì)大量調(diào)用第三方接口服務(wù),我們會(huì)對(duì)每個(gè)方法動(dòng)態(tài)的設(shè)置超時(shí)時(shí)間。
如果 UMP 報(bào)警再結(jié)合 JVM 性能數(shù)據(jù),我們會(huì)將這個(gè)接口的超時(shí)時(shí)間閾值調(diào)小,通過(guò) Zookeeper 下發(fā)到每一個(gè)服務(wù)節(jié)點(diǎn)上。
在大促前,我們會(huì)重點(diǎn)檢查 MySQL、Redis、JSF 等 RPC 調(diào)用的超時(shí)設(shè)置,確保每一次 RPC 調(diào)用都要有上限閾值。
關(guān)于 RPC 調(diào)用超時(shí),這里多說(shuō)一下,有時(shí)候我們會(huì)發(fā)現(xiàn)調(diào)用端性能比如超過(guò) 500ms,但是服務(wù)端卻是在 100ms 上線徘徊。
這里面我們除了檢查網(wǎng)關(guān)延時(shí),TCP 重傳,還要注意一點(diǎn),就是任何一個(gè)成熟的 RPC 框架都不會(huì)讓業(yè)務(wù)線程直接參與網(wǎng)絡(luò)請(qǐng)求。
RPC 會(huì)提供一個(gè)消息隊(duì)列,調(diào)用端直接跟消息隊(duì)列打交道。此時(shí),我們就要想到隊(duì)列這塊是否有問(wèn)題了。
降級(jí)限流
這種技術(shù)實(shí)際上是保命的措施。降級(jí)一般有屛蔽降級(jí)和容錯(cuò)降級(jí)兩種,對(duì)一些非核心的功能,比如京麥的麥圈,服務(wù)號(hào),論壇等功能,而它們恰恰又請(qǐng)求著 MySQL,Redis 等公共資源。
為了減少這種競(jìng)爭(zhēng)我們就會(huì)對(duì)這些功能進(jìn)行屛蔽降級(jí),直接切斷 RPC 調(diào)用,不再發(fā)起遠(yuǎn)程調(diào)用,返回空或者其他異常提示,減少公共資源的訪問(wèn)。
降級(jí)開(kāi)關(guān),目前京麥?zhǔn)遣捎媒y(tǒng)一配置中心來(lái)使用。同樣,限流技術(shù)在京麥平臺(tái)中也是異常重要的一個(gè)措施,尤其是對(duì)京麥網(wǎng)關(guān)的調(diào)用。
我們目前是采用令牌桶的方法,實(shí)現(xiàn)方式是 Guava RateLimiter,簡(jiǎn)單有效,再結(jié)合統(tǒng)一配置中心,我們可以動(dòng)態(tài)調(diào)整限流閾值。不用重啟服務(wù)器即可實(shí)現(xiàn)快速限流策略調(diào)整。
我們?cè)诰W(wǎng)關(guān)里面還有一個(gè)設(shè)置,就是并發(fā)度,這個(gè)是方法粒度的,對(duì)每一個(gè)調(diào)用第三方的接口都有一個(gè)并發(fā)度數(shù)值設(shè)置。
而且是動(dòng)態(tài)設(shè)置,也是通過(guò) Zookeeper 下發(fā)到每一個(gè)服務(wù)節(jié)點(diǎn)上。并發(fā)度的具體實(shí)現(xiàn)是通過(guò) JDK 的 Semaphore。
我們?cè)賮?lái)說(shuō)一下,監(jiān)控配置和性能壓測(cè)。
監(jiān)控配置是一定不能缺少,我們要求自己一定要***時(shí)間早于用戶發(fā)現(xiàn)問(wèn)題。
平時(shí)開(kāi)發(fā)在上線的時(shí)候我們都應(yīng)該有統(tǒng)一要求每一個(gè) RPC 調(diào)用都要有監(jiān)控和錯(cuò)誤棧的輸出。
在備戰(zhàn)期間實(shí)際是對(duì)監(jiān)控配置的一個(gè)治理過(guò)程,監(jiān)控配置 key 要規(guī)則化,比如***.mysql.***,***.redis.***等讓我們一眼便知道是操作的哪一個(gè)資源。
京麥平臺(tái)這次備戰(zhàn)的過(guò)程中通過(guò)采用 AOP 的方式,把所有類似的調(diào)用統(tǒng)一規(guī)范化處理,后續(xù)結(jié)合 Python 腳本對(duì)監(jiān)控閾值進(jìn)行統(tǒng)一調(diào)整。
這樣走下來(lái)一方面把我們平時(shí)可能漏掉的監(jiān)控給補(bǔ)全,另外一方面我們的監(jiān)控配置按照統(tǒng)一的規(guī)則來(lái)生成。
性能壓測(cè)這一塊,可以很好的檢驗(yàn)我們優(yōu)化的結(jié)果,壓測(cè)的過(guò)程中我們關(guān)注 QPS 的同時(shí),還要結(jié)合服務(wù)器的 CPU、IO、內(nèi)存等機(jī)器性能指標(biāo)來(lái)定位我們的性能瓶頸。
***來(lái)預(yù)估我們系統(tǒng)的承載能力,提供數(shù)據(jù)支撐來(lái)申請(qǐng)服務(wù)器或者 Docker 資源。
壓測(cè)工具研發(fā)可以自己寫(xiě)腳本,借助 jmeter、loadrunner 等工具,也可以有計(jì)劃的聯(lián)系性能壓測(cè)組他們協(xié)助執(zhí)行。最困難的還是產(chǎn)生真實(shí)的壓力。
還有一個(gè)備戰(zhàn)點(diǎn),返璞歸真,回到代碼,重點(diǎn)核心功能檢查一遍,把壞味道的代碼查出來(lái)。
比如 join 了很多張表的 SQL 語(yǔ)句、日志輸出沒(méi)有采用條件方式或者占位符的方式、日志重復(fù)打印、try 代碼塊放到了事務(wù)中、循環(huán)體中含有低性能的語(yǔ)句、鎖代碼塊中進(jìn)行 RPC 調(diào)用,等等。
***,我們可以想一下備戰(zhàn)備的是什么,總結(jié)歷次的大促,我認(rèn)為主要在工具、知識(shí)、經(jīng)驗(yàn)三個(gè)方面的備戰(zhàn):
- 工欲善其事,必先利其器,我們要有一些好的工具輔助我們解決問(wèn)題,比如 MDC(里面含有 CPU,網(wǎng)絡(luò)等監(jiān)控)、UMP(京東自研方法性能,可用率等監(jiān)控平臺(tái))、CAP(容器的總體監(jiān)控,也含有 CPU 負(fù)載等信息查看)。
- 知識(shí)層面,是我們歷來(lái)的積累,我們認(rèn)識(shí)的提高,使用工具時(shí)候的指導(dǎo)。
- 經(jīng)驗(yàn)是我們以往的大大小小的教訓(xùn)的總結(jié),前車之鑒,防止我們?cè)俅伟l(fā)生類似的事情。
以“結(jié)硬寨,打呆賬”這句話結(jié)束對(duì) 618 備戰(zhàn)的總結(jié),最重要的一點(diǎn),還是要求我們備戰(zhàn)在平時(shí)。
作者:王新棟
簡(jiǎn)介:目前就職于京東,一直從事京麥平臺(tái)的架構(gòu)設(shè)計(jì)與開(kāi)發(fā)工作,熟悉各種開(kāi)源軟件架構(gòu)。在 Web 開(kāi)發(fā),架構(gòu)優(yōu)化上有較豐富實(shí)戰(zhàn)經(jīng)歷。有多年在 NIO 領(lǐng)域的設(shè)計(jì)、開(kāi)發(fā)經(jīng)驗(yàn),對(duì) HTTP、TCP 長(zhǎng)連接技術(shù)有深入研究與領(lǐng)悟,目前主要致力于移動(dòng)與 PC 平臺(tái)網(wǎng)關(guān)技術(shù)的優(yōu)化與實(shí)現(xiàn)。