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