解耦神器 —— 統跳協議和Rewrite引擎
題記:天貓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引擎與之配合。post
統跳協議
統跳協議設計之初就保留了很強的可擴展性,爲接下來更豐富的場景預留了能力。上文講到了統跳協議在界面跳轉中做用,而事實上界面跳轉僅僅是這套方案的一個典型場景,一個最佳實踐。界面跳轉在統跳協議的框架中被認爲成一個服務,而跳轉到哪個界面則是由服務內部實現決定的。編碼
註冊一個服務
服務經過聲明URL的方式註冊到統跳協議中,這個聲明發生在服務所屬模塊內部的一個配置文件中,而這個配置文件被註冊到統跳協議裏。也就是說,整個App中的每個模塊都要註冊一個配置文件到統跳協議,統跳協議在初始化過程當中會遍歷配置文件列表,逐一加載這些模塊配置,根據配置信息把一個一個的模塊服務註冊到協議中。url
統跳協議要求調用服務的URL必須是符合W3C URL標準的,服務註冊使用的URL只能包括host和path兩部分,其中host是必須的,path則可選。當統跳協議接收一個跳轉請求的URL後,先根據該URL的host和path兩部分做爲條件查找已註冊的服務,再初始化對應服務,把URL交給服務實例執行後續操做。spa
如何實現服務
統跳協議聲明瞭一個服務接口,這個接口中只有一個方法,服務必須由該接口實現而來。每個服務能夠經過實現接口中聲明的方法,使用參數中傳遞來的完整URL,參數列表和調用發起者指針,執行具體業務邏輯。.net
例如分享服務,以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
。
這份配置文件在分享模塊裏:
分享模塊的配置文件sharekit_bundle.plist
也註冊到統跳協議中。
這份配置文件在統跳協議模塊裏:
統跳協議如何處理界面跳轉
界面跳轉是統跳協議的初衷,也是統跳協議最重要的任務。所以在統跳協議服務註冊機制中,爲界面服務註冊作了更精細的定製開發。
上文提到跳轉服務是一個單一服務,而界面則成百上千,因此在界面註冊和服務註冊中出現了衝突。本着下降開發成本的原則,咱們又但願把同一個模塊中界面註冊和服務註冊放在一塊兒。因此在統跳協議中作了以下訂製:
-
默認註冊跳轉服務
跳轉服務是默認存在的,在統跳協議初始化過程當中這個服務就已經初始化了。
-
給界面註冊提供特殊的標記
上文中能夠看到在註冊分享服務的配置中
object
字段是服務的類名,若界面註冊也按照這個規則,那麼界面的類就會被認爲成一個服務,在調用過程當中必然會出現錯誤。所以咱們約定,界面註冊須要在類名前加#
標示。
如此一來,在統跳協議初始化過程當中,默認加載跳轉服務。當調用發生,解析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引擎查詢流程
- 取出規則列表中的首條規則
- 以模式串爲模板對原始URL作匹配,並獲得模式串定義的參數表
- 若匹配成功則繼續進行,不然進入下一條規則,從2開始進行下一輪匹配
- 查看該條規則是否包含
s
標記位,若包含,則使用原始串作一次個性域名的查詢 - 使用1的結果和重寫串對原始URL進行重寫操做,獲得目標URL
- 查看該條規則是否包含
l
標記位:- 若包含,則結束匹配,返回目標URL
- 若不包含,則把目標URL賦值給原始URL,並進入下一條規則,從2開始下一輪匹配
- 直到最後一條規則結束,返回目標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,讓全部人一塊兒挖掘這套方案的潛力。