京東手機單品頁在每次大促時承載所有流量的入口,它被天然賦予的一個標籤就是抗壓,對系統的穩定性、效能方面要求極其苛刻,另外單品頁本身業務複雜度較高,單品頁有幾十種垂直流程業務,並且展示上都要求個性化的單品頁,加上依賴有50+的基礎服務,稍有抖動,對整體服務品質都會有比較大的影響,因此之前大促也出現過各種問題,不斷打磨,持續優化升級,當前系統架構可支撐接近百萬的QPS瞬時訪問,並且今年雙11表現非常平穩,藉此機會一塊和大家做一次分享。
一、先聊聊APP介面開發的特點
1. 手機網路、流量受限
手機單品頁提供給APP的API受限於運營商的網路,手機的訊號時有時無、時好時壞極其不穩定,為了減少客戶端和後端建連握手的過程,因此介面下發內容大而全,涵蓋了頁面上的所有內容,沒辦法像瀏覽器BS的結構可以有大量的ajax請求;
API介面依賴了幾十個基礎服務,任何介面的抖動對整體介面效能影響很大,因此必須是併發請求依賴,減少介面抖動疊加的影響;
單品頁有大量的圖片資訊,商品主圖、插圖、推薦商品、手機配件商品、排行榜等等圖片資訊量比較大,單個圖片的大小對手機流量影響較大,所有下發的圖片採用是webp格式,極大減少網路傳輸流量。
2. 手機不同解析度、網路環境、系統版本的適配
不同環境下使用者的體驗存在差異,比如在弱網、低版本、解析度差的手機會保持最基本的購物車流程,會減少一些增值的體驗;
圖片的展示尺寸也會根據網路環境、解析度大小進行適配。
3. APP版本相容
新業務需求變更儘可能相容老版本,但有些業務很難相容老版本,因此係統裡面存在很多版本適配的邏輯,增加了系統的複雜度;
客戶端如果出現重大bug並且沒辦法進行hotfix的情況下,需要服務端針對特定版本進行打補丁,也增加程式碼複雜度以及後期的維護成本。
因此APP的介面開發邏輯複雜度和後續的維護成本被放大很多。
二、單品頁業務系統架構
這是當前單品頁系統的整體架構圖,其他的核心交易流程,比如購物車、下單等也都基本類似,單品頁系統主要有三個程序:OpenResty、Tracer-Collect、Tomcat,以及包含幾個旁路系統。OpenResty是nginx層的web容器,主要職責是做靜態化和限流防刷,只有經過清洗過的流量才會流轉到tomcat的java程序真正的業務處理,Tracer-Collect程序是通過旁路的方式非同步埋點到統一的監控平臺,進行實時的資料分析。
三、核心技術點
1. OpenResty
這個是在今年618之前架構上做的一個變化,主要有以下幾點考慮:
業務需要,業務流量到一定程度,需要把靜態化資料以及限流策略前置,更多把流量擋在前端,減少業務系統壓力;
ngx_openresty模組有效地把Nginx 伺服器轉變為一個強大的 Web 應用伺服器,在其他0級系統中已經很好驗證了帶來的高可用、高併發的能力。
使用規範上
Lua屬於指令碼語言,開發相對java語言比較開放,比如方法可以返回多物件,這對長期java開發人員就有很多不適應,在灰度過程中及時發現並進行修復,因此利用lua來滿足特殊需求外,不會進行過多業務邏輯處理;
任何技術上的變動都要極度謹慎,不斷的進行灰度測試,我們是從一臺機器逐步灰度到一個set,再擴散到一個渠道,最後全量,並且具備實時的異常資料埋點能力,及時發現灰度過程中問題,一旦發現問題要有開關能實時降級。
Lua語言對於很多團隊都使用過程中都遇到各種問題,今年雙11的總結會上也有團隊分享大促期間lua死鎖問題,我們這裡遇到的一個場景是zk的配置資料同步到lua時一定概率出現死鎖。
原因:lua執行在nginx主執行緒中,但zk在nginx主執行緒外啟動新的執行緒watch,當zk更新時通過這個新執行緒通知資料更新,這時我們在這個新的執行緒中直接呼叫lua程式碼,會有概率產生死鎖。
解決方案:在這個新執行緒中不直接呼叫lua程式碼,而是通過http協議直接進入nginx主執行緒更新配置資料。
2. 資料靜態化
單品頁給APP提供的API重點包含兩個,一個是靜態介面,一個是動態介面資料,這裡提到的靜態化重點是針對靜態介面資料,包含商品圖片、基本資訊、店鋪商家資訊、顏色尺碼、延保…..等,去年雙11期間,由於一些熱點商品訪問量過大,對jimdb叢集單個分片的連線數和運算元都非常高,服務壓力過大,整體叢集服務效能變差,因此針對此進行了三級熱點的優化:
CDN
眾所周知,CDN本來就是替業務靜態流量扛熱點資料,但是上邊提到後端有很多的適配工作,包括平臺、網路環境、解析度尺寸,要知道Android的解析度五花八門,所以這種邏輯的話CDN很難發揮作用,因此今年針對這個邏輯做了優化,介面下發給APP的資料都是標準資料格式,同時會下發對應的適配規則給APP,由APP根據規則進行動態適配,極大地提升了快取命中率,另外別忘了還要加上各種開關控制和資料的埋點監控,這也是APP開發的一個重要特徵,APP發出去的版本如果出現各種未知情況將會是災難,在618之前版本經過各種灰度最終還是順利上線,在618期間發揮了重要作用,CDN的命中率達到60%以上,大促的0點開始大部分人還是集中在少數的爆款商品上。這是第一層的保護。
OpenResty(Nginx+Lua)層靜態化
上邊提到這一層重點還是資料靜態化和防刷,您可能有疑問,CDN已經替擋住了大部分流量,為什麼還需要這一層?CDN只是擋住了App的最新版本熱點流量,還有M渠道是通過內網閘道器過來的,不經過CDN,以及App的老版本也是不走CDN,因此這一層主要依賴Nginx的共享快取,分配100M的共享空間,在大促時命中率也可以到接近20%。
JVM本地快取
JVM的堆記憶體主要是針對商品的基本資訊和特殊屬性資訊進行本地快取,支援動態介面商品熱點資料,依賴Guava元件實現的LRU快取淘汰演算法,大致5000個熱點sku資料,資料量在5M左右,這是第三層的資料保護,大促時命中率在27%左右,另外強調一下,這裡的java物件可動態配置成弱引用或者是軟引用,一般建議採用弱引用,這樣避免記憶體增長過快,導致頻繁的GC。
3. 資料異構,減少強依賴
資料異構帶來的好處是可以減少一些基礎服務的強依賴,之前老闆提的一個目標就是基礎服務掛了,上層業務還能很好的活著,但是京東這個資料體量來看成本是非常巨大的,因此APP單品頁選擇部分資料異構,減少基礎服務介面的強依賴,主要是商品的基礎資料、擴充套件屬性資訊、商品的詳情資料,全量資料同步一次之後通過中介軟體JMQ進行增量的資料同步變更,儲存使用的是快取中介軟體jimdb(redis快取)。
4. 併發請求非同步化
APP單品頁前期屬於野蠻發展,很多RPC的依賴極其不合理,比如依賴關係沒有層次概念,超時時間設定超長、內外網介面同時依賴,造成任何的服務品質變差和網路抖動對整體API影響非常大,因此進行了一次SOA化改造,主要工作是把單品頁系統從大閘道器分離出來,然後制定服務接入標準並進行改造,第三方面就是上游基礎服務呼叫並行化,系統整體併發能力及穩定性得到了極大的提升。
服務依賴的標準
依賴介面必須是內網服務,不允許依賴外網服務;
介面超時時間不超過100ms,並且除了一些核心資料,比如商品、價格、庫存,其他都不進行重試;
核心介面必須可支援跨機房的雙活容災,client端出現問題必須可切換,並且要有降級方案;
RPC呼叫最好是依賴中介軟體JSF,這樣是點對點的長連線服務,減少每次建連的開銷,HTTP依賴需要經過內網的LB,增加一層代理的開銷,會出現一些不可控的問題。
隨著流量不斷增加,並行化遇到了瓶頸,每次請求會建立大量的執行緒,執行緒的維護和上下文切換成本本身比較消耗CPU資源,因此基於現有HttpClient和JSF基礎元件的非同步化支援,進一步進行非同步化的改造,單機壓測效果還是比較明顯,併發能力提升40%。
5. 監控
系統流量到一定程度,系統的各維度監控尤為重要,可以幫助我們縮短排查、定位問題的時間,甚至可以幫助預警風險,當前APP業務從使用者到後端整個服務鏈條的監控都已經非常完善,包括各運營商入口流量的監控、內外部網路品質、負載均衡、以及閘道器流量的監控以外,我重點介紹下單品頁業務層的監控,下邊是業務監控系統資料非同步埋點的架構,主要分為兩類資料,第一業務指標資料比如單品頁各渠道訪問資料,通過UDP協議實時埋點到Kafka,然後storm實時線上分析形成最終需要的資料落地,另一類是大流量資料,比如系統異常資訊落到磁碟日誌中,然後通過logCollector非同步傳送到Kafka中,這類資料對磁碟IO、網絡卡IO的流量佔比大,針對磁碟IO,會按照檔案大小100M滾動生成日誌檔案,資料搬走之後進行刪除操作,網絡卡IO在資料傳輸過程中進行了限速,按照1m/s的速度進行傳輸,可進行動態調整,基本對業務不產生任何影響,大促峰值期間會針對一定比例降級。
業務系統除了基本的伺服器各項指標CPU、MEM監控,服務的效能、可用率監控以外,介紹幾個比較實用的業務能力監控:
方法tree監控,一次請求在單品頁SOA系統內部所經過的每一個類的方法作為結點形成這麼一顆樹,這棵樹非常直觀看到系統內部的依賴結構關係和任何一個節點的請求量的大小,如果效能、可用率、異常量、訪問量出現異常可第一時間標紅報警,出現任何故障可第一時間聚焦到具體某一個點上,極大提升了定位和處理故障的時間;
異常監控,系統依賴的外部服務以及系統內部丟擲的任何一條異常的堆疊資訊都被非同步埋點記錄下來,進行實時的統計和報警,對系統健康度的把控起到至關重要的作用;
使用者行為的跟蹤,詳細記錄使用者在什麼時間做了什麼事情以及當時的網路情況,方便使用者出現問題時回放出問題時的現場,快速幫助使用者解決問題。
監控細節還有很多,以上幾個監控手段對當前業務系統幫助非常大也是非常實用的一些能力,現在業務系統已經做到非常透明,任何人都可以清晰看到系統的健康度,並且部分服務具備通過故障自動容錯的能力,對系統整體的穩定性提供了非常大的保障。
6. 限流手段
京東APP上所有商品價格庫存都是分割槽域的,因此很多刷子以及爬蟲不斷的來爬京東各區域的價格、庫存和促銷資訊等,有一個很明顯的特徵就是大量刷子刷時使用者登入態的佔比會明顯下降,因此單品頁針對使用者的行為實時行為資料進行分析和控制:
流量清洗能力,根據使用者的pin、IP的實時分析和清洗,並可以根據已登入和未登入做不同策略;
系統過載保護能力,任何時候不能讓系統掛掉,把明顯的惡意流量清洗之後進行按一定策略進行排隊,保障流量不超過系統極限負載,同時給使用者比較友好的一些互動體驗。
7. 壓測
單品頁壓測比較麻煩,一方面壓測的流量大,對線上可能會造成很多不可預知的問題,另一方面涉及的基礎服務比較多,牽涉的人就多,每次壓測要協調上下游幾十號人支援,協調成本比較高,第三方面壓測的商品數量都在上百萬的商品,每次壓測的SKU會變更,指令碼變更比較大,第四每次壓測完成之後需要人工形成壓測報告並分析其中的薄弱環節問題,因此APP端產出了一個自己的壓測平臺,通過流程方面來協助解決以上幾個問題:
啟動壓測任務可自動收集壓測資料併產出需要的壓測指令碼:
線上流量的隔離;
通知相關方,確認壓測時間和壓測方案;
按照壓測指令碼逐步進行壓測任務執行;
形成壓測報告,並分析壓測過程中問題點。
壓測資料準備方面:
線上流量日誌進行回放,並且按照壓測目標放大到一定倍數來回放;
按照各品類的流量佔比選出一部分商品作為熱點資料來進行壓測,檢驗各環節對熱點資料的處理是否合理;
針對一些埋點以及統計要能清洗掉這部分資料,目前主要是根據請求的一些固定特徵,比如裝置號和特殊標識來進行區分,並且上下游都要能做到一致的資料清洗規則。
四、未來方向
單品頁還有很大的一些優化空間,比如為適應快速的業務迭代進行系統重構、jvm垃圾收回策略和堆記憶體分配大小的調整、非同步化的改造等等優化正在進行,未來單品頁最重要的幾個方向:
動態配置化:不同品類商品可根據單品頁元素動態形成一個個性化的單品頁,做到完全樓層可配置化;
精細化:流控、自動化降級等方面能夠根據使用者特徵,比如使用者級別、地域等可執行不同策略;
智慧化:根據使用者畫像資料展示更多個性化推薦的資料,千人千面,給使用者提供更有價值的資訊。