写点什么

Oceanus:美团 HTTP 流量定制化路由的实践

  • 2020-02-25
  • 本文字数:4904 字

    阅读完需:约 16 分钟

Oceanus:美团HTTP流量定制化路由的实践

背景

Oceanus 是美团基础架构部研发的统一 HTTP 服务治理框架,基于 Nginx 和 ngx_lua 扩展,主要提供服务注册与发现、动态负载均衡、可视化管理、定制化路由、安全反扒、session ID 复用、熔断降级、一键截流和性能统计等功能。本文主要讲述 Oceanus 如何通过策略抽象、查询、渲染和分组动态更新,实现 HTTP 请求的定制化路由。


随着公司业务的高速发展,路由场景也越来越复杂。比如:


  • 团购秒杀要灵活控制压测流量,实现线上服务单节点、各机房、各地域等多维度的压测。

  • 外卖业务要做流量隔离,把北方地域的流量转发到分组 a,南方地域的流量转发到分组 b。

  • 酒旅业务要对 App 新版本进行灰度,让千分之一的用户试用新版本,其他用户访问老版本。

  • QA 部门要通过请求的自定义参数指定转发分组,构建稳定且高可用的测试环境。


由于公司早期的业务场景相对比较简单,所以均通过 Nginx if 指令支持。比如某业务要把来源 IP 为 10.4.242.16 的请求转发到后端节点 10.4.232.110,其它请求转发到后端节点 10.4.232.111 和 10.4.232.112,就可以进行如下配置:


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 类型,官方描述如下:



如果该业务要把 IP 段 10.4.242.16/34 的请求转发到 10.4.232.110 时,if 指令勉强还可以支持。但对于上述的复杂业务场景,if 指令均无法支持。除此之外,这种方式还存在以下两点不足:


  • 规则调整不支持动态化:如果要把客户端 10.4.242.16 调整为 10.4.242.17,需要对 Nginx 进行 reload,而 reload 操作会使 Nginx 的并发能力下降,业务高峰时甚至会导致请求 504 或 502。

  • 指令坑太多:if 指令和 set、rewrite 指令等一起使用时,很多时候会出现不符合预期的行为,严重时甚至会导致段错误,最好的方法就是避免使用。


为了解决上述问题,Oceanus 开始探索如何实现 HTTP 流量的定制化路由。

业界调研

通过初步调研,发现业界有一套开源的 ABTestingGateway(以下简称 AB)框架:



由上图所示,AB 框架使用 Redis 存储策略数据,key 是 Host 字段,value 是策略对象,包括策略类型、匹配区间和要分发的 Upstream。策略的增删改查可以通过基于 Nginx 搭建的 Web 服务的 API 实现,运行时根据请求的 Host 字段从 lua-shared-dict 或 Redis 获取关联的策略,根据策略类型(iprange/uidrange/uidsuffix/uidappoint)选择对应的 Lua 脚本从请求中获取相关参数(IP、UID)查询是否匹配策略,若匹配,就修改请求的 Upstream 上下文完成分流的目的。


相比 if 指令的方式,AB 框架有下面两个优点:


  • 策略调整动态生效:已有策略类型中的策略变更均可以通过 HTTP API 进行动态管理。

  • 分流策略丰富:支持 IP 段、UID 段等策略,也可以通过新增策略类型对策略库进行扩展。


由于 AB 框架只支持 4 种策略类型,对于业务要根据请求 Cookie、自定义 header 控制转发的情况,均需要开发新的策略类型和发布上线。另外,策略类型和业务场景紧密相关,导致 AB 系统的扩展性极差,很难快速支持新业务的路由需求。


无论是 Nginx if 指令,还是 AB 框架,要么需要 reload 重新加载才能生效,要么无法支持某些业务场景下的分流需求,所以都很难作为解决公司级分流框架的有效手段。针对它们所存在的不足,Oceanus 开发了一套应用级、高可扩展的动态分流框架,不仅动态支持各种业务场景的分流需求,而且保证了请求转发的性能,下文将阐述我们如何解决分流机制的几个核心问题。

Oceanus 定制化路由的核心设计 &实现

关于分流机制,我们主要从以下四个方面来讲述:


  • 策略抽象:合理定义策略结构,适用尽可能多的业务场景。

  • 策略的高效查询:接口粒度关联,应用维度管理。

  • 运行时策略渲染:渲染策略模板,判断是否匹配策略,实现动态路由。

  • 分组动态更新:分组数据增删改,均不需要 reload。

策略的结构定义

以 AB 框架为例,只支持 iprange、uidrange、uidsuffix、uidappoint 四种场景,对策略类型和匹配方式太具体化,导致无法支持更多普适性的业务场景。从分流的本质出发,即根据请求特征完成流量的定制化路由。结合 Nginx if 指令的几个组成部分:条件判断依赖的变量、条件判断要匹配的 value、条件表达式、匹配后要执行的 proxy_pass,一个策略必须要包含请求特征描述、定制化路由描述以及两者的关系描述。其中请求特征描述包含特征关键字、关键字的上下文传输方式,定制化路由描述通过 Upstream 表示,Upstream 可以预先设置,也可以动态指定,两者的关系通过泛型表达式表示。那么一个策略就需要包含下面几个属性:


  • name:策略名,没有实际意义,可以根据业务场景进行定义。

  • key:分流时依赖的关键字,比如要根据城市地域进行分发路由时,key 就是 regionid。

  • passway:关键字在 HTTP 协议中的传输方式,可以是 Parameter、Cookie、header、body 中的一种。

  • condition:表达式模板,支持四则运算/取模、关系运算符、逻辑运算符等。

  • group:后端服务集群,即匹配策略后,转发请求的目标节点,一般是策略所属应用集群中的部分节点。

  • category:策略类型,如果为 1,表示某个服务的私有策略;如果为 2,表示公共策略,主要用于策略数据管理。

  • switch:策略开关,用于控制当前策略是在线还是离线。

  • graylist:灰度列表,用于策略变更的线上灰度校验。


其中 switch、graylist 字段主要用于策略的上下线操作,这里不做过多讨论。下面重点介绍上面的策略定义是如何表述业务场景的:



备注:应用 apk1 和 apk2 分别配置 2 个私有策略,apk3 使用公共策略。


如上图所示,无论业务根据请求的哪些特征进行分流,策略结构均可以支持。


以私有策略 gray-deploy 为例,在 Oceanus 管理平台进行添加,如下图所示:



备注:这里省略了策略的非核心字段比如 switch、graylist 等。

如何实现策略的高效查询?

策略拓扑关系

分流策略分为私有策略和公共策略。私有策略是面向服务的,而且和该服务创建的分组紧密相关。不同服务的私有策略完全独立,可以相同,也可以不同。一个服务可以配置多个私有策略,也可以关联多个 Host 的 Location,Location 之间的策略使用完全独立,一个 Location 可以启用该服务的一个或者多个私有策略。如果通过 Host+location_path 直接关联策略数据,不同 Location 关联同一个私有策略时,会存在大量的数据冗余。所以我们通过服务标识(appkey,唯一标识一个应用服务)关联具体的策略数据,Host+location_path 只关联当前 Location 使用的策略名列表,策略之间支持指定顺序。


公共策略与具体服务无关,策略名全局唯一,可以使用策略名关联策略数据即可。综上,策略的拓扑关系描述如下:



StrategyTopology


如上图所示,以应用 apk1 为例,关联了两个 Location 接口,分别为/api 和/list,总共部署了 8 个节点,创建了 2 个分组 ups-cq 和 ups-gray,其中节点 10.5.23.6 和 10.5.24.72 属于分组 ups-cq,节点 10.7.46.32 和 10.7.72.232 属于分组 ups-gray。应用配置了两个私有策略 stress-testing 和 gray-deploy,其中策略 stress-testing 被接口/api 启用,匹配策略的流量路由到分组 ups-cq,策略 gray-deploy 被接口/list 启用,匹配策略的流量路由到 ups-gray。

运行时获取 Location path

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 存储策略。


更新机制如下图所示:



  1. Oceanus 在 init_worker 阶段随机选择某个 worker 进程,嵌入 timer。

  2. 被选中的 worker 会异步非阻塞地从 MySQL 定时拉取策略数据。

  3. timer worker 把拉取到的策略数据解析,按照策略的拓扑关系,更新到当前共享内存中的写缓存区,完成更新后,切换读写缓存区,保证最新的策略立即生效。

  4. worker 进程在处理请求时,从当前共享内存中的读缓存区获取策略数据。


为了解决 timer worker 和其它 worker 在读写策略数据时的竞态关系,我们采用了双 buffer 机制,实现了业务层策略数据的无锁读写。另外,通过设置 timer 的时间为 0,保证在所有 worker 处理请求前,策略数据已经在共享内存中完成初始化。

策略查询机制

查询算法如下图所示:



  1. worker 进程从 request 上下文中获取请求的 Host,以及所匹配 Location 的 location_path。

  2. 根据 Host+location_path,到共享内存中查询所开启的策略名。

  3. 如果是公共策略,直接根据策略名去查询策略数据。

  4. 如果是私有策略,从 request 上下文获取 Location 关联的 Upstream,即应用标识 appkey,到共享内存读缓存区获取具体的策略数据。


备注:公共策略以”oceanus”开头,区别于私有策略的命名。

运行时策略渲染

查询到请求开启的策略后,Oceanus 需要运行时判断是否匹配,以私有策略为例,执行流如下图所示:



  1. 在 rewrite phase,Oceanus 通过 rewrite_by_lua_file 嵌入回调,触发请求处理,进入分流框架的主流程。

  2. 通过上面的策略查询机制获取请求的策略,进行解析,获取策略的 key 和 passway。

  3. 根据 passway 从请求对应的上下文获取 key 的 value。

  4. 用 3 获取到的 value 渲染策略的 condition,把 condition 中的占位符替换为 value。

  5. 基于 Lua VM,通过 load 计算 condition 的结果,即 true 或 false。

  6. 从策略中获取 condition 的 value 和 group 数据。

  7. 如果 condition 为 true,就用 group 覆盖请求的 Upstream 上下文,否则,不做处理。

分组动态更新

分组列表的动态化是分流框架的重要一环。更新机制如下图所示:



  1. 分组数据使用 ZooKeeper 存储,变更通过 watcher 机制实现增量同步。

  2. Oceanus 也会定时拉取,进行全量同步。

  3. Oceanus 把所有变更都通过本地的 HTTP 调用同步到 Nginx 内存。

  4. worker 处理变更请求前,会先抢锁,读取共享内存中的消息队列,同步其它 worker 进行的历史变更。

  5. 把这次变更同步到当前 worker 的 Upstream main 上下文中,完成当前 worker 的更新。

  6. 把变更封装成消息,加入到共享内存中的队列。

  7. 其它 worker 通过 timer 或者自己处理变更消息前读取消息队列,完成更新。

总结

通过 Oceanus 分流机制在美团外卖、酒旅、到店餐饮等多个业务线的广泛使用,基础架构部帮助业务同胞解决了多个定制化路由的需求,比如服务 set 化、链路压测、灰度发布、泳道环境建设等等。目前,Oceanus 分流机制只关注了流量转发方向,还不支持更复杂的转发动作,比如根据策略调整请求的 Parameter、header、Cookie,也不支持根据请求的 URL 实现动态路由等,未来我们还将逐一完善这些问题,当然也欢迎大家跟我们一起交流,共同进步。

作者简介

  • 周峰,美团高级工程师,2015 年 7 月加入美团基础架构部,先后负责统一密钥管理服务、智能反爬服务和 HTTP 负载均衡,目前主要负责 HTTP 服务治理 Oceanus 的相关工作,致力于探索和研究服务的自动化、智能化、和高性能等方向。

参考文献


2020-02-25 20:33842

评论

发布
暂无评论
发现更多内容

金三银四互联网大厂精选1160道Java面试题答案整理(2023最新版)

架构师之道

编程 程序员 java面试

不可错过!Arm 、Intel 及阿里云等资深技术专家现场解读系统安全

OpenAnolis小助手

系统安全 Meetup 龙蜥社区 sig 机密计算

GaussDB(DWS)运维:导致SQL执行不下推的改写方案

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 3 月 PK 榜

镜舟:打造行业顶级国产OLAP数据库

镜舟科技

龙蜥自动化平台 SysOM 2.1 热补丁中心介绍 | 第 74 期

OpenAnolis小助手

直播 系统运维 龙蜥大讲堂 SysOM 补丁

终于有人把Java面试高分Guide总结得如此系统,堪称傻瓜式笔记总结

采菊东篱下

java面试

adobe安装提示错误“Error:SyntaxError:JSON Parse error:Unexpected EOF”

互联网搬砖工作者

镜舟科技荣获IT168年度技术卓越奖!

镜舟科技

数据库

测试人社区技术沙龙——计算机视觉在App兼容性测试中的实践

测试人

软件测试 测试开发 测试开发自动化测试

2023中国儿童防敏市场发展洞察

易观分析

医疗 防敏 儿童

CloudCanal 落地 StarRocks 数据迁移同步的实践与思考

StarRocks

数据库

中国全屋智能市场将达万亿级,仅3.5%住宅渗透率拥有巨大潜力

Geek_2d6073

镜舟数据库与 DataBuilder 完成兼容性认证,助力企业开展“极速统一”数据分析

镜舟科技

数据库

初学后端,如何做好表结构设计?

王中阳Go

Go golang 数据库 表结构 golang 面试

镜舟数据库荣获 CSDN 年度创新产品与解决方案!

镜舟科技

数据库

【分享】为什么我设计的PCB很少出错?

华秋PCB

工具 测试 电路 PCB PCB设计

2022 OpenHarmony年度运营报告

OpenHarmony开发者

OpenHarmony

面试官:try-catch放在循环体内还是循环体外,哪种效率更高?

做梦都在改BUG

pytest学习和使用3-对比unittest和pytest脚本在pycharm中运行的方式

Python 自动化测试 pytest

好家伙!阿里最新SpringBoot进阶笔记涵盖了SpringBoot所有骚操作

Java你猿哥

Java Spring Boot 面经 SSM框架

pytest学习和使用4-pytest和Unittest中setup、teardown等方法详解和使用(最全)

Python 自动化测试 unittest 测试框架 pytest

腾讯大神耗时三年,立足实际开发的巅峰之作,详解高并发程序设计

做梦都在改BUG

Java 程序设计 高并发

NFTScan 与 DeBox 达成合作,双方在 NFT 社交数据层面展开合作

NFT Research

NFT

软件工程高效学 | 软件的内涵与危机

TiAmo

软件工程 软件开发

面试被怼:技术更新这么快,你还不懂响应式微服务就out了

做梦都在改BUG

Java spring 微服务 响应式

好用的Java开发工具:IntelliJ IDEA 2022v2022.3.3汉化激活版

真大的脸盆

Java Mac Mac 软件 Java 开发

Git客户端工具:SourceTree中文激活版

真大的脸盆

git Mac Mac 软件 Git客户端

ByteHouse:基于ClickHouse 的实时计算能力升级

字节跳动数据平台

大数据 云原生 flink 消费 kafka Clickhouse 企业号 3 月 PK 榜

百度CTO王海峰:全栈AI技术加持,打造新一代大语言模型文心一言

飞桨PaddlePaddle

跟着字节AB工具DataTester,5步开启一个实验

字节跳动数据平台

大数据 云服务 AB testing实战 ab测试 企业号 3 月 PK 榜

Oceanus:美团HTTP流量定制化路由的实践_文化 & 方法_美团技术团队_InfoQ精选文章