Oceanus是美团基础架构部研发的统一HTTP服务治理框架,基于Nginx和ngx_lua扩展,主要提供服务注册与发现、动态负载均衡、可视化管理、定制化路由、安全反扒、session ID复用、熔断降级、一键截流和性能统计等功能。本文主要讲述Oceanus如何经过策略抽象、查询、渲染和分组动态更新,实现HTTP请求的定制化路由。 随着公司业务的高速发展,路由场景也愈来愈复杂。好比:html
因为公司早期的业务场景相对比较简单,因此均经过Nginx if指令支持。好比某业务要把来源IP为10.4.242.16的请求转发到后端节点10.4.232.110,其它请求转发到后端节点10.4.232.111和10.4.232.112,就能够进行以下配置:nginx
upstream backend_aaa {
server 10.4.232.110:8080 weight=10;
}
upstream backend_bbb {
server 10.4.232.111:8080 weight=10;
server 10.4.232.112:8080 weight=10;
}
location /abc {
if($remote_ip = "10.4.242.16") {
proxy_pass http://backend_aaa; #路由到backend_aaa集群
}
proxy_pass http://backend_bbb; #路由到backend_bbb集群
}
复制代码
上述方式虽然不须要额外开发,性能方面也接近原生的Nginx框架,可是使用场景比较受限,由于if指令仅支持比较简单的condition类型,官方描述以下:git
若是该业务要把IP段10.4.242.16/34的请求转发到10.4.232.110时,if指令勉强还能够支持。但对于上述的复杂业务场景,if指令均没法支持。除此以外,这种方式还存在如下两点不足:github
为了解决上述问题,Oceanus开始探索如何实现HTTP流量的定制化路由。算法
经过初步调研,发现业界有一套开源的ABTestingGateway(如下简称AB)框架:数据库
因为AB框架只支持4种策略类型,对于业务要根据请求Cookie、自定义header控制转发的状况,均须要开发新的策略类型和发布上线。另外,策略类型和业务场景紧密相关,致使AB系统的扩展性极差,很难快速支持新业务的路由需求。后端
不管是Nginx if指令,仍是AB框架,要么须要reload从新加载才能生效,要么没法支持某些业务场景下的分流需求,因此都很难做为解决公司级分流框架的有效手段。针对它们所存在的不足,Oceanus开发了一套应用级、高可扩展的动态分流框架,不只动态支持各类业务场景的分流需求,并且保证了请求转发的性能,下文将阐述咱们如何解决分流机制的几个核心问题。api
关于分流机制,咱们主要从如下四个方面来说述:数组
以AB框架为例,只支持iprange、uidrange、uidsuffix、uidappoint四种场景,对策略类型和匹配方式太具体化,致使没法支持更多普适性的业务场景。从分流的本质出发,即根据请求特征完成流量的定制化路由。结合Nginx if指令的几个组成部分:条件判断依赖的变量、条件判断要匹配的value、条件表达式、匹配后要执行的proxy_pass,一个策略必需要包含请求特征描述、定制化路由描述以及二者的关系描述。其中请求特征描述包含特征关键字、关键字的上下文传输方式,定制化路由描述经过Upstream表示,Upstream能够预先设置,也能够动态指定,二者的关系经过泛型表达式表示。那么一个策略就须要包含下面几个属性:缓存
其中switch、graylist字段主要用于策略的上下线操做,这里不作过多讨论。下面重点介绍上面的策略定义是如何表述业务场景的:
分流策略分为私有策略和公共策略。私有策略是面向服务的,并且和该服务建立的分组紧密相关。不一样服务的私有策略彻底独立,能够相同,也能够不一样。一个服务能够配置多个私有策略,也能够关联多个Host的Location,Location之间的策略使用彻底独立,一个Location能够启用该服务的一个或者多个私有策略。若是经过Host+location_path直接关联策略数据,不一样Location关联同一个私有策略时,会存在大量的数据冗余。因此咱们经过服务标识(appkey,惟一标识一个应用服务)关联具体的策略数据,Host+location_path只关联当前Location使用的策略名列表,策略之间支持指定顺序。 公共策略与具体服务无关,策略名全局惟一,可使用策略名关联策略数据便可。综上,策略的拓扑关系描述以下:
Nginx在解析Location配置时,经过不一样的字段区分不一样类型的Location,没有记录配置中的Location path。若是要运行时获取,通常有两种方式:一种是根据相关字段逆向还原path,另外一种是为框架新增变量。因为Nginx在处理正则Location时,对因而否忽略大小写的状况,并无作标记,即解析的过程是不可逆的,因此咱们选择了第二种方式。在核心模块的变量数组ngx_http_core_variables中新增了内置变量,记录下原始的Location path,变量属性定义以下:
{ngx_string("loc_mod"), NULL, ngx_http_variable_loc_mod,
0, NGX_HTTP_VAR_NOCACHEABLE, 0},
{ngx_string("loc_name"), NULL, ngx_http_variable_loc_name,
0, NGX_HTTP_VAR_NOCACHEABLE, 0}
复制代码
loc_mod和loc_name之间用一个空格符链接,格式和Oceanus管理平台保持一致。
为了保证运行时获取策略数据的高效性,咱们经过异步定时拉取,把策略数据全量同步到本地的共享内存中。基于稳定性和灵活性的考虑,咱们采用了关系型数据库MySQL存储策略。 更新机制以下图所示:
为了解决timer worker和其它worker在读写策略数据时的竞态关系,咱们采用了双buffer机制,实现了业务层策略数据的无锁读写。另外,经过设置timer的时间为0,保证在全部worker处理请求前,策略数据已经在共享内存中完成初始化。
查询算法以下图所示:
备注:公共策略以"oceanus"开头,区别于私有策略的命名。
查询到请求开启的策略后,Oceanus须要运行时判断是否匹配,以私有策略为例,执行流以下图所示:
分组列表的动态化是分流框架的重要一环。更新机制以下图所示:
经过Oceanus分流机制在美团外卖、酒旅、到店餐饮等多个业务线的普遍使用,基础架构部帮助业务同胞解决了多个定制化路由的需求,好比服务set化、链路压测、灰度发布、泳道环境建设等等。目前,Oceanus分流机制只关注了流量转发方向,还不支持更复杂的转发动做,好比根据策略调整请求的Parameter、header、Cookie,也不支持根据请求的URL实现动态路由等,将来咱们还将逐一完善这些问题,固然也欢迎你们跟咱们一块儿交流,共同进步。
周峰,美团高级工程师,2015年7月加入美团基础架构部,前后负责统一密钥管理服务、智能反爬服务和HTTP负载均衡,目前主要负责HTTP服务治理Oceanus的相关工做,致力于探索和研究服务的自动化、智能化、和高性能等方向。
招聘广告:若是你对大规模分布式环境下的HTTP服务治理、分布式会话链路追踪等系统感兴趣,诚挚欢迎投递简历至:zhangzhitong#meituan.com。