多容器動態化方案在遊戲SDK中的實踐

陶然陶然發表於2023-11-15

   一、前言

  從產品運營角度來說,功能的使用者觸達是實現使用者價值轉化的最基本前提。所以如何快速將一個新的功能觸達到使用者,同時減少觸達過程中對運營推廣、使用者帶來額外的成本就成了一個必須被重視的課題。對於APP而言,以一個相對固定的節奏進行版本迭代,在一個迭代週期內,新版本基本可以達到一個相對較高的覆蓋率。但對SDK而言,因為其並非直接面向使用者,更像是一個ToB類產品,快速的版本迭代顯然不是一個有效的途徑,遊戲SDK也不例外。

   二、背景與目標

  2.1 “三座大山”

  週期長,遊戲SDK的功能觸達使用者需要一個月左右的時間。一個功能從需求到使用者觸達要經歷SDK開發->SDK測試→遊戲接入遊戲測試→版本釋出一個漫長的週期。另外,遊戲的強更與非強更也直接影響到最終的觸達時效。同時,為積極響應國家對個人資訊保護的政策持續升級,長迭代週期的業務模式也成了必須“翻越”的首座“高峰”。

  成本高,目前接入嗶哩嗶哩遊戲SDK的遊戲成百上千款,釋出一個版本,如果要同時覆蓋全部遊戲,中間對於運營來說是一個巨大的溝通和推廣成本。就算全部通知溝通到位,眾多的遊戲在接入過程中,因為引擎差異、遊戲研發人員對Native開發知識的掌握程度不同、相容性問題等,導致在技術支援上也要付出相當大的人力成本。

  灰度能力不完善,為了保證一個新版本的穩定性,我們一般會透過一兩款遊戲接入上線進行灰度驗證。一方面溝通遊戲接入需要花費一定的成本。另一方面,因為不同遊戲之間因為引擎、玩法、接入第三方庫等的差異,在整體灰度效果上,覆蓋的場景比較侷限。無法以一定取樣率進行全平臺的灰度。

  2.2 目標

  實現功能快速觸達;

  減少觸達過程中對運營、遊戲研發、技術支援以及使用者帶來額外的成本;

  具備全平臺灰度的能力,進一步提升線上業務的穩健性。

  如何翻越“三座大山”,實現業務價值轉化呢?是否能將APP中慣用的熱更新能力遷移到SDK場景中來使用呢?

   三、熱更新在遊戲SDK場景下的思考

  3.1 遊戲SDK業務模式簡介

  因為遊戲SDK與移動開發小夥伴們平時工作中接入的一些第三方SDK還是存在一定的差異。所以在分析具體業務與選型前,我先介紹一下游戲SDK的業務模式。

  首先,應用場景不同。遊戲SDK並非應用於原生APP,而是遊戲。而不同遊戲因開發引擎(Unity、Cocos、UE4等)的差異,在呼叫SDK相關API時需要封裝一層Bridge。同時因為遊戲開發者對Native開發的技術數量程度參差不齊,在SDK的接入過程中,需要花費較大的人力成本去提供技術支援。

  其次,業務流程不同。遊戲SDK主要是為遊戲提供統一的登入、支付等體系化功能,在流程上存在一些線性的固定邏輯。比如以登入為例,原生APP的登入基本是使用者點選登入→提交賬號資訊→服務端驗證→返回登入結果→登入成功/失敗。

  但遊戲的登入在這個過程中會增加很多強校驗子流程,具體為:使用者點選登入→提交賬號資訊→服務端驗證→返回登入結果三無賬號檢測→ 防沉迷檢測 → 遊客升級 → 實名認證檢測 → 防沉迷提醒→ 登入成功/失敗。這些額外的固定流程在每次登入都必須強校驗,且流程不可阻塞。任何一個環節檢測不透過都會導致登入失敗。

  最後,介面互動不同。因為遊戲SDK的所有功能介面,都是展示在遊戲場景之上,所以所設計的頁面都是以彈窗的形式進行展示。

  下面是一個登入入口的截圖,希望可以幫助您對遊戲SDK業務場景有一個大概的瞭解。  

圖3.1 SDK登入入口截圖

  3.2 常用的熱更新方案

  目前行業內比較常用具備熱更新能力的方案大致可以分為四類:動態化佈局、虛擬執行環境、業務外掛化和Web容器增強。  

圖3.2 不同方案的代表性框架

  3.3 業務場景分析

  具備熱更新能力的方案百花齊放,我們該如何選擇呢?魯迅說,沒有最好的技術方案,只有最契合業務的方案。  

  所以我們首先要做的就是對業務場景進行審視。我們將業務大概劃分為三類:

  核心業務 —— 對於遊戲SDK而言,核心業務就是指登入和支付。這部分業務是基礎也是重點,對效能和穩定性有著極高的要求。

  附加業務—— 比如修改密碼、賬號升級等。生命週期長,需要長期維護;同時需求整體穩定、沉澱性高;業務邏輯結構相似,提煉性強;使用者觸發頻率相對較低,對效能有一定的敏感性,但不追求極致。

  展示型業務—— 比如活動、公告等。內容調整頻繁,整體邏輯性不強以展示為主。有一定的可沉澱性,對效能極致性不敏感,更多的是追求對需求和內容變化的快速響應。

  3.3 技術選型

  明確了不同業務場景的特徵與需求重心,回到問題本身,我們如何選擇合適的技術方案呢?

  佈局動態化方案透過佈局引擎,對動態下發的佈局模板進行解析,底層透過Native元件進行承載,保證了一定的動態化能力,同時充分發揮Native極致效能。但一個適配雙端的頁面模板的開發需要一定的配套設施開發,方案落地有一定的成本。對於需求相對穩定、業務邏輯可抽象性強的附加業務型別匹配度較高。

  web容器增強方案透過提供一個高效能WebView容器,同時沉澱大量通用性bridge來承載html頁面,實現和移動開發框架的有效結合,開發成本低,整體效能與Native有一定差距。對內容變更頻繁但對效能要求不追求極致的展示型業務比較符合。

  業務外掛化是提供宿主APP,透過遠端下載代表各個功能模組的外掛,實現資源和程式碼的動態載入。整體開發模式與現有Native迭代沒有太大差異,效能和穩定性最高,但僅支援Android平臺。對登入、支付等核心業務外掛化後業務價值較大。  

圖3.3 不同方案優缺點與適用場景對比

  不同技術方案各有優劣,適用場景也有所差異,很難透過一種方案完全匹配業務需求。所以我們索性就採用新的方式——多種方案相結合,將SDK容器化,根據不同的需求型別和業務場景選擇優秀的容器來承載,打造一個集多種容器於一身的複合型熱更方案。

   四、方案設計

  4.1 整體架構

  本方案整體可以分為服務端、SDK、釋出運維繫統和質效保障四大部分。其中:

  SDK:採用分層化設計,自上而下將SDK分為應用層、協議層、容器層、框架層、元件層、基建層和系統層。應用層主要是為遊戲研發提供相應的API能力,如登入、支付等。協議層是建立統一的路由協議,對業務模組進行模組化拆分和路最佳化改造後,方便進行統一的排程和互動;容器層是本案的核心,即建立多種型別的容器承載不同型別和場景的業務需求;框架層主要是對容器層能力的支撐和管理,如外掛管理、引擎管理、模板管理等。元件層是將不同的業務模組進行元件化拆分,化整為零實現模組解耦;基建層主要承載網路、圖片、APM等通用能力;系統層主要是指Android和iOS雙端的系統服務;

  服務端:主要負責根據不同使用者的資訊狀態(裝置資訊、遊戲資訊、使用者資訊等)進行篩選匹配,完成產物的動態下發。

  釋出運維繫統:負責從釋出、灰度配置、相關效能指標收集、全流程效能監控等工作。

  質效保障:建立完善的設計和編碼規範,結合自動化檢測能力,確保開發質量可靠;沉澱統一的工程手腳架、模板和元件提升開發效率。

  透過對SDK的分層化設計改造,以多容器化模式承載不同的業務場景;建立統一的業務路由協議,對業務模組進行路最佳化改造,實現多容器之間的排程與互動;通用元件和基架做下沉處理,實現多容器多業務服務用;最後建立完善的釋出運維繫統,保障動態化產物從構建→ 配置→ 白名單內測→ 灰度→釋出等全流程的跟蹤監控,結合完整的流程規範與能效工具,保障整個系統的高效性與穩定性。  

圖4.1 方案架構圖

  4.2 子系統設計

  4.2.1 動態化路由協議

  4.2.1.1 整體思路

  動態化路由總體設計思路是透過對業務元件進行元件化拆分,指定路由入口。然後根據業務流程,透過服務端動態下發相應的業務路由配置表。

  當新增業務模組或現有業務模組有調整時,直接動態修改路由配置,即可完成新業務的排程與新流程的調整。有兩個核心介面,透過RouteMapProcessService定義完整業務流程的路由表。透過不用業務模組實現RouteProcessService並完成相應的狀態和邏輯處理後,實現流程的流轉。  

圖4.2 RouteMapProcessService介面  

圖4.3 RouteProcessService介面

  4.2.1.2 路由協議規則

  路由協議整體採用Scheme URL的格式,主要分為Scheme、group、path、固定引數和自定義引數幾個部分,其中:

  Scheme:自定義協議,主要是方便內部和遊戲研發側對路由協議的呼叫

  Group和Path:模組名和路徑名,按需載入路由對映表

  固定引數:一些與路由流程排程相關的必要引數,如:allow_failure,標識該流程是否為強校驗流程。

  自定義引數:不同業務場景所需特殊欄位,如:通用web容器路由需要配置url引數  

圖4.4 路由協議結構示例

  4.2.2 多容器

  4.2.2.1 外掛化容器

  外掛化容器我們採用的是VirtualAPK方案(此處手動鳴謝VirtualAPK開源專案組)。

  其基本原理主要有三部分:

  合併素組和外掛的ClassLoader;

  合併外掛和宿主的資源;

  去除外掛包對宿主的引用。

  整體功能比較完備,支援Android四大元件,四大元件實現原理:

  Activity:採用宿主manifest中的佔坑方式繞過系統校驗,然後再載入真正的Activity;

  Service:動態代理AMS,攔截Service相關請求,將其中轉給一個虛擬空間(Matrix)去處理,Matrix會結果系統的所有操作;

  BroadcastReceiver:將外掛中靜態註冊的廣播重新註冊一遍;

  ContentProvider:動態代理IContentProvider,攔截Provider的相關請求,將其中轉給Matrix,由Matrix接管。

  具備較好的相容性和較低的侵入性。整體框架如下圖所示(圖片來源於網路):  

圖4.5 VirtualAPK架構圖

  因為VirtualAPI從2017年開始已經停止維護,所以我們針對整個框架做了大量的升級和相容工作,主要包括:

  升級對高系統版本的支援(目前已完美適配android 11)

  升級對對androidx的支援

  升級對gradle plugin高版本的相容

  最佳化外掛資源載入(包括res、layout、theme、so等)

  最佳化對四大元件中Service、Broadcast、ContentProvider的支援能力

  最佳化對類載入器、外掛包安裝等方面能力

  還有一些機型適配等方面的相容性問題

  4.2.2.2 動態化模板容器

  動態化模板容器我們採用的是基於FlexboxLayout渲染引擎(谷歌開源框架)的Native + DSL方案。動態化能力建設主要可以分為四個部分:DSL定義、動態檢視管理、檢視構造和資料繫結。

  DSL定義:基於Flexbox佈局系統進行檢視佈局,採用JSON語言描述檢視結構,使用屬性區分檢視控制元件型別。其中常用的屬性大概有佈局屬性、檢視屬性、資料屬性、互動屬性等。

  動態檢視管理:基於DSL語言進行頁面樣式編寫→ 進入相應的動態化頁面時請求服務端樣式介面→ 服務端根據客戶端相關資訊(裝置資訊、遊戲資訊、使用者資訊等)進行策略匹配後下發模板→ 客戶端接受到模板資訊後進行校驗、快取、解析和渲染。

  檢視構建:客戶端為每一個UI元件建立對應的解析器,當客戶端獲取到動態模板後,由解析器覺得采用什麼原生控制元件進行承載。因為業務場景的特殊性,目前在元件支援上主要以ImageView、TextView、Button、EditText等基礎元件為主。

  資料繫結:對於一些介面資料,直接透過NodeJs服務在下發的頁面模板中進行相應屬性的配置。對於本地資源則在編寫模板時進行指定。解析器完成對控制元件相應引數的設定。

  整體架構如下圖所示:  

圖4.6 動態化模板方案架構圖

  4.2.2.3 通用web容器

  對於通用web容器相信很多app和場景下都會用到,就容器而言整體實現方式也大同小異。這裡主要介紹以下我們SDK容器的一些差異點。

  一方面因為遊戲SDK本身場景的特殊性,頁面基本以彈窗形式展示。所以我們對於整個容器的樣式做了大量的個性化配置能力。

  包括主題(原生主題、遊戲主題)、樣式(全屏、橫屏、豎屏、特定大小、特定比例、自定義等樣式)最大限度去保證不同遊戲,不同需求在配置時的需要。另一方面就是JsBridge能力,我們梳理了目前業務場景中可能用到的一些bridge,包括(登入態、網路、埋點、裝置資訊、引數傳遞、頁面跳轉、雙向互動等等)。

  4.2.3 釋出系統

  對於釋出系統,首先採用基於jenkins實現外掛、動態化模板等動態化產物的自動、持續構建。然後,設計了一個從灰度配置、過濾配置和排除配置三個步驟的靈活釋出方案。

  灰度配置:主要分為基於使用者進行灰度分桶和基於裝置進行灰度分桶兩個維度。支援千分之一級別的灰度粒度。主要針對新功能熱更時按0.1% -> 100%施行逐漸放量釋出,最大限度降低可能發生的異常事件造成線上事故的損失。

  過濾配置:主要從SDK版本、遊戲、渠道、ROM版本、網路環境、裝置品牌、裝置型號等維度進行靈活的配置,同時支援使用者白名單配置。主要針對一些定製化需求和定向化最佳化需求。

  排除配置:排除配置從維度上與過濾配置相似,也是從SDK版本、遊戲、渠道、ROM版本、網路環境、裝置品牌、裝置型號等維度進行配置,同時支援配置多組。但作用有所差異,主要用於線上偶發性缺陷和一些相容適配問題的降級。透過靈活的配置方案,結合對熱更新產物從下發、下載、應用等全生命週期的資料打點,對異常情況進行實時監控,發現問題快速進行配置相容、回滾、修復,確保線上業務穩定執行。

  透過灰度配置,實現灰度逐漸放量能力;透過過濾配置,可多維度靈活配置灰度範圍;透過排除配置,規避一些可能發生的線上相容性問題。三管齊下建立一個相對完善的灰度釋出系統,實現全平臺灰度能力。

   五、實踐

  5.1 不同方案在業務側的嘗試

  外掛化方案目前主要應用與登入及其子流程(實名認證、防沉迷、未成年人保護等)、懸浮球(截圖、自動錄屏釋出、一鍵直播等)。

  選擇這兩個場景主要處於兩點考慮:一方面外掛化本質上採用的是Native開發的方式,在整體效能和穩定性上可以得到有效保障,同時對端能力的依賴上沒有任何瓶頸。另一方面從業務場景上來說,這兩個場景可能發生功能擴充的可能性較高,作為外掛化改造,後續的版本迭代上適用性更強。

  動態化模板方案目前主要應用於賬號系統(修改密碼、找回密碼等)二級頁面。這些頁面與Native之間有較多的邏輯互動,但互動的方式可以被很好的抽象封裝成通用能力。具體場景如下圖所示:  

圖5.1 動態化模板配置頁面一  

圖5.2 動態化模板配置頁面二

  通用web容器,目前主要應用於雙協議(使用者協議、隱私協議)和一些遊戲系統公告等場景。這些場景具有重展示輕互動的特點。

  內容上有較高的動態化需求,但沒有太複雜的邏輯,效能上也沒有過高要求。具體場景如下圖所示:  

圖5.3 Web容器應用場景 —— 隱私協議  

圖5.4 Web容器應用場景 —— 公告

  5.2 上線效果

  5.2.1 產物下發及應用情況

  以外掛為例,圖5.1 為"get_config_success",即成功獲取外掛配置資訊資料;圖5.2 為"download_success",即成功對外掛包進行下載的資料;圖5.3為"install_success",即成功完成對外掛包的安裝情況資料。

  其中配置下發->成功下載轉化率為97.24%(其中絕大部分為網路異常,此部分有進一步最佳化空間)

  下載成功-> 安裝成功轉化率為99.99%,整體框架穩定性和相容性得到有效驗證。  

圖5.5 成功獲取外掛包配置資訊資料走勢圖  

圖5.6 成功完成外掛包下載資料走勢圖  

圖5.7 成功完成外掛包安裝資料走勢圖

  5.2.2 緊急回滾

  同樣以外掛為例,2021-11-02 11:00 點開始對外掛進行回滾。回滾後下載和安裝資料斷崖式歸零,同時成功解除安裝的日誌大量產生。具體資料情況如下圖:  

圖5.8 外掛回滾後成功下載資料走勢圖  

圖5.9 外掛回滾後成功安裝資料走勢圖  

圖5.10 外掛回滾後本地解除安裝成功資料走勢圖

   六、 避坑指南

  1)R8相容:外掛框架在gradle版本升級後,低版本R8生成的mapping.txt檔案存在大量重複的對映記錄,這樣的mapping檔案直接進行applaymapping時解析過程會因報錯而終止外掛構建,無法構建成功。直接使用高版本R8對低版本進行覆蓋構建結果中,外掛功能存在一些不可預見的異常,需要在構建時,先對mapping.txt進行指定,然後再構建過程中applymapping指定的mapping檔案,確保構建和功能正常。  

圖6.1 mapping內容示例  

圖6.2 動態處理mapping檔案指令碼

  2)混淆mapping處理:遊戲SDK是以aar形象進行交付,本身在交付前進行了一次混淆。同時不同的遊戲完成SDK接入後,會再一次進行混淆,不同遊戲的混淆結果不同,且無法將混淆結果回撥給SDK,比如交付的A.class,遊戲打包後變成了B.class或者C.class。

  如果要求遊戲接入方不做混淆處理,對遊戲和SDK都存在一定的安全隱患。如果SDK對可能被外掛呼叫的API做特殊處理不進行混淆,則需要大量的人工介入操作,且無法完全預計後續業務迭代過程中可能需要用到的API。可以採用在構建混淆結束後,讀取混淆結果,輸出混淆結果為proguard-rules規則,確保遊戲二次混淆後,遊戲SDK不會被再次混淆。  

圖6.3 輸出混淆規則示例

   七、未來發展與規劃

  1)建立完善的監控預警機制,實現自動通知、降級、回滾的分級預警的干預措施。

  2)基於大資料和使用者標籤,實現千人千面的個性化動態下發,提供精細化運營能力。

   八、總結

  本案為我們在遊戲SDK側的一點嘗試,以動態化路由協議為媒介,多種容器並行的熱更新方案。很多地方有最佳化的空間,但從效果上也基本達到了預期。感謝您能閱讀到最後,希望在某些設計思路上能讓您有所收穫。如有不足之處,歡迎在下方進行評論指教。

  寫在最後,“路漫漫其修遠兮”,我們一直走在前進的路上,加油!

來自 “ 嗶哩嗶哩技術 ”, 原文作者:羅星;原文連結:https://server.it168.com/a2023/1113/6829/000006829334.shtml,如有侵權,請聯絡管理員刪除。

相關文章