搭建React Native熱更新平臺的詳細過程

一,什麼是熱更新

所謂熱更新,也叫做動態更新,一種類似 Web 的更新方式。相對於 App 的發版更新而言,熱更新能及時的修復線上存在的問題,大幅的提升業務迭代效率。我們都知道,互聯網業務講究兵貴神速,如果業務能夠通過熱更新來快速發版和迭代,這就相當於在產品和用戶之間搭建瞭一座能夠隨時通行的橋梁,代替瞭原來好幾周才能有一次迭代的問題。

那麼,熱更新和發版更新有什麼不同呢?為什麼熱更新比發版更新快這麼多呢?下面是這兩種更新方式的原理對比圖。

發版更新,指的是你把 React Native App,當作 Android App 和 iOS App,按照 Android、iOS 上架流程,通過各自的應用商店進行更新。通常每個 Native App 都會有一個自己的上架節奏,可能是兩周,也可能是 4 周。此外,從提交應用商店到審核通過,也需要等上幾天時間。甚至,即便新版本上架瞭,用戶更新到最新版本也需要一個過程,可能需要一個月的時間,新版本才能覆蓋到 90% 的用戶。

所以說,如果你的 React Native App 選擇發版更新,就會受到發版節奏、審核耗時和版本覆蓋耗時的影響,這些都會導致業務迭代速度變慢。

不過,React Native 的熱更新就可以繞過應用商店直接進行更新。隻要你的是集成熱更新功能的 React Native App,在應用商店上架過一次之後,後續你的業務都可以走你自己的熱更新流程,再也不用依賴應用商店發版。這樣你的業務就能隨時上線,隨時更新瞭。

二,熱更新方案

雖然官方沒有提供標準的熱更新方案,不過能夠支持常見熱更新方案有pushy、CodePush、Expo 和自研。

不過,由於國內網絡環境的原因,訪問國外的雲服務速度比較慢,所以我不太推薦直接使用 Code Push 和 Expo。Code push 是微軟 App Center 的服務之一,它底層用的是微軟自傢的 Azure 雲服務;Expo 使用的是亞馬遜的 AWS 和 Google Cloud 雲服務。

不過,大傢也可以基於 Code Push 或 Expo 自行搭建熱更新服務。如果你嫌自己搭建太麻煩,你也可以看看 React Native 中文網提供的 Pushy 熱更新方案。它使用的是國內的阿裡雲服務,且有比前兩者更省流量的差量更新方案,應該是國內目前市面上唯一可以直接使用的開源熱更新方案瞭。

三,熱更新原理

說到自研熱更新平臺,如果你沒有接觸過,可能會認為很難,但事實上並非如此。其實,自研熱更新平臺的難度主要體現在如何大規模應用上,還有到達率上。對於規模小的應用來說,搭建一個自研熱更新平臺本身並不是很難。總的來說,一個自研熱更新平臺,主要包括這兩個部分:

  • 打包服務:Bundle Server;
  • 靜態資源服務:Static Server。

所謂的打包服務,是將 React Native 項目中的所有 JavaScript 代碼打包成一個 Bundle 文件的服務。而所謂的靜態資源服務,是將 Bundle 文件分發給客戶端的服務;當客戶端拿到 Bundle 代碼後,執行 加載Bundle 文件就能渲染 React Native頁面瞭。

其實,這和我們本地的npm start的流程是一樣的。即我們在本地通過 npm start 啟動的 Metro 服務時,Metro 服務就同時具備瞭打包和靜態資源下發兩種功能,再配合框架默認的代碼加載功能就完成瞭熱更新。

也就是說,找臺服務器把 React Native 代碼放上去,然後運行 npm start 命令,然後在客戶端配置對應的 ip 和端口號,並把相關的調試開關給關瞭,接著訪問服務器就能把 React Native 頁面渲染出來。

當然,這個最簡單的熱更新流程是不能跑在線上的,畢竟 npm start 的本意是用於調試的,它的首次加載耗時太長,扛不住高並發,而且服務可用性也是問題。

四,CDN 熱更新方案

不過,使用npm start 的熱更新方案不太靠譜,那有沒有靠譜點的熱更新方案呢?有,那接下來我們看另一種比較簡單的熱更新方案:CDN 熱更新方案。

在 npm start 方案中,Metor Server 提供瞭代碼打包和 Bundle 下發的功能。而在 CDN 方案中,代碼打包是通過 react-native bundle 命令提供的,Bundle 下發的能力是通過 CDN 提供的。

下面是使用CDN熱更新方案的流程:

首先,通過 react-native bundle 命令把 JavaScript 代碼打包成一個 Bundle文件,命令如下:

npx react-native bundle --entry-file index.tsx --dev false --minify false --bundle-output ./build/index.bundle --assets-dest ./build

通過上述命令打包出來的是 index.bundle 文件,本質是一個可執行的 JavaScript 文件。如果你使用的是 Hermes,那麼還需要把 JavaScript 文件轉成相應的字節碼文件。Hermes 提供瞭把 JavaScript 文件轉成字節碼文件的方案,你可以先按照官方文檔 搭建 Hermes 環境,然後執行如下命令進行轉換:

hermes -emit-binary -out ./build/index.hbc ./build/index.bundle

接下來,就是將包上傳到 CDN 上,國內比較出名的有阿裡雲、騰訊雲,或者使用公司內部的提供的 OSS 和 CDN 服務也是可以的。

以阿裡雲為例,在第一次使用時,你可以先將包文件上傳到 OSS,然後開啟 CDN 加速。 完成文件上傳 CDN 這一步後,你將會得到一個 CDN 地址,我們可以使用該地址來訪問你的文件,例如:

https://static001.geekbang.org/resource/rn/index.bundle

拿到包地址後,熱更新最後一步是,在客戶端請求和加載該地址的 .bundle 文件或 .hbc 文件,這樣就完成熱更新的整個流程瞭。

如果需要執行熱更新,我們隻需要用新包把老包給覆蓋掉即可,當客戶端在加載的時候,會自動加載最新的包,從而完成熱更新。

五,純 CDN 方案的弊端

但是對於大流量業務,我並不推薦你用純 CDN 方案,為什麼呢?因為純 CDN 方案,會存在幾分鐘的更新延遲的問題。在小流量業務中,這種幾分鐘的更新延遲不是什麼問題,但是對於大流量業務來說,如果線上出現瞭一個重大 BUG,需要等幾分鐘才能完全回滾,那麼對用戶或者收入的影響會很大。

下圖演示瞭CDN 方案的時序圖:

上述時序圖中,涉及 React Native App、CDN 邊緣節點和 OSS 源站,以及 index.bundle 包文件的兩個版本。舊版本包是綠色的,新版包是藍色的。將舊版本更新為新版本的本質是:刪除 CDN 中緩存的舊版資源,當 CDN 中沒有緩存瞭,這時來自用戶的請求才不會命中 CDN 中的緩存,而是到 OSS 上拉取最新的資源,返回給 React Native App。

然而,我們都知道 CDN 指的並不是某一臺具體的機器,它指的是上千個分佈在全國各地的節點網絡。當我們使用 CDN 的刷新能力時,實際上是刪除上千個節點中的緩存。一位負責 CDN 的同學告訴我,要把這上千個節點的緩存都刪除幹凈的時間,最長可能需要個 5 分鐘吧,而且還不敢保證 5 分鐘的時效性。因此,在這 5 分鐘內,會存在三種情況:情況一,命中老版緩存;情況二,未命中緩存重新拉取新版資源;情況三,命中新版緩存;

六,線上方案

可以看到,不管是npm start 還是CDN方案,都存在一定的缺陷,那有什麼比較完整的方案嗎。其實仔細思考一下,隻需要在CDN 方案解決延遲問題即可達到線上運行的需求,那如果改進CDN 方案呢。

解決方案就是多發一次版本請求:既然上千個節點 CDN 更新有延遲,那麼就自己搭建一個版本服務,資源依舊上傳 CDN,然後用我們自己的版本服務來控制更新。此時,熱更新的時序圖變成如下:

增加一個版本服務後,可以看到整體流程發生瞭一些變化。純 CDN 方案的更新方式采用的是覆蓋更新,版本服務方案會提前告知更新,從而保持代碼的最新。那接下來,我們梳理下這種方案的完整流程。

1,上傳 Bundle 到源站,也就是 OSS
先在將本地打包好 Bundle 文件,並將文件命名為 “MD5”.bundle 上傳到 OSS 源站。此時理論上,隻要 Bundle 內容發生瞭變化,那麼生成 MD5 值就是不一樣的,用 MD5 作為文件的命名能保證文件的唯一性。

2,正式發版上線
當需要正式上線時,我們隻需要上傳前面生成的bundle包,然後將服務最新的線上 Bundle 的名字修改成最新的,這時版本服務會在內部通過 mysql 或 redis 把線上最新文件名給記錄下來。

3,React Native App 發起版本請求
由於隻有一個版本服務,不會存在 CDN 上千個節點在某一時刻不同步的問題,版本服務會直接把最新的 Bundle 名字告訴 React Native 應用。

4,React Native 發起 CDN 資源請求
資源請求會先詢問某個 CDN 的邊緣節點,如果該邊緣節點沒有緩存,則會去源站拉取;如果該邊緣節點有緩存,則直接返回。

七、整體方案梳理

事實上,無論是 CDN 熱更新方案,還是官方的版本方案,它們都不是完整的自研熱更新方案,隻是提供瞭一種解決基礎資源更新的問題。因為自研熱更新平臺的核心難點在於,它需要你對前端、Node.js、React Native,甚至 Java 都有所瞭解,特別是在熱更新服務這一塊要特別的擅長。涉及的難點包括:

  • 如何支持多人的並行打包;
  • 如何支持多人的並行測試;
  • 如何保障 CDN 資源更新成功;
  • 如何保障版本服務的高並發、高可用。

依據多年的經驗,我們設計瞭一張熱更新平臺的全貌圖。

可以看到,熱更新平臺整體上包括以下幾個部分:

  • 熱更新平臺後臺服務:一般兩臺機器就行,它提供打包、測試、上線、權限管理和相應的前端頁面展示等能力。
  • MySQL、Redis持久能力:MySQL 提供持久化存儲能力,Redis 用於緩存用來抗高並發,這裡推薦用成熟的相關服務就行,不要自行搭建。
  • 打包集群:獨立集群,至少兩臺機器,具體看情況而定,用於支持多人的並行打包。把它獨立出來的原因是,打包是非常消耗 CPU、內存資源的任務,和其他服務混在一起容易導致其他服務卡頓。
  • 版本服務集群:獨立集群,用於提供能支持高並發的版本服務。如果你有 node.js 抗高並發的經驗,可以使用 node.js 來做,或者直接找 Java 同學開發。

接下來,就是搭建熱更新平臺,我推薦的技術棧有:

  • NestJS :它是一個 Node.js 框架 ,它能提供高效、可靠和可擴展的後端服務。
  • Bull:它一個任務隊列庫,它能幫你解決熱更新平臺後臺集群如何向打包集群發佈打包任務的難題,讓你的平臺可以支持多人並行打包。

八,總結

要實現 React Native 熱更新功能,有四種思路 Code Push、Pushy、Expo 和自研。如果你選擇自研 React Native 熱更新功能,這就需要 React Native 熱更新平臺和 Native 熱更新模塊的緊密配合。

自研 React Native 熱更新功能,並不一定需要搭建一個熱更新平臺,你也可以采用純 CDN 方案,比如你可以利用阿裡雲提供的靜態資源部署能力和對應的 CDN 服務。如果決心要完全自研一個熱更新平臺,那麼你最好找一個對後端技術比較瞭解同學一起來設計,我也為你提供瞭一個熱更新平臺的全貌圖,你可以用它來幫你進行整體設計。

到此這篇關於搭建React Native熱更新平臺的文章就介紹到這瞭,更多相關React Native熱更新內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: