Scheme & Rewrite

原文地址

蘋果核 - 解耦神器 —— 統(tǒng)跳協(xié)議和Rewrite引擎

題記:天貓App長大了,已經(jīng)長成了流量以千萬計規(guī)模的App,當下至少有10個團隊在直接維護天貓App翠肘。在App長大叁丧,團隊擴充的過程中解耦是一個永恒的話題,而界面解耦又是App架構(gòu)的重中之重刃滓。

統(tǒng)跳協(xié)議是天貓App統(tǒng)一跳轉(zhuǎn)協(xié)議,主要負責天貓App界面之間的串聯(lián)耸弄,也就是界面跳轉(zhuǎn)服務(wù)咧虎。Rewrite引擎是與之配合的一套URL重寫引擎,可以通過配置實現(xiàn)重寫規(guī)則動態(tài)化计呈。

歷史上的今天

統(tǒng)跳協(xié)議的前身是一套叫做internal的協(xié)議砰诵,internal要重點解決的問題是在WebView和推送通知中如何跳轉(zhuǎn)到指定的界面,進一步在任何動態(tài)場景下如何跳轉(zhuǎn)到指定界面捌显。在這樣的思路下茁彭,internal中定義了多種協(xié)議格式,如:

tmall://tmallclient/?{"action":""}
internal:url=
link:url=
tmall://mobile.tmall.com/page/

幾乎每一種場景都有一種格式的協(xié)議與之對應(yīng)扶歪。在具體操作過程中這些協(xié)議都以URL表現(xiàn)出來理肺。不難看出,這套協(xié)議最大的問題在于協(xié)議格式異構(gòu)化嚴重善镰,且不符合W3C的URL標準妹萨。隨著App規(guī)模的擴大,場景日趨復雜炫欺,界面越來越多乎完,這套協(xié)議的弊端也日益顯露。

而在天貓App開始從百萬級沖擊千萬級的時候品洛,我們認識到一套格式統(tǒng)一树姨,符合標準,規(guī)則簡潔的協(xié)議非常必要桥状。這套協(xié)議的任務(wù)也絕不是解決固定場景跳轉(zhuǎn)帽揪,而是完全托管整個App的跳轉(zhuǎn)工作,從而實現(xiàn)全App界面解耦和跳轉(zhuǎn)動態(tài)化辅斟。因此台丛,我們重新設(shè)計了界面協(xié)議,形成了當下這套規(guī)范——統(tǒng)跳協(xié)議。配合統(tǒng)跳協(xié)議挽霉,為了解決更多細節(jié)問題和跨平臺問題,我們還設(shè)計了Rewrite引擎與之配合变汪。

統(tǒng)跳協(xié)議

統(tǒng)跳協(xié)議設(shè)計之初就保留了很強的可擴展性侠坎,為接下來更豐富的場景預留了能力。上文講到了統(tǒng)跳協(xié)議在界面跳轉(zhuǎn)中作用裙盾,而事實上界面跳轉(zhuǎn)僅僅是這套方案的一個典型場景实胸,一個最佳實踐。界面跳轉(zhuǎn)在統(tǒng)跳協(xié)議的框架中被認為成一個服務(wù)番官,而跳轉(zhuǎn)到哪一個界面則是由服務(wù)內(nèi)部實現(xiàn)決定的庐完。

統(tǒng)跳協(xié)議.png

注冊一個服務(wù)

服務(wù)通過聲明URL的方式注冊到統(tǒng)跳協(xié)議中,這個聲明發(fā)生在服務(wù)所屬模塊內(nèi)部的一個配置文件中徘熔,而這個配置文件被注冊到統(tǒng)跳協(xié)議里门躯。也就是說,整個App中的每一個模塊都要注冊一個配置文件到統(tǒng)跳協(xié)議酷师,統(tǒng)跳協(xié)議在初始化過程中會遍歷配置文件列表讶凉,逐一加載這些模塊配置,根據(jù)配置信息把一個一個的模塊服務(wù)注冊到協(xié)議中山孔。

注冊服務(wù).png

統(tǒng)跳協(xié)議要求調(diào)用服務(wù)的URL必須是符合W3C URL標準的懂讯,服務(wù)注冊使用的URL只能包括host和path兩部分,其中host是必須的台颠,path則可選褐望。當統(tǒng)跳協(xié)議接收一個跳轉(zhuǎn)請求的URL后,先根據(jù)該URL的host和path兩部分作為條件查找已注冊的服務(wù)串前,再初始化對應(yīng)服務(wù)瘫里,把URL交給服務(wù)實例執(zhí)行后續(xù)操作。

如何實現(xiàn)服務(wù)

統(tǒng)跳協(xié)議聲明了一個服務(wù)接口酪呻,這個接口中只有一個方法减宣,服務(wù)必須由該接口實現(xiàn)而來。每一個服務(wù)可以通過實現(xiàn)接口中聲明的方法玩荠,使用參數(shù)中傳遞來的完整URL漆腌,參數(shù)列表和調(diào)用發(fā)起者指針,執(zhí)行具體業(yè)務(wù)邏輯阶冈。

例如分享服務(wù)闷尿,以iOS為例:實現(xiàn)了TMShareUrlHandler服務(wù)。

@interface TMShareUrlHandler : NSObject<AliAppURLHandler>

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

在分享模塊的配置文件中聲明該服務(wù)的URL為sharekit.tm/doShare女坑。

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

分享模塊.png

分享模塊的配置文件sharekit_bundle.plist也注冊到統(tǒng)跳協(xié)議中填具。

這份配置文件在統(tǒng)跳協(xié)議模塊里:

配置文件.png

統(tǒng)跳協(xié)議如何處理界面跳轉(zhuǎn)

界面跳轉(zhuǎn)是統(tǒng)跳協(xié)議的初衷,也是統(tǒng)跳協(xié)議最重要的任務(wù)。因此在統(tǒng)跳協(xié)議服務(wù)注冊機制中劳景,為界面服務(wù)注冊做了更精細的定制開發(fā)誉简。

上文提到跳轉(zhuǎn)服務(wù)是一個單一服務(wù),而界面則成百上千盟广,所以在界面注冊和服務(wù)注冊中出現(xiàn)了沖突闷串。本著降低開發(fā)成本的原則,我們又希望把同一個模塊中界面注冊和服務(wù)注冊放在一起筋量。所以在統(tǒng)跳協(xié)議中做了如下訂制:

  • 默認注冊跳轉(zhuǎn)服務(wù)

跳轉(zhuǎn)服務(wù)是默認存在的烹吵,在統(tǒng)跳協(xié)議初始化過程中這個服務(wù)就已經(jīng)初始化了。

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

上文中可以看到在注冊分享服務(wù)的配置中object字段是服務(wù)的類名桨武,若界面注冊也按照這個規(guī)則肋拔,那么界面的類就會被認為成一個服務(wù),在調(diào)用過程中必然會出現(xiàn)錯誤呀酸。因此我們約定凉蜂,界面注冊需要在類名前加#標示。

###.png

如此一來七咧,在統(tǒng)跳協(xié)議初始化過程中跃惫,默認加載跳轉(zhuǎn)服務(wù)。當調(diào)用發(fā)生艾栋,解析URL查找到的對應(yīng)對象帶有#爆存,則認為這是一個界面,則初始化這個對象蝗砾,但不對其調(diào)用處理URL的方法先较,而是托管給已注冊的跳轉(zhuǎn)服務(wù)。跳轉(zhuǎn)服務(wù)則根據(jù)URL和初始化的界面對象執(zhí)行跳轉(zhuǎn)服務(wù)悼粮。

Rewrite

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

天貓App中所有界面都是通過URL來標示的扣猫,然而標示Native界面的URL全部都建立在Native規(guī)范下菜循,無法和其他平臺對應(yīng)起來,而Rewrite引擎通過重寫URL來實現(xiàn)平臺一致性申尤。

例如:商品詳情頁面在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識別環(huán)境,從而通過跳轉(zhuǎn)實現(xiàn)一致性时鸵,也就是說在手機瀏覽器訪問PC Web的URL胶逢,會通過一次302轉(zhuǎn)到Mobile Web的URL。而Native App的環(huán)境具有一定的特殊性,Native界面則無法通過類似302這樣的跳轉(zhuǎn)來實現(xiàn)無感知切換初坠,而Rewrite引擎就是來解決這個問題的和簸。首先,無論是Native還是Web某筐,在天貓App中他們兩兩之間的跳轉(zhuǎn)都被統(tǒng)跳協(xié)議托管比搭,而統(tǒng)跳協(xié)議在執(zhí)行跳轉(zhuǎn)操作之前會把原始URL放入Rewrite引擎中做一次Rewrite操作。這樣一來南誊,Rewrite引擎就根據(jù)配置規(guī)則,把原始URL轉(zhuǎn)換成適用于天貓App的目標URL蜜托,實現(xiàn)了URL表現(xiàn)平臺一致性抄囚。

原理

Rewrite引擎的原理非常簡單,模擬Web容器(Apache/Nginx等)的Rewrite配置橄务,根據(jù)配置把傳入的原始URL進行重寫幔托,返回重寫后的目標URL,交給統(tǒng)跳協(xié)議處理蜂挪。

配置是通過正則表達式描述的Rewrite規(guī)則列表重挑,這份列表通過貓客的配置中心實現(xiàn)動態(tài)更新。

Rewrite規(guī)則

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

轉(zhuǎn)換模板中的保留字

  • 變量

    變量由變量標示符和變量名組合而成,如:$0驳糯,$#1篇梭,query$#fragment等酝枢。變量被用在轉(zhuǎn)換串中描述轉(zhuǎn)換后的目標URL中的值恬偷。

    • 變量標示符:$$$$#
      • $原變量的值
      • $$對原變量做URL Encode
      • $#自動識別編碼帘睦,對原變量做URL Decode
      • $$$自動識別編碼袍患,對原變量做URL Decode,再以UTF8URL Encode
    • 變量名:數(shù)字(從0開始)官脓,枚舉(scheme协怒,hostport卑笨,path孕暇,queryfragmentshopid
      • 數(shù)字:0 -表示整個URL妖滔,1~n - 表示正則中使用圓括號取出的參數(shù)
      • 枚舉:scheme隧哮,hostport座舍,path沮翔,queryfragment表示標準URL中的相應(yīng)部分曲秉;shopid表示對個性店鋪域名查詢后得到的shopid
  • 標記位

    即上述規(guī)則中的標記位

Rewrite引擎查詢流程

  1. 取出規(guī)則列表中的首條規(guī)則
  2. 以模式串為模板對原始URL做匹配采蚀,并得到模式串定義的參數(shù)表
  3. 若匹配成功則繼續(xù)進行,否則進入下一條規(guī)則承二,從2開始進行下一輪匹配
  4. 查看該條規(guī)則是否包含s標記位榆鼠,若包含,則使用原始串做一次個性域名的查詢
  5. 使用1的結(jié)果和重寫串對原始URL進行重寫操作亥鸠,得到目標URL
  6. 查看該條規(guī)則是否包含l標記位:
    • 若包含妆够,則結(jié)束匹配,返回目標URL
    • 若不包含负蚊,則把目標URL賦值給原始URL神妹,并進入下一條規(guī)則,從2開始下一輪匹配
  7. 直到最后一條規(guī)則結(jié)束家妆,返回目標URL

舉例

上述提到過商品詳情頁的例子鸵荠,在Rewrite配置中就體現(xiàn)為:

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市凌箕,隨后出現(xiàn)的幾起案子拧篮,更是在濱河造成了極大的恐慌,老刑警劉巖牵舱,帶你破解...
    沈念sama閱讀 223,126評論 6 520
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件串绩,死亡現(xiàn)場離奇詭異,居然都是意外死亡芜壁,警方通過查閱死者的電腦和手機礁凡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,421評論 3 400
  • 文/潘曉璐 我一進店門高氮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人顷牌,你說我怎么就攤上這事剪芍。” “怎么了窟蓝?”我有些...
    開封第一講書人閱讀 169,941評論 0 366
  • 文/不壞的土叔 我叫張陵罪裹,是天一觀的道長。 經(jīng)常有香客問我运挫,道長状共,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,294評論 1 300
  • 正文 為了忘掉前任谁帕,我火速辦了婚禮口芍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘雇卷。我一直安慰自己,他們只是感情好颠猴,可當我...
    茶點故事閱讀 69,295評論 6 398
  • 文/花漫 我一把揭開白布关划。 她就那樣靜靜地躺著,像睡著了一般翘瓮。 火紅的嫁衣襯著肌膚如雪贮折。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,874評論 1 314
  • 那天资盅,我揣著相機與錄音调榄,去河邊找鬼。 笑死呵扛,一個胖子當著我的面吹牛每庆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播今穿,決...
    沈念sama閱讀 41,285評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼缤灵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蓝晒?” 一聲冷哼從身側(cè)響起腮出,我...
    開封第一講書人閱讀 40,249評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芝薇,沒想到半個月后胚嘲,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,760評論 1 321
  • 正文 獨居荒郊野嶺守林人離奇死亡洛二,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,840評論 3 343
  • 正文 我和宋清朗相戀三年馋劈,在試婚紗的時候發(fā)現(xiàn)自己被綠了攻锰。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,973評論 1 354
  • 序言:一個原本活蹦亂跳的男人離奇死亡侣滩,死狀恐怖口注,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情君珠,我是刑警寧澤寝志,帶...
    沈念sama閱讀 36,631評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站策添,受9級特大地震影響材部,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜唯竹,卻給世界環(huán)境...
    茶點故事閱讀 42,315評論 3 336
  • 文/蒙蒙 一乐导、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧浸颓,春花似錦物臂、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,797評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晋涣,卻和暖如春仪媒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谢鹊。 一陣腳步聲響...
    開封第一講書人閱讀 33,926評論 1 275
  • 我被黑心中介騙來泰國打工算吩, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人佃扼。 一個月前我還...
    沈念sama閱讀 49,431評論 3 379
  • 正文 我出身青樓偎巢,卻偏偏與公主長得像,于是被迫代替她去往敵國和親松嘶。 傳聞我的和親對象是個殘疾皇子艘狭,可洞房花燭夜當晚...
    茶點故事閱讀 45,982評論 2 361

推薦閱讀更多精彩內(nèi)容