解耦神器 —— 統跳協議和Rewrite引擎

puviolet 9年前發布 | 32K 次閱讀 Rewrite 移動開發 IOS Andoird

記:天貓App長大了,已經長成了流量以千萬計規模的App,當下至少有10個團隊在直接維護天貓App。在App長大,團隊擴充的過程中解耦是一個永恒的話題,而界面解耦又是App架構的重中之重。

統跳協議是天貓App統一跳轉協議,主要負責天貓App界面之間的串聯,也就是界面跳轉服務。Rewrite引擎是與之配合的一套URL重寫引擎,可以通過配置實現重寫規則動態化。

歷史上的今天

統跳協議的前身是一套叫做internal的協議,internal要重點解決的問題是在WebView和推送通知中如何跳轉到指定的界面,進一步在任何動態場景下如何跳轉到指定界面。在這樣的思路下,internal中定義了多種協議格式,如:
tmall://tmallclient/?{"action":""}
internal:url=
link:url=
tmall://mobile.tmall.com/page/
幾乎每一種場景都有一種格式的協議與之對應。在具體操作過程中這些協議都以URL表現出來。不難看出,這套協議最大的問題在于協議格式異構化嚴重,且不符合W3C的URL標準。隨著App規模的擴大,場景日趨復雜,界面越來越多,這套協議的弊端也日益顯露。

而在天貓App開始從百萬級沖擊千萬級的時候,我們認識到一套格式統一,符合標準,規則簡潔的協議非常必要。這套協議的任務也絕不是解決固定場景跳轉,而是完全托管整個App的跳轉工作,從而實現全App界面解耦和跳轉動態化。因此,我們重新設計了界面協議,形成了當下這套規范——統跳協議。配合統跳協議,為了解決更多細節問題和跨平臺問題,我們還設計了Rewrite引擎與之配合。

統跳協議

統跳協議設計之初就保留了很強的可擴展性,為接下來更豐富的場景預留了能力。上文講到了統跳協議在界面跳轉中作用,而事實上界面跳轉僅僅是這套方案的一個典型場景,一個最佳實踐。界面跳轉在統跳協議的框架中被認為成一個服務,而跳轉到哪一個界面則是由服務內部實現決定的。

解耦神器 —— 統跳協議和Rewrite引擎

注冊一個服務

服務通過聲明URL的方式注冊到統跳協議中,這個聲明發生在服務所屬模塊內部的一個配置文件中,而這個配置文件被注冊到統跳協議里。也就是說,整個App中的每一個模塊都要注冊一個配置文件到統跳協議,統跳協議在初始化過程中會遍歷配置文件列表,逐一加載這些模塊配置,根據配置信息把一個一個的模塊服務注冊到協議中。

解耦神器 —— 統跳協議和Rewrite引擎

統跳協議要求調用服務的URL必須是符合W3C URL標準的,服務注冊使用的URL只能包括host和path兩部分,其中host是必須的,path則可選。當統跳協議接收一個跳轉請求的URL后,先根據該URL的host和path兩部分作為條件查找已注冊的服務,再初始化對應服務,把URL交給服務實例執行后續操作。

如何實現服務

統跳協議聲明了一個服務接口,這個接口中只有一個方法,服務必須由該接口實現而來。每一個服務可以通過實現接口中聲明的方法,使用參數中傳遞來的完整URL,參數列表和調用發起者指針,執行具體業務邏輯。

例如分享服務,以iOS為例:實現了TMShareUrlHandler服務。

@interface TMShareUrlHandler : NSObject<AliAppURLHandler>
@end
@implementation TMShareUrlHandler
#pragma mark - URL調用分享組件
- (id)handleUrl:(NSURL *)url withTarget:(id)target withParams:(id)params
{
// 省略代碼詳情
    return nil;
}
@end

在分享模塊的配置文件中聲明該服務的URL為sharekit.tm/doShare

這份配置文件在分享模塊里:

解耦神器 —— 統跳協議和Rewrite引擎

分享模塊的配置文件sharekit_bundle.plist也注冊到統跳協議中。

這份配置文件在統跳協議模塊里:

解耦神器 —— 統跳協議和Rewrite引擎

統跳協議如何處理界面跳轉

界面跳轉是統跳協議的初衷,也是統跳協議最重要的任務。因此在統跳協議服務注冊機制中,為界面服務注冊做了更精細的定制開發。

上文提到跳轉服務是一個單一服務,而界面則成百上千,所以在界面注冊和服務注冊中出現了沖突。本著降低開發成本的原則,我們又希望把同一個模塊中界面注冊和服務注冊放在一起。所以在統跳協議中做了如下訂制:

  • 默認注冊跳轉服務

    跳轉服務是默認存在的,在統跳協議初始化過程中這個服務就已經初始化了。

  • 給界面注冊提供特殊的標記

    上文中可以看到在注冊分享服務的配置中object字段是服務的類名,若界面注冊也按照這個規則,那么界面的類就會被認為成一個服務,在調用過程中必然會出現錯誤。因此我們約定,界面注冊需要在類名前加#標示。

    解耦神器 —— 統跳協議和Rewrite引擎

如此一來,在統跳協議初始化過程中,默認加載跳轉服務。當調用發生,解析URL查找到的對應對象帶有#,則認為這是一個界面,則初始化這個對象,但不對其調用處理URL的方法,而是托管給已注冊的跳轉服務。跳轉服務則根據URL和初始化的界面對象執行跳轉服務。

Rewrite

Rewrite引擎的思路來源于Web容器中的Rewrite機制,主要解決天貓App中URL平臺展現一致性的問題。

天貓App中所有界面都是通過URL來標示的,然而標示Native界面的URL全部都建立在Native規范下,無法和其他平臺對應起來,而Rewrite引擎通過重寫URL來實現平臺一致性。

例如:商品詳情頁面在PC Web的URL是https://detail.tmall.com/item.htm,在Mobile Web則是https://detail.m.tmall.com/item.htm,在Native聲明的是tmall://page.tm/itemDetail。三者各不相同。PC Web和Mobile Web可以通過判斷瀏覽器的UA識別環境,從而通過跳轉實現一致性,也就是說在手機瀏覽器訪問PC Web的URL,會通過一次302轉到Mobile Web的URL。而Native App的環境具有一定的特殊性,Native界面則無法通過類似302這樣的跳轉來實現無感知切換,而Rewrite引擎就是來解決這個問題的。首先,無論是Native還是Web,在天貓App中他們兩兩之間的跳轉都被統跳協議托管,而統跳協議在執行跳轉操作之前會把原始URL放入Rewrite引擎中做一次Rewrite操作。這樣一來,Rewrite引擎就根據配置規則,把原始URL轉換成適用于天貓App的目標URL,實現了URL表現平臺一致性。

原理

Rewrite引擎的原理非常簡單,模擬Web容器(Apache/Nginx等)的Rewrite配置,根據配置把傳入的原始URL進行重寫,返回重寫后的目標URL,交給統跳協議處理。

配置是通過正則表達式描述的Rewrite規則列表,這份列表通過貓客的配置中心實現動態更新。

Rewrite規則

  • 每條Rewrite規則中有三個字段:模式串,轉換串和標記位
    • 模式串:即正則表達式,用于匹配原始URL
    • 轉換串:即需要被轉換成目標URL的描述
    • 標記位:以西文逗號分隔的標記位,包括表示匹配則終止的l,需要進行店鋪域名查詢的s
  • Rewrite規則按行整理,并自上而下按順序逐行匹配

轉換模板中的保留字

  • 變量

    變量由變量標示符和變量名組合而成,如:$0,$#1,query,$#fragment等。變量被用在轉換串中描述轉換后的目標URL中的值。

    • 變量標示符:$,$$和$#
      • $:原變量的值
      • $$:對原變量做URL Encode
      • $#:自動識別編碼,對原變量做URL Decode
      • $$$:自動識別編碼,對原變量做URL Decode,再以UTF8做URL Encode
    • 變量名:數字(從0開始),枚舉(scheme,host,port,path,query,fragment,shopid)
      • 數字:0 - 表示整個URL,1~n - 表示正則中使用圓括號取出的參數
      • 枚舉:scheme,host,port,path,query,fragment表示標準URL中的相應部分;shopid表示對個性店鋪域名查詢后得到的shopid
  • 標記位

    即上述規則中的標記位

Rewrite引擎查詢流程

  1. 取出規則列表中的首條規則
  2. 以模式串為模板對原始URL做匹配,并得到模式串定義的參數表
  3. 若匹配成功則繼續進行,否則進入下一條規則,從2開始進行下一輪匹配
  4. 查看該條規則是否包含s標記位,若包含,則使用原始串做一次個性域名的查詢
  5. 使用1的結果和重寫串對原始URL進行重寫操作,得到目標URL
  6. 查看該條規則是否包含l標記位:
    • 若包含,則結束匹配,返回目標URL
    • 若不包含,則把目標URL賦值給原始URL,并進入下一條規則,從2開始下一輪匹配
  7. 直到最后一條規則結束,返回目標URL

舉例

上述提到過商品詳情頁的例子,在Rewrite配置中就體現為:

模式串 轉換串 標記位
^(?:https?:)?\/\/detail(?:.m)?.tmall.com\/?item.htm\?(.*) tmall://page.tm/itemDetail?$1 l

在這條規則的保護下,PC Web和Mobile Web下的商品詳情URL在天貓App中都會被攔截到Native商品詳情頁面,可以帶來最好的用戶體驗。也就是說,在日常的運營工作中,不需要關注一個商品在某個平臺內部需要以什么樣的URL來投放,只需要投放一個主要的URL格式。這個URL在天貓App內部會被Rewrite引擎重寫為Native界面聲明的URL,進行展示。

統跳和Rewrite在雙11中的表現

統跳協議和Rewrite引擎在剛剛過去的雙11期間,在全鏈路界面降級方案和會場上下線中發揮了重要作用。

全鏈路界面降級

上文中提到了天貓App中全部界面都聲明了自己的一個tmall://協議的Native URL,但在業務邏輯上使用的不是這個Native URL,而是和Web保持一直的http://協議的URL,在統跳協議中會調用Rewrite引擎對這個http:// URL進行重寫后再做展現。

為了保證整個天貓App的可用性,我們在配置列表中預先定義了一系列Rewrite規則,用于攔截這些URL。一旦發現Native邏輯出現異常,將快速上線預定義的規則,從而在Rewrite引擎把http:// URL重寫成Native URL之前攔截,直接返回http:// URL,實現Native界面到Mobile Web界面的降級。

Native會場上下線

雙11會場是雙11活動期間曝光率最高的頁面,也是對體驗要求最高的界面。因此,我們在天貓App雙11版本中對重要會場做了Native化,以提升用戶體驗。而Native會場展現依賴會場數據,且開啟時間嚴格控制在雙11當日的24小時內。

在這樣的要求下,我們配置了http://協議的會場URL到Native會場URL的Rewrite規則,并在雙11開始時上線,結束時下線,實現了雙11當日天貓App的會場Native化。

結語

統跳協議在設計過程中預留了很好的擴展能力,所以在界面解耦之外還承擔了更多的服務調用功能。Rewrite引擎為實現平臺一致性而設計,而在實際應用過程中又挖掘出更多的場景和功能。

在統跳協議和Rewrite引擎接下來的發展過程中,將更注重Android和iOS雙平臺的高度一致性,并嘗試開放更多API,讓所有人一起挖掘這套方案的潛力。

來源:http://pingguohe.net/2015/11/24/Navigator-and-Rewrite.html

 本文由用戶 puviolet 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!