作者:京東零售 胡本奎
隨著行動網際網路的快速發展,為滿足各類使用者及人群的體驗需求,行動端的開發者們開發了豐富多彩的體驗與功能。同時對於快速控制各類功能的切換、灰度,降級等能力的要求也越來越高,例如通過設定快速開啟某個灰度功能,通過設定資訊的實時觸達關閉某個引起App崩潰的功能等等。因此需要一套具有實時觸達設定資訊到行動端的能力,低沉本的設定平臺來解決。 我們研發了Switchquery設定平臺,它是一套具有秒級變更能力,助力業務快速變更,讓設定資訊在App執行中秒級生效,同時提升設定資訊觸達率,統一管理的設定平臺。
在Switchquery設定平臺核心能力中,實時觸達的能力尤為重要,目前業界主流的觸達技術方案主要有以下幾種方式:
推播push 訊息: push訊息當前已經成為實時推播行銷資訊、重要通知的最主要手段之一,push訊息擁有較強的實時性,而實際的行動端的應用場景中,push訊息多用於行銷方案或重要資訊的通知,很少使用此通道來作為研發設定資訊的觸達通道,其會產生UI互動的變化。另外push訊息依賴於使用者開啟通知許可權開關,而業內普遍開啟通知開關許可權的比例低於50%,因此大部分使用者無法觸達。
tcp或websocket長連線: 通過建立一條使用者端到伺服器端之間的長連線通道,此方案可以在發生設定資訊變更後實時的將資訊傳遞至使用者端,但是需要耗費較大的伺服器資源,來維護一條長連線通道。
3) 輪詢: 使用者端以一定的時間間隔向伺服器端發出請求,通過頻繁請求的方式來保持使用者端和伺服器端的資訊同步,這種同步方案的最大問題是當用戶端以固定頻率向伺服器發起請求的時候,伺服器端的資料可能並沒有更新,這樣會帶來很多無效的網路傳輸。
由於推播push訊息方案存在觸達比例低的問題,長連結存在耗費伺服器資源的缺點,同時輪詢的方案也存在很多無效的網路傳輸等弊端,因此以上三種方案都不是Switchquery 設定平臺的觸達方案的最佳技術選型。通過調研,我們採取了一種設定資訊從伺服器端實時觸達到使用者端的新方案,方案的具體描述為: 先搭建一個資訊設定管理CMS平臺,同時構建一個使用者端獲取設定資訊的使用者端元件,由使用者在CMS設定資訊,然後由CMS後臺將設定資訊的版本號資訊同步至統一閘道器,所有使用者端請求到達統一閘道器,並在返回的介面資料的header內都會攜帶最新的版本號至使用者端,使用者端對比發現新的版本號比快取的版本大則請求設定資訊拉取Switchquery設定介面,這樣只要App開啟就會存在介面攜帶變化標誌返回,這樣就會觸發使用者端主動發起請求更新設定資訊,提高了實時性,不受push開關許可權控制和影響,不需要額外打造長連線通道,具有低成本,實時性高等優點。以下是Switchquery設定平臺的時序圖:
使用者在Switchquery CMS 後臺設定相關資訊,目前平臺上支援設定布林型別、整型、字串等型別的設定資訊,可以設定App版本生效區間,按裝置號灰度比例,iOS或者安卓平臺的設定,生效白名單等相關資訊。
Switchquery CMS後臺設定資訊並提交和儲存完成後,由CMS設定後臺將新的版本號寫入到統一閘道器後臺(所有使用者端到伺服器端的http請求都會經過統一閘道器,所有伺服器端返回到客戶的http請求響應都會經過統一閘道器),統一閘道器記錄下資料版本號,使用者端的所有介面請求的響應header增加一個欄位x-switch-config,此欄位會攜帶回設定資訊的版本號至使用者端。
Switchquery CMS設定後臺完成資訊設定後,後臺會基於當前時間戳,生成一個新的設定資訊版本號,同時將這些設定的靜態資料寫入到伺服器端記憶體快取內,同步重新整理設定開關介面。
Switchquery CMS設定後臺將設定資訊資料寫入和儲存一份靜態資料json到CDN,防止介面降級或者失敗以後可以降級從CDN拉取設定資訊資料。
5) 閘道器會將版本號下發至使用者端網路元件,網路元件在接受到網路請求返回後,首先會解析網路請求的響應header,如果解析到關鍵字將其對應的value一起解析封裝後發起一個全域性通知。
6) 設定使用者端元件在監聽到通知後,與本地已經快取的設定資訊資料版本號進行比對,相同則不處理,大於本地版本號則發起設定資訊拉取請求,這樣即可獲取到最新的開關設定資訊並快取在磁碟。
此種觸達方式只要使用者端開啟即會觸發請求至統一閘道器,隨即就可以根據變化情況來決定是否更新最新的設定介面資料,無需push通知,無需建立長連線通道,成本低,實時性高。
1) 使用者在CMS設定平臺進行資訊設定後,設定後臺介面對設定資訊進行對比,包括設定資訊中的開關的狀態,開關值等關鍵資訊,如果沒發生變化,則結束;發生變化則判斷此次變更距離上一次變更是否到了n秒,距離n秒內則不會觸發設定資訊變更同步,距離n秒外則觸發設定資訊同步,觸發閘道器和後臺介面資料發生變化。
2) CMS觸發變化將新的版本號傳遞至統一閘道器,統一閘道器會做資料版本號的儲存,同時會將統一閘道器的所有機器記憶體裡都儲存一份最新的版本號。 也會將資料變更資訊同步到設定後臺介面,同步寫入一份json靜態資料到CDN,這個是為了防止設定資訊介面伺服器端掛了後可以走CDN兜底。
3) 使用者端任意介面請求都會經過統一閘道器,所有請求的返回也會通過統一閘道器返回,在返回的響應header內新增一個x-switch-config欄位,其value是一段字串,由下劃線隔開,格式如: switch_version_randomtime_reserved,第一個欄位switch=0/1,其中0表示此能力統一降級關閉,1表示此能力開啟;第二個欄位version就是設定資訊的資料版本號,目前是按時間戳的形式標識版本號;第三個欄位randomtime表示使用者端獲取到版本號變化差異後並非立刻請求,會延遲[0,randomtime]之間的一個隨機時間後才發起請求,這個是為了降低瞬間尖峰流量的產生;第四個欄位是留作未來使用。
4) 使用者端網路框架在使用者端會不間斷隨機廣播全域性通知, 開關使用者端元件收到通知後,獲取到統一閘道器的返回資料,解析網路介面返回的header部分,獲取x-switch-config欄位,解析欄位中的value,如果是降級,則結束,如果版本號沒有發生變化,則結束,如果非限流同時 switch=1,並且原生的快取的開關version小於解析後的version,則根據randomtime亂數發起使用者端請求;如果伺服器端返回了特定的限流碼則使用者端直接從CDN拉取設定資訊資料並更新本地快取資料,如果伺服器端正常返回則獲取開關資料並更新本地快取。
在Switchquery 設定平臺的設計開發中,從實時性,效能,成本,穩健等多維度進行了優化,具體的優化措施如下:
考慮到實時性與機器成本的平衡,我們在CMS設定端做了聚合n秒後才將設定變更同步至統一閘道器,主要為了防止多個使用者在很近的時間內做了多個修改,會導致統一閘道器側頻繁的接收到不同的版本號,進而引起使用者端頻繁的發起請求,導致設定資訊伺服器端的流量陡增,目前的經驗值是n=5秒。
考慮效能與機器成本的平衡,使用者端會根據randomtime來隨機發起請求,是為了打散請求發起時機,實際經驗我們發現5s會增日常2倍左右的QPS,10s會增加日常1.5倍左右,實際各個場景可以根據自身的伺服器機器數量與成本來動態決定選擇設定多長時間。
為了保障在大促或者特殊促銷場景下的穩定性和健壯性,我們在設定資訊產生後,首先寫入資料到伺服器的記憶體裡,這樣每次使用者端的請求就直接讀取記憶體效能很高,另外也會寫入一份json資料到CDN,當伺服器端出問題後或在大促主動降級後可以通過CDN來兜底。
實時觸達方案在App原生端來實現此功能,同時對於App內嵌的小程式、H5、RN都提供了橋接元件,尤其webview也可以讀取此設定資訊來實現設定資訊的實時獲取。
//chName:開關名稱,拉取失敗或未取到設定返回defValue預設值
SwitchQueryFetcher.getSwitchBooleanValue(String switchName, boolean defValue)
//獲取int開關值,拉取失敗或未拉取到設定返回defValue預設值
SwitchQueryFetcher.getSwitchIntValue(String switchName, int defValue)
//獲取String開關值,拉取失敗或未拉取到設定返回defValue預設值
SwitchQueryFetcher.getSwitchStringValue(String switchName, String defValue)
//自定義bool開關,獲取不到返回NO
BOOL JDSwitchBoolValue(NSString *key);
//自定義整型開關,獲取不到返回0
NSInteger JDSwitchIntValue(NSString *key);
//自定義字元開關,獲取不到返回nil
NSString* JDSwitchStringValue(NSString *key);
//取intvalue
NSNumber *n1 = [JDRouter openURL:@"router://JDBSHServerConfigModule/intValue?key=test"
arg:nil
error:nil
completion:nil];
//取boolvalue
NSNumber *n2 = [JDRouter openURL:@"router://JDBSHServerConfigModule/boolValue?key=test"
arg:nil
error:nil
completion:nil];
//取stringValue
NSString *n3 = [JDRouter openURL:@"router://JDBSHServerConfigModule/stringValue?key=test"
arg:nil
error:nil
completion:nil];
為了讓研發測試階段和線上的資料安全隔離,預發和線上的資料是隔離的,在預發環境測試驗證OK後,設定資料再同步到線上。
進入CMS介面,選擇左側的模組管理選單,進入模組管理介面,新增業務模組,彈出彈窗如下圖:
名稱:對應業務模組名稱;
關鍵字:設定對應業務模組的關鍵字;
管理員:模組分配的管理員,一般預設為模組的建立者;
成員:該模組分配的管理成員,成員有新增,刪除,修改該模組下的設定許可權,但沒有修改,刪除當前模組的許可權;
說明:對當前模組的描述;
建立好業務模組後,在該業務模組下新建或者編輯設定,編輯介面如下圖:
開關編號:為當前開關設定分配的唯一標識編號;
平臺版本:目前支援android,apple,ipad三種平臺,可支援開關生效App的版本的區間範圍設定,按照左閉右開的原則,預設不設定,全版本生效;
系統版本:設定系統版本的區間,預設不設定,全系統版本生效;
開關型別:目前支援布林開關,整形開關,字串開關以及敏感資料開關(10.4.4版本以上)4種型別開關設定資訊;
開關名稱:設定的開關設定名稱;
開關開啟比例:命中規則為:android 平臺根據uuid,apple平臺根據openudid,通過hash演演算法算出來的值和100取餘加1,如果最終值小於或者等於設定的值即為命中,反之不命中;
白名單:該開關設定對應的白名單,可以通過手動的方式將pin以逗號分隔拷貝到編輯框,未來可接入嚐鮮系統通過掃碼的方式新增白名單;
開關設定值:布林開關預設不展示,整形或者字串型別的設定型別需設定開關值;
開關描述:通過描述知道開關設定的用途;
在2022年春晚X專案中每次口播的時候面臨著流量大,啟動介面多的問題,這樣會造成閘道器壓力巨大,業務癱瘓的問題,會導致整個X專案的失敗,不容有失。因此通過銷峰降頻,減少介面請求等方式進行App啟動降級。通過Switchquery設定平臺,在使用者端下發降級資訊設定,以及修正時間戳等資料,成功的保障X專案中App啟動介面的成功降級。
降級成功率為100%,線上0事故,啟動介面數從110個降級到8個。
單個介面的峰值QPS比日常QPS降幅達88.7%左右,閘道器整體峰值QPS比日常QPS降幅達30%左右,均在2分鐘之內完成降級。
2022年雙十一期間Switchquery 保障了大促活動的穩定運營,具體情況如下:
2022 年雙十一期間,Switchquery 設定平臺服務業務模組數為38個,線上開關總數為392個,整體開關設定的觸達率為98.3%左右,服務開關設定秒級變更次數為61次,Switchquery設定介面峰值QPS環比去年下降58.8%有效的保障了大促活動的順利進行。
未來Switchquery設定平臺會為更多的業務模組提供設定服務,同時我們會賦能更多的App,支援一整套從設定使用者端元件控制到後臺CMS支援多App切換以及閘道器實時秒級觸達的一整套秒級觸達的高效能移動設定技術方案。同時支援更完備的功能體驗例如操作紀錄檔檢視能力,支援機型過濾能力,黑白名單通過接入嚐鮮平臺通過掃碼的方式動態新增等功能,打造一套具有秒級設定變更能力,助力業務快速變更的設定平臺。