京東上千頁面搭建基石——CMS前后端分離演進史
京東CMS簡介
CMS即內容管理系統(ContentManagementSystem),目的是用于快速進行網站建設或者網頁開發。對于京東網站部門來說,CMS核心目的是用來快速開發和上線各種頁面,諸如各種垂直頻道頁,訪問www.jd.com將看到如下頁面,如點擊“服裝城”、“家用電器”等都會跳轉到一個垂直頻道頁;這些頁面中有許多頁面風格是類似的,因此很適合使用CMS進行快速搭建。
對于我們來說,CMS最核心的目的就是進行數據和模板的統一管理、頁面的統一發布,從而減少之前的很多重復工作。
京東CMS是2014年提出來的,經過兩年多的完善,目前已經發展為一個集標準服務管理、標準組件服務和智能投放于一體的標準化導購運營系統。具有以下特點:
1. 搭建快速,統一發布,統一架構;
2. 前后端分離,后端不再負責頁面渲染,只提供高性能、可復用的API;
3. 移動端頁面支持;
4. 數據分析、智能投放的特點;
業務支持場景:
首頁、頻道頁、垂直頁、活動頁的搭建及單品頁、列表頁部分可維護的業務等。
從基本功能及架構來看,可以分為三個階段:
CMS 1.0——虛擬分類系統
CMS 2.0——CMS系統
CMS 3.0——CMS-portal系統
CMS 1.0—虛擬分類系統
虛擬分類系統是一個獨立的促銷商品、促銷文字維護系統,與具體前端業務架構脫離,哪條線接入虛擬分類,需要根據自己的業務邏輯單獨開發、維護、部署自己的架構。說白了,虛擬分類系統提供一些基礎數據;然后比如我要搭建一個家電頻道頁,則需要開發一個Web項目,然后調用虛擬分類系統獲取數據然后進行模板渲染處理處理。因此虛擬分類系統當時只是一個基礎數據維護平臺,無法實現信息的共享、復用和集約化管理。這就會存在各種各樣的頻道頁系統,導致管理混亂,性能上各有差異,出現過很多次事故。而且各系統需要獨立配置,導致工作量大,占用資源多,無法快速響應業務需求。
下圖是當時不同業務體系的架構:
可以看出部分頻道頁和活動頁用的nginx+tomcat,部分頻道及垂直站用的nginx+php-fpm,與虛擬分類聯系不大,總體遵循各業務層獲取虛擬分類的數據,分別獨立上線、部署、維護,應用層直連mysql,mysql 抗不住,會增加一層 memcache。
CMS 2.0—CMS系統
Cms2.0總結了1.0時的不足,從節省資源、控制成本的角度考慮,把導購類的個體頁面業務進行了統一,模板能復用的復用,以前虛擬分類系統的頻道也需要遷移到新的系統。
我們做了以下改動:
1. CMS 2.0數據結構適合虛擬分類體系,方便新老數據兼容;
2. 升級架構,統一配置、發布流程;去memcache為redis,做大量性能壓測來調優php-fpm配置,單機TPS能達到3000+; 配置定時任務,保證redis數據實時性;
3. 單點發布,一鍵預覽增強采銷維護數據的機動性;
4. 單機閉環,單機閉環服務設計是CMS整體架構的核心部分,需要展示的內容在本機獲取、封裝、校驗;
5. 模塊化、動態數據類型初期版本(CMS 3.0會細說);
架構圖:
對比1.0,新的CMS可以讓頻道頁的開發周期降低2~4周,大大提高了業務需求的響應速度;它看起來更獨立,更像一個整體,在容災、規避風險方面做了嚴謹的優化;同時讓采銷在維護數據時,更方便、更簡單。
后續由于個性化的需求越來越多,大量的頻道業務需要開發人員一個一個套模板來實現,大大加大了開發人員的工作強度,之前的模板復用方式已經無法滿足業務的需求,同時太簡單的數據模塊,需要手工來綁定數據類型也增加了開發成本,這時候需要我們必須做出改變。
CMS 3.0—CMS-portal系統
CMS 2.0后也存在很多痛點,因此我們也想在CMS3.0上解決這些問題:
1. 本質問題就是要快:快速支持、響應業務越來越多的垂直化頁面改版;
2. 前后端分離,頁面渲染讓前端實現,解放后端讓后端做高大上的事情;
3. 減輕運營人員的工作量,系統簡單好用,提高導購類商品的轉化率;
4. 其他系統的支撐,實現CMS的集約化管理;
5. 兼容手機端;
6. 站點管理、統一架構、容災、高性能、水平擴容;
通過兩版CMS系統的開發,我們發現CMS無外乎管理的是數據和模板,另外需要好的預覽、一鍵發布支持。而傳統數據管理都是靜態數據類型,而我們做了動態數據類型的設計;另外我們做了模板管理中心,并抽象了模板、樓層、元件、模塊的概念,從而實現更好的復用性。
統一架構
主要分為如下幾部分
- CMS系統:統一管理CMS相關數據,并進行頁面的生成和發布;
- 數據Worker管理中心:調用第三方服務進行數據校驗(如庫存不足補貨),并調用CMS系統進行頁面生成和發布,發布到單點服務器上;
- 單點服務器:相關頁面的單機閉環實現,即CMS發布的頁面會存儲在這些單點服務器上;每個機房會部署一臺;
- 頁面定時更新Worker:定期同步單點服務器內容到靜態應用服務器集群,并保存歷史版本,當出現問題時可以切換回上一個版本;
- 靜態應用服務器集群:靜態托底實現,會存儲相關的靜態頁面文件,當單點服務器掛了時,降級到該集群;
- 異步加載的個性化服務:有些數據不能存儲到靜態頁,如價格/庫存/推薦等數據,此時通過異步加載這些數據,實現個性化服務;
- 接入層Nginx:接入層Nginx負責請求的路由和服務的降級。
主要思路
1. 引入動態數據類型;
2. 頁面模板管理中心,模板、樓層、元件、模塊設計,實現可復用;
3. 使用元件實現前后端分離;
4. 動態服務和業務數據閉環;
5. 預覽、一鍵發布,單點管理;
6. H5版直接搭建,native版 API 支持;
7. 大數據智能選品應用;
動態數據類型
所謂的動態是指能靈活擴展的,不需要上線也不需要修改數據庫字段,支持自由擴展;這樣做的好處是能夠快速響應電商網站的日益靈活多變的促銷需求。比如促銷語:
目前常用的數據類型為文字鏈、小圖文、商品池等。
操作界面:
動態數據類型數據結構:
fields是json串,用于動態定義字段。
使用元件實現前后端分離
使用動態數據類型定義了數據之后,需要在模板中使用它。而在我們CMS系統中進行了頁面、模板、樓層、元件、模塊的劃分。模塊是某種數據類型的具體化,即有了數據的數據類型。元件是由模塊和HTML代碼段(根據模塊數據進行渲染的一小段模板)組成;樓層通過一系列元件組成,而模板會引入多個樓層,當然也會引入一些JS、CSS等,最終通過模板渲染出相應頁面。
type是數據類型表,module是模塊表,source是數據表,按照上面的邏輯我們是通過數據類型獲取到數據模塊,并同時能拿到該模塊所對應的商品數據(商品池)。
有了這個元件之后,就可以徹底解放后端,頁面渲染工作完全交由前端來開發,實現了前后端的分離。
即CMS研發只負責平臺和基礎數據(動態服務)的維護,業務人員進行模塊的維護,而前端人員獨立完成元件開發、模板設計、開發和發布。
動態服務
跨線條業務間的資源復用、獨立調用時需要提供相關的API,如三級地址服務,類目服務、動態加載的數據(如爆款特賣、今日推薦等)等。數據格式滿足數據閉環原則。Lua+redis架構實現,單機(16U)QPS能達到20000+。
頻道業務數據閉環
數據閉環,即數據的自我管理,或者說數據都在自己系統維護,不依賴與其他任何系統,去依賴化,這樣得到的好處是別人抖動與我沒關系。因此我們先要數據異構。
數據異構是數據閉環的***步,將依賴系統的數據拿過來,按照自己的業務需求存儲起來。頻道業務需要異構的數據主要是三部分:商品基本信息、第三方數據、大數據。
數據原子化處理,數據異構的數據是原子化數據,這樣未來我們可以對這些數據再加工再處理而響應變化的需求。我們有了一份原子化異構數據雖然方便處理新需求,但恰恰因為***份數據是原子化的,那么它會很分散,前端讀取時mget的話 性能不是很好,因此我們又做了數據聚合。
數據聚合,是將多個原子數據聚合為一個大JSON數據,這樣前端展示只需要一次get,當然要考慮系統架構,比如我們使用的Redis改造,Redis又是單線程系統,我們需要部署更多的Redis來支持更高的并發,另外存儲的值要盡可能的小。
容災
應用層容災
1. 數據校驗,CMS在頁面預覽有一層嚴格的數據校驗邏輯,比如數據格式、數據大小、敏感詞等,保證頁面生成100%沒有問題。
2. 版本降級,靜態頁面出現問題,除了頁面本身數據有問題外,潛入的js、css出現問題也會影響頁面展示,這時候會版本降低為前一天的正確版本;
3. 異步服務,異步化數據容災方面主要是監聽服務的狀態及響應時間;降級訪問有隱藏該功能和切換服務器實現;
服務器容災
主要是通過多機房部署,監控80端口,出現問題可以自動把流量水平切走。
智能選品
智能選品,是服務于前臺的流量運營,為采銷及運營人員提供運營支持,為每一次訪問提供最合適和匹配的商品、品牌以及促銷活動。是根據用戶的行為推薦出相關的商品及活動。可以分為群體特征和個體特征。群體特征分為兩部分,數據部分及規則部分。
數據部分是從大數據平臺異構過來,當然這個數據是海量的,我們選擇熱點TOP 5000的數據來異構。
規則部分是通過京智后臺創建,在審核通過后,觸發MQ,通過ES 跑出對應數據,然后由頻道頁動態服務系統對外提供json格式的http服務。前端業務以異步的方式傳遞相關規則參數進行調用。
智能選品實現數據化、定制化、個性化、自動及半自動化內容運營。它可以模擬人腦選貨邏輯,以運營指標為導向(GMV、訂單轉化率、點擊量、毛利等),分區域、分用戶選取最匹配的內容。目前應用于京東超市、行業頻道以及618大促主會場,帶來優于人工選品的轉化效果,并解放采銷運營人員日常繁瑣的運營工作,提高了整體效率。
遇到的坑
rsync文件同步
上面的介紹過,我們的靜態頁面為了保持數據的一致性由單點服務器通過rsync同步靜態文件到其他服務器,有時候會發現服務器負載無端的被打滿。
分析問題發現如果定時任務腳本的同步未在規定時間內完成,crontab接下來的還會執行此腳本,這樣就會產生相同的rsync的進程。按照這種狀態,長時間就會衍生出很多個rsync進程,就會導致負載過高,甚至有些服務器會掛掉。這時候我們用到了rsync的進程鎖,在目錄下生成一個rsync.lock文件,當crontab執行時,rsync會判斷鎖文件是否存在,如果存在說明本次同步未完成,則不執行rsync。
數據一定要閉環
別人的接口抖動以及返回數據的異常,影響到前端展示對我們來說說是不能容忍的,這就需要我們針對各種情況一一校驗。
CMS總結
目前通過CMS搭建、正在搭建以及使用CMS API支持的PC端、移動端頁面約有上千個,這對CMS來說意義重大,隨著業務需求量的越來越大,也給我們帶來了新的期望。接下來,我們會從可視化編輯、數據統計分析、關鍵詞管理、商品下架預警等方面進行相關的優化工作。
作者:于林坤,2012年加入京東,網站移動研發部頻道業務技術負責人,先后多次主導京東商城首頁、頻道頁技改及架構升級,在高并發系統架構、前端系統架構與優化方面有豐富經驗,PHPer。
【本文來自51CTO專欄作者張開濤的微信公眾號(開濤的博客),公眾號id: kaitao-1234567】