原文: http://pingguohe.net/2015/11/24/Navigator-and-Rewrite.html

解耦神器 —— 统跳协议和Rewrite引擎

Nov 24, 2015 • Gaosboy

题记:天猫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引擎与之配合。

统跳协议

统跳协议设计之初就保留了很强的可扩展性,为接下来更丰富的场景预留了能力。上文讲到了统跳协议在界面跳转中作用,而事实上界面跳转仅仅是这套方案的一个典型场景,一个最佳实践。界面跳转在统跳协议的框架中被认为成一个服务,而跳转到哪一个界面则是由服务内部实现决定的。

注册一个服务

服务通过声明URL的方式注册到统跳协议中,这个声明发生在服务所属模块内部的一个配置文件中,而这个配置文件被注册到统跳协议里。也就是说,整个App中的每一个模块都要注册一个配置文件到统跳协议,统跳协议在初始化过程中会遍历配置文件列表,逐一加载这些模块配置,根据配置信息把一个一个的模块服务注册到协议中。

统跳协议要求调用服务的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

这份配置文件在分享模块里:

分享模块的配置文件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引擎查询流程

  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,让所有人一起挖掘这套方案的潜力。

 

页面解耦—— 统跳协议和Rewrite引擎的更多相关文章

  1. 如何优雅的实现界面跳转 之 统跳协议 - DarwinNativeRouter

    PS 感谢大家的关注,由于我本想开源4个库,除了router, 另外三个分别是native dispatcher, web dispatcher 和 react dispatcher , 所以rout ...

  2. http协议和web应用有状态和无状态浅析

    http协议和web应用有状态和无状态浅析 (2013-10-14 10:38:06) 转载▼ 标签: it   我们通常说的web应用程序的无状态性的含义是什么呢? 直观的说,“每次的请求都是独立的 ...

  3. http协议和web本质

    转载:http://www.cnblogs.com/dinglang/archive/2012/02/11/2346430.html http协议和web本质 当你在浏览器地址栏敲入“http://w ...

  4. HTTP协议和SOCKS5协议

    HTTP协议和SOCKS5协议 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 我们平时上网的时候基本上是离不开浏览器的,尤其是搜索资料的时候,那么这个浏览器是如何工作的呢?用的又是 ...

  5. Http协议和Tomcat服务器

    Http协议和Tomcat服务器 什么是Http协议 HTTP,超文本传输协议(HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议. Http协议的组成 Ht ...

  6. http协议和chrome浏览器

    http协议和Chrome抓包工具 什么是http和https协议: HTTP协议:全称是HyperText Transfer Protocol,中文意思是超文本传输协议,是一种发布和接收HTML页面 ...

  7. 网络协议学习笔记(八)DNS协议和HttpDNS协议

    概述 上一篇主要讲解了流媒体协议和p2p协议,现在我给大家讲解一下关于DNS和HttpDNS的相关知识. DNS协议:网络世界的地址簿 在网络世界,也是这样的.你肯定记得住网站的名称,但是很难记住网站 ...

  8. 在线聊天室的实现(1)--websocket协议和javascript版的api

    前言: 大家刚学socket编程的时候, 往往以聊天室作为学习DEMO, 实现简单且上手容易. 该Demo被不同语言实现和演绎, 网上相关资料亦不胜枚举. 以至于很多技术书籍在讲解网络相关的编程时, ...

  9. js 页面之间的跳转、传参以及返回上一页

    js实现html 页面之间的跳转传参以及返回上一页的相关知识点 一.页面之间的跳转传参 1.在页面之间跳转的方式有两种: window.location.href="test.html?nu ...

随机推荐

  1. [LeetCode] Arranging Coins 排列硬币

    You have a total of n coins that you want to form in a staircase shape, where every k-th row must ha ...

  2. Underscore 整体架构浅析

    前言 终于,楼主的「Underscore 源码解读系列」underscore-analysis 即将进入尾声,关注下 timeline 会发现楼主最近加快了解读速度.十一月,多事之秋,最近好多事情搞的 ...

  3. hdu3714 三分找最值

    Error Curves Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Tota ...

  4. Sqlserver内置函数实现MD5

    16位: SELECT substring(sys.fn_sqlvarbasetostr(HashBytes('MD5', '需要加密字符串')),3,16) 32位 SELECT substring ...

  5. js判断浏览器类型

    使用navigator.userAgent和来判断 PC端: <script type="text/javascript">var ua=navigator.userA ...

  6. 百度文库下载器 V2.3.4.3 支持豆丁百度文库道客巴巴

    支持豆丁百度文库道客巴巴免下载劵财富值导出word 下载地址: http://pan.baidu.com/s/1qYCmQde

  7. Leetcode 259. 3Sum Smaller

    class Solution(object): def threeSumSmaller(self, nums, target): """ :type nums: List ...

  8. note

    John的博客 http://blog.sina.com.cn/chinatownjohn 剑4-11真题+新东方pdf,王陆语料库(听力)+顾家北手把手教你写剑9版(写作)+人人雅思哥记忆卡(口语) ...

  9. URL.createObjectURL和URL.revokeObjectURL

    一.URL.createObjectURL URL.createObjectURL()方法会根据传入的参数创建一个指向该参数对象的URL. 这个URL的生命仅存在于它被创建的这个文档里,新的对象URL ...

  10. web前端基础知识-(八)Django进阶之数据库对象关系映射

    Django ORM基本配置 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻辑层去 ...