【大前端攻城獅之路】愛番番線索列表進化之旅

導讀:「以客戶為中心,技術為產品服務」是百度愛番番線索管家團隊一貫遵循的原則。技術架構規劃首先應該圍繞業務訴求展開,用合理的技術賦能產品,產品在不斷的演進中又對技術提出更高的標準和要求。作為愛番番PV最高的頁面,本文將詳細介紹線索列表如何從快速交付的刀耕火種原始狀態,逐步走向「高可用、高質量、高體驗「的成熟期。

全文9355字,預計閱讀時間24分鐘。
在後台系統中,列表是最常見的數據展示方式之一,它就像系統的水電煤一樣平常,以至於你可能會問列表開發能有什麼技術挑戰呢?

一、列表應該提供什麼能力?

列表頁有三個基本模塊:

  1. 搜索
    1. 搜索框(定向查找某條數據)
    2. 篩選項(預置搜索條件快速找到符合條件的結果)
  2. 數據呈現
    1. 表頭
    2. 數據
    3. 分頁器
  3. 操作項
    1. 操作按鈕(對整行數據),比如線索列表的分配、打電話
    2. 修改數據(對某個字段),比如添加標籤

 

 

 

列表承載着業務上的數據,列表的設計體驗關乎用戶對業務數據的處理和管理效率,最終的目標是為了能提高客戶使用數據及決策的效率。

二、技術落地演進

隨着愛番番產品的快速迭代與演進,線索列表也從快速交付的刀耕火種原始狀態,逐步走向「高可用、高質量、高體驗」的成熟期。下面將逐一介紹線索列表經歷過的不同版本,踩過的坑以及相應的解決方案。

V1 – 快速落地基礎能力

愛番番項目初期,為保證項目快速落地,幫助用戶及時快速查詢和跟進線索,線索信息存放到MySQL,提供基礎的CRUD能力。

 

 

  

V2 – 配置擴展能力

隨着業務不斷演進,基礎的表單及列表功能與用戶可配置、可擴展需求之間的矛盾愈發突出,如何支持自定義能力,是線索列表面臨的一大難題。愛番番線索管家團隊主要從「元數據驅動」,「指標解析器」,「前端渲染引擎」三方面着手解決。

2.1 元數據驅動 

伴隨線索產品不斷迭代,線索預置字段數量不斷增加。並且線索升級能力支持自定義字段,原來頁面固定的檢索項已不能滿足用戶對所有線索字段進行檢索的需求,所以我們提出了通過自定義檢索項元數據動態完成檢索項的渲染,以滿足用戶的個人偏好。

 

 

 

1、UI渲染信息完成檢索項在頁面動態渲染2、檢索條件構建器,動態構建檢索項指標數據3、檢索項操作符支持不同指標的檢索範圍4、檢索項關係符可以動態組裝檢索條件的查詢場景5、當有新的篩選指標需要添加時,只需要添加檢索項元數據和實現檢索項的構建器

 

2.2 指標解析器

隨着線索字段的不斷增加, 在列表中需要實現表頭的靈活配置,和列表項的快速加載, 提煉了自定義列表指標項元數據。

 

1、 自定義列表在列表檢索的這個服務中,通過接入通用自定義指標服務來完成用戶自定義列表項的靈活配置。2、 指標檢索配置化通過引入檢索指標的概念,可以將列表字段的查詢抽象為對指標的查詢。這樣的話不同的查詢場景即可建模為對不同指標數據的查詢,指標數據之間可以任意組合以滿足需求。此時底層只需要提供一個通用的指標檢索服務即可。3、 後置處理器對於不同的場景,列表字段需要不同的展現形式;通過引入後置處理器,可以通過配置化的方式定製渲染邏輯。新指標只需在配置對應的指標元數據即可。

2.3 前端配置化引擎設計

最初線索列表和篩選項僅支持較少字段展示和篩查,因此我們只需要對特定字段進行特殊處理UI組件進行渲染即可。但隨着業務複雜度增加、字段增多以及自定義字段的加入,單純針對特定字段進行特殊處理進行渲染帶來了巨大維護成本(增加字段需要為此字段單獨開發UI組件,需不斷調整相關業務代碼)和測試回歸成本,且無法滿足自定義字段的篩選及展示。於是引入配置化引擎設計,以組件為基本單位,通過組件參數配置完成頁面相關內容渲染。

1. 每個組件均是由元數據驅動的標準化組件,分為組件和配置兩部分;

2. 將後端返回的組件配置信息和本地默認配置merge後生成最終配置;

3. 組件配置引擎通過解析組件配置,動態化的綁定事件、傳遞數據、渲染組件;引入配置化引擎設計後,前端只有在新增組件類型時才需要投入開發人力,對於其他同質化需求僅需後端修改配置即可生效,極大降低了開發和測試回歸成本。

V3 – 升級體驗

3.1 背景

線索列表是線索管家的核心業務場景,隨着業務的不斷發展,急需對線索列表的體驗進行升級。

3.2 設計目標

秉持信息易懂、體驗易用的設計價值觀,通過對列表信息進行重組和體驗升級,從而提升線索列表客戶滿意度。

3.3 設計思路

3.3.1 頁面拆解

線索列表頁由標題、工具欄、表格三大模塊構成;

  1. 標題區:包括標題、規則說明等信息,概括整個頁面的信息;
  2. 工具欄區:包括數據過濾(篩選、搜索)、功能操作(新建、導入…),承載頁面的增、刪、改、查等操作;
  3. 表格區:包括表頭、表體、分頁,展現頁面的核心信息。
3.3.2 核心痛點
  1. 標題區:規則不明確,理解成本高;
  2. 工具欄區:數據過濾及功能操作密度大,干擾信息繁雜;
  3. 表格區:核心信息展示屏效比低,操作成本高。
3.3.3 設計策略
1. 標題區升級
  • 【標題區】規則說明由圖標優化為圖標+文字形式,直觀易懂;

2. 工具欄區升級

  • 【功能操作】外露重要高頻操作,摺疊低頻操作,降低對用戶的干擾;
  • 【數據過濾】外露高頻篩選,摺疊低頻篩選,提升核心表格區屏效佔比;

3. 表格區升級

  • 【表頭】頁面上滑,表頭吸頂展示,易於定位字段信息,拓展線索列表縱向空間;
  • 【表體】頁面橫滑,橫向滾動條懸浮於屏幕底部,提升橫向操作效率;
  • 【表體】操作列採用主次形式,外露高頻功能,摺疊低頻功能,有效提升操作效率和功能擴展空間。

 

 

 

3.4 設計收益

通過構成、交互、反饋、適配四個維度將頁面信息進行深挖與重組,核心表格區屏佔比達到70%,數據展示更合理,提升線索列表頁面易用性與客戶滿意度。

V4 – 升級檢索能力

4.1 現狀和挑戰

目前系統線索預置字段58個,動態擴展字段125個以及億級線索數據。業務不斷升級所有線索字段納入自定義檢索(多條件檢索,分詞檢索等)和日益膨脹的數據讓檢索的性能面臨挑戰, 我們使用ES替代MySQL來實現高性能的高級檢索功能。

4.2 方案設計

名詞解釋-WATT:百度數據流開放平台-瓦特(WATT), 是數據流自助開發和運維平台。數據流捕獲mysql數據庫中變化數據實時發佈出來, 以增量和基準兩種方式進行發佈, 提供文本格式數據,保證實時性、數據的有序和不丟,還可以對訂閱數據進行計算。

 

 

方案:檢索由DB遷移到百度雲ES服務,用戶操作完線索後數據更新到DB, 再通過WATT、MQ和寫入服務將數據同步到ES。收益:提高查詢效率, 支持全文分詞場景,數據進行相關度排序等,支持任意檢索條件組合查詢不影響查詢性能。

V5 – 支持寫後讀(Read your writes)

5.1 現狀和挑戰

線索列表檢索由MySql遷移到ES後,又帶來了新的問題,DB在同步ES的過程中,容易受依賴環境影響導致數據更新有延遲,導致用戶在線索列表看到的不是最新數據,對用戶體驗有一定的影響, 從而對線索列表的穩定性和準確性帶來了新的挑戰。

數據流現狀如下: 

上述中DB同步ES共需要5步,任何一步出現延遲都會造成線索列表數據更新不及時。

5.2 優化目標

即使DB到ES數據同步存在延時,也能保證線索列表中的數據與用戶操作後的結果一致。

5.3 方案設計

 

 

1、用戶對線索進行操作(新建,分配, 編輯等)後, 將操作後的線索ID寫入到redis有序隊列中,由於DB同步到ES延遲時間大概在0~2s間,因此redis隊列自動過期策略設置為10s。

2、redis隊列只寫入線索ID,而不是存儲完整的線索數據快照: a、線索屬性多、結構復、迭代變化快,只記錄線索ID比較輕量級,線索本身變更不影響redis中緩存的數據結構;
b、查詢時根據ID從DB中查詢,數據更準確,因為沒有事務去保證redis與DB中的數據強一致性。

3、用戶查詢線索列表數據a、獲取redis有序隊列中最近5s的線索IDb、組裝ES篩選條件查詢ES數據,獲取redis線索ID篩選DB數據, 為了保證列表的查詢性能,採用並發查詢c、通過表達式引擎,根據ES篩選條件過濾DB線索數據,獲取有效的查詢結果d、通過merge策略完成ES數據和DB數據merge
merge 策略為了能夠簡單闡述merge的各個場景,我們先定義一下幾個對象1、leads_es:線索對應的ES結果視圖
2、leads_db:線索對應的數據庫視圖
3、result_es = {….} :ES查詢結果的結果集
4、result_redis_db = {….}:通過redis獲取線索ID查詢DB的結果集
5、result_redis_db_filter = {…}:result_redis_db根據篩選條件通過表達式引擎過濾後的結果集
6、candidate = {….}:最終融合到的結果集我們稱之為候選結果集

 

 

candidate 選擇策略1、有新增或修改操作後的線索且滿足過濾條件的,應該把DB中查詢到的線索加入候選集2、沒有操作過的線索且滿足過濾條件的,應該把ES中查詢到的線索加入候選集3、操作過的線索且不滿足過濾條件的,不應該加入候選集

效果收益:上線後無用戶在反饋操作線索後列表數據延遲類問題,線索打標籤後首次查得率、編輯線索後首次查得率均為100%。

V6 – 性能優化 

6.1 如何度量性能

If you can’t measure it, you can’t improve it!
頁面性能度量是優化的前提,而前端性能監控也是一個經久不衰的話題。監控方向、監控指標、埋點方案、上報工具都是需要考慮的點,業界商用打點平台及廠內埋點平台,均無法解決易用、實時、前後端全鏈路監控等問題。為此愛番番啟動「大前端APM」專項,參考服務端RED指標及業界主流前端埋點方案,探索最適合愛番番的前端APM體系架構。

6.1.1 監控目標

從全局問題出發,能夠洞察統計性的頁面url真實性能指標,以及可以做操作流任意階段之間的統計性耗時分析。從個案問題出發,能夠基於用戶id進行任意一次全端調用鏈追蹤。

6.1.2 解決方案

採集

埋點SDK:愛番番業務打點使用付費系統「神策」,為避免重複造輪子,前端APM SDK基於神策SDK進行二次封裝,通過npm包形式進行版本管理;使用方在公共模塊對埋點SDK進行初始化。增加無侵入性能採集能力,提供採樣率等配置擴展能力。

上報

綜合image、sendBeacon、Ajax上報方案。首先拼接攜帶參數的完整的url,判斷url的長度,如果url的長度小於瀏覽器允許的最大長度內,則通過動態創建img標籤的形式來發送前端性能數據;若url太長,則判斷瀏覽器是否支持sendBeacon方法,如支持則通過sendBeacon方法來發送請求,否則發送同步的ajax請求。示例代碼如下:

function dealWithUrl(url,appId){
    let times = performanceInfo(appId);
    let items = decoupling(times);
    let urlLength = (url + (url.indexOf('?') < 0 ? '?' : '&') + items.join('&')).length;
    if(urlLength < 2083){    imgReport(url,times); }
        else if(navigator.sendBeacon){    
                sendBeacon(url,times);  
        }else{ajaxReport(url,times);
    }
};

  

前後端Trace打通

 

 

6.2 性能問題&目標問題:前端APM在2021年Q2全面落地,使得愛番番前端性能監控邁出一大步。但同時暴露出不少頁面性能較差、大數據量用戶可感知時長較長等問題。其中線索列表性能問題尤為突出,頁面初始化用戶可感知時長超過5000ms(P90);目標:通過對愛番番所有頁面性能統計分析,前端整體性能指標為「頁面初始化用戶可感知時長低於2000ms(P90)」,分段拆解目標如下:

 

 

6.3 優化思路&方案整體優化思路及節奏:先抓主要矛盾,摘低垂果實;再摳細節,進行難點攻堅。每個方向均做到極致,各個擊破。

  • 【鏈路】釐清頁面完整鏈路及耗時,全面了解頁面性能現狀。
  • 【後端】深入分析後端代碼實現邏輯,從並發、緩存、es調優、線程池調優,依賴超時分析等方面着手優化接口性能。
  • 【前端】分析前端代碼、編譯配置、瀏覽器Performance火焰圖,從靜態資源體積、JS Runtime Long Task、渲染性能等方面入手優化前端性能。
  • 【交互】與產品、設計共同探討交互升級方案,以達到極致用戶體驗。

6.3.1 耗時鏈路分析名詞解釋:BFE,Baidu Front End,百度統一前端,是百度統一的七層(HTTP/HTTPS)流量接入平台;為整個百度提供流量接入服務。
用戶可感知耗時鏈路:

  1. 從CDN節點加載靜態資源(靜態資源耗時)
  2. 執行js文件,發送Ajax請求(接口總耗時開始)
  3. 瀏覽器發出Http請求,抵達BFE的網絡耗時
  4. BFE轉發至Access Gateway鏈路耗時
  5. Access Gateway轉發至BFF服務鏈路耗時
  6. BFF自身服務耗時(包含服務內部請求、拼裝微服務等),由Skywalking統計
  7. BFF返回response,至Access Gateway鏈路耗時
  8. Access Gateway返回response,至BFE鏈路耗時
  9. BFE返回response到瀏覽器網絡耗時
  10. 瀏覽器渲染耗時

問題:

  • BFF Node.js 服務調用後端微服務鏈路耗時長。
  • BFF通過域名-BFE-微服務方式調用時,BFE部分VIP存在連接超時問題無法徹底解決。

優化前BFF調用鏈:

 

 

方案:

  • BFF模塊升級Mesh服務。BFF調用後端服務方式,由網關域名調用,升級為Mesh service調用。

優化後BFF調用鏈:

 

 

 

收益:BFF調用微服務鏈路耗時降低100ms+,BFE VIP連接超時頻發問題得到根治。
6.3.2 接口性能優化通過分析接口實現邏輯以及SkyWalking調用鏈,發現下面三類問題:第一類問題:代碼實現問題。屬於低垂的果實比較容易摘到。問題:

  • BFF及後端接口鏈路存在非必要串行請求
  • 前後端均存在可以使用緩存場景

方案:

  • 在BFF實現層面,將三次串行請求,優化成必要的兩次串行,其餘均使用Promise.all並行處理
  • 對服務端接口後置處理器,統一使用線程池異步處理
  • 對標籤元數據、表頭配置元數據等接口增加Redis緩存
  • 前端對權限、查詢條件等接口進行預加載,並增加本地緩存

第二類問題:性能調優。這部分需要了解ES、RPC框架底層實現原理。問題:

  • 使用ES檢索模糊查詢性能較差
  • 線索服務壓測不達標,高QPS下請求排隊

方案:

  • 使用全文檢索來代替模糊查詢
  • 調整RPC調用RPCIoWorkThreadNumber工作線程數

第三類問題:超時問題。這部分優化場景複雜,難度較高。超時問題有幾類:問題:

  • RPC調用連接超時:老版本Mesh sdk每次發起請求時會重新創建新的連接,導致調用方在並發大或者訪問的服務平響高時,會出現連接等待,以至連接超時。
  • 網關查詢超時:線索所屬DB集群 sfcrmsales磁盤利用率較高,慢SQL及長事務較頻繁,DB集群穩定性不高,導致DB查詢頻發超時。
  • 依賴外部服務超時:線索列表需要對線索池權限等進行鑒權操作,依賴ACS團隊權限接口超時頻繁;列表檢索完畢後,需要對部分字段進行加工,涉及不少外部團隊HTTP請求調用,其中大商業廣告信息open API調用超時最為頻繁。

方案:1. 對Mesh SDK進行連接復用改造,並調整服務自身IO線程數,解決QPS高的情況下連接等待問題;2. DB集群穩定性治理

  • 設立DB穩定性治理專項,增加DB告警、值班機制;
  • 聯合DBA對慢SQL、長事務、連接數異常、主從延遲高等問題進行徹底分析、排查、解決、驗證、根治;
  • 對DB中重試表、分配日誌表、outbox表等大表進行清理,對線索原始信息主表進行定期數倉轉儲,並清理表中大字段;
  • 對集群中非一級業務表進行遷庫操作,減輕線索DB集群存儲壓力。

3. 超時問題治理

  • 調整ACS服務線程池大小,對ACS服務進行水平擴容;
  • 對廣告信息接口進行   緩存+離線數倉+實時調用  組合查詢方案,並調整後置處理器超時時間,保證絕大部分場景查詢速度。

收益:線索列表服務端耗時(P90)從2500ms+,縮短至800ms以下

6.3.3 前端性能優化

前端性能問題主要集中在渲染卡頓和靜態資源加載耗時長兩方面。
第一方面:列表渲染優化背景:愛番番前端整體採用vue技術棧,elementUI作為vue生態中最受歡迎的UI框架,因其強大的功能、健全的生態以及活躍的社區,被選做愛番番前端UI框架。線索列表使用element-ui中el-table組件,能夠支持列表場景常見需求。
問題:1.大數據量(100行、50列)情況下,JS Long Task時長將近3000ms,線索列表渲染卡頓。2.數據整體渲染時間較長,用戶等待(loading)時間長。3.el-table不支持吸頂、吸底操作,單純通過修改CSS樣式亦無法實現,體驗不佳。
分析:閱讀el-table源碼發現,el-table的實現是通過四個HTML 原生table(表頭、表身,列表左右側勾選列)實現。大數據量下,DOM數量會變得異常龐大;尤其是在表頭拖拽,對頁面進行Repaint和Reflow時計算量巨大,Long Task較長,導致頁面卡頓。調研基於動態渲染思路的開源組件pl-table,不支持吸頂功能,且在行高不固定的情況下支持情況不良,被排除。
方案:1.通過調研業界主流UI框架,只有React體系中的AntD table支持吸頂功能,但其vue版本存在滯後性並未支持該提醒。經過組內討論,決定參考React AntD table實現思路,重新實現AFF-UI table。2.列表數據漸進式渲染。先渲染首屏數據,結束loading為用戶呈現首屏列表。同時在用戶無感知的情況渲染其他列表數據。
收益:1.AFF UI table相比el-table,相同數據量下,DOM數量減少60%,100行、60列數據量下,渲染性能提升300%;2.支持列表吸頂吸底功能,體驗提升較大;3.漸進式渲染,首屏可感知渲染時長(P90)從2000ms降低至500ms以下。
第二方面:靜態資源優化背景:愛番番前端使用自研Tangram微前端架構,分為主模塊「common」,以及其他業務域子模塊。支持各敏捷小組頁面分代碼庫,獨立部署運維。前端靜態資源均部署在百度雲BOS上,通過BFE進行域名靜態資源轉發定位。
問題:

  • 靜態資源經過BFE轉發,增加鏈路耗時
  • 靜態資源未使用CDN網絡
  • 靜態資源體積較大

分析:靜態資源優化思路,從鏈路、緩存、編譯三方面入手。鏈路和緩存,是前端優化常見手段,方案大同小異。編譯的問題一般隱藏較深,需要對webpack編譯打包原理有一定了解。下面着重介紹下編譯相關分析過程:1、首先通過監控平台,對js加載瀑布流進行分析,定位到chunk-vender.js存在耗時較高的情況,為性能瓶頸。

 

 

 

2、通過 webpack analyzer 對編譯情況進行分析:

發現存在以下問題:

  • 業務域代碼中chunk-vendors.js打包了微前端框架底座 biz-crm-fe-common模塊,這部分能力可以在主應用中提供,業務模塊打包是應該排掉。
  • 部分依賴庫(bce-sdk、tangram-ui、moment、lodash)體積巨大,需要考慮作為公共依賴放入主應用,或者在頁面通過Promise異步加載。
  • 代碼庫路由較多,復用組件較多,導致chunk-common.js體積較大,且靜態資源整體體積大。

 

 

 

方案:鏈路:

  1. 靜態資源直接請求BOS域名,不走aifanfan.baidu域名,減少一層BFE轉發鏈路
  2. 開啟百度雲BOS CDN加速
  3. 開啟靜態資源gzip壓縮

緩存:

  1. 在首頁中預加載線索列表相關靜態資源
  2. 在HTML預加載線索列表相關JS,利用webpack require特性,全局緩存列表相關JS(該方案是方案1的進階版,預加載更前置、徹底)

編譯:

  1. 修改微前端框架tangram-sdk編譯配置,排除biz-crm-fe-commom包
  2. 拆分前端代碼庫。將列表和詳情之外的非核心頁面遷移新代碼庫,保證列表js體積最小化
  3. 進行異步加載大體積依賴js,使得初始化加載js體積壓縮到極致

收益:

  1. 靜態資源耗時(P90)從 2000+ms,優化至500ms以下
  2. 靜態資源gizp總體積:693.52KB → 228.83KB,總體積縮小67%

附編譯優化前後js體積對比(飄綠為優化後)

 

 

 

6.3.4 體驗優化

問題:

  1. 列表全屏loading,白屏時間長;
  2. 列表篩選條件全部、實時加載,客戶感知時間長,且會有抖動;
  3. 表頭數據默認值可全選(50+列),請求、渲染數據量大。用戶在使用時需要橫向滑動3-5屏,體驗不佳。

方案:

  1. 列表篩選使用側拉麵板交互,本地記憶上次篩選條件並展示;
  2. 優化loading區域及方式,從視覺感知上最小化用戶感知時長;
  3. 通過對所有用戶自定義表頭設置數據分析,及參考業界通用設置,增加表頭數量上限。

收益:性能優化和體驗提升殊途同歸。在UBS調研中,愛番番線索管家整體體驗和性能在業界均處於領先地位。

6.4 收益

經過上述優化手段,線索列表整體性能達標。

 

 

回顧將近一年的性能優化旅程,大致經歷了下面幾個階段:

  1. 「無從下手」用戶反饋列表性能存在問題,嘗試解決,但無法度量,在黑暗中摸索。(20年Q4 — 21年Q1)
  2. 「磨刀不誤砍柴工」調研前端性能監控解決方案,啟動愛番番大前端APM Topic。(21年Q1)
  3. 「有的放矢」APM建設,web端全面落地。對線索列表性能有全面了解,優化工作逐步清晰明朗。(21年Q2)
  4. 「先摘低垂果實」使用常規手段,對前後端常見性能問題進行解決和優化。(21年Q3)
  5. 「難點攻堅、毫秒必爭」不放過任何一個優化點,對前後端難點問題分別成立專項小組,集思廣益,重點攻堅,不拿結果誓不罷休。(21年Q4)

三、總結

「以客戶為中心,技術為產品服務」是愛番番線索管家團隊一直遵循的原則。技術架構的規劃首先應該圍繞業務訴求展開,用合理的技術賦能產品,產品在不斷的演進中對技術提出更高的標準和要求。線索列表是愛番番眾多核心功能其中的一點,但管中窺豹,技術問題往往要從業務中尋找答案,理解業務是開展技術的前提。同時,業務的複雜度升級反哺技術架構進化。業務與技術相互促進,相輔相成,最終為用戶創造價值。
產品和技術演進只有起點,沒有終點。展望未來,線索列表能力通用化,配置擴展能力向PaaS平台演進,LowCode及NoCode等方向均需要繼續探索實踐,我們要走的路還很長。

四、作者介紹

本篇系愛番番線索管家團隊多位同學共同編寫。

  • 飛邪:架構師,擅長通過微服務架構和DDD落地複雜系統
  • TJ:設計師出身,努力向服務端轉型的前端開發
  • Hana:一隻耕耘在To B行業的設計獅
  • 東拾:資深研發工程師,擅長業務系統架構設計
  • 三木:  web前端工程師,擅長各種擼貓