Alibaba Sentinel RESTful 接口流控处理优化

栏目: 后端 · 发布时间: 4年前

内容简介:作者:亦山札记 ID:louluan_note笔者最近打算使用

作者:亦山札记 ID:louluan_note

0.前言

笔者最近打算使用 Sentinel 替换掉之前的 Hystrix 作为微服务架构的熔断/断路组件。整体上, Sentinel 的设计比 Hystrix 要易用很多。在实际使用的过程中,也存在了一些问题。本文将介绍Sentinel 在处理RESTful 风格的web项目过程中存在的问题。

问题描述

在Spring Cloud架构下,如果Http请求格式是按照RESTful风格设计的,当大规模的Http请求访问系统集群, Sentinel Dashboard实时监控簇点链路 的记录数非常多;看到的资源名已经飙到了 几千条之多 !另外虽然 资源名 数量庞大,但是监控的TPS和并发数却非常低,甚至很多资源名仅仅被访问过一次。想从这么多资源名中找到动态调控 TPS 或者 并发数资源名 ,是非常困难的。另外由于统计量级的问题,也会导致  sentinel 控制机器往 Sentinel Dashboard 传输的统计数据也非常大,整体请求下来,会导致整体服务的质量变得非常差。

1.RESTful接口问题重现:

1.1 在服务端定义一个RESTful风格的URL接口:

1.2 项目中引入 spring-cloud-starter-alibaba-sentinel 依赖

1.3 模拟客户端,请求Http服务,随机生成请求访问

1.4 在 Sentinel Dashboard 上的 簇点链路实时跟踪 已经飚到上千个 资源名

Alibaba Sentinel RESTful 接口流控处理优化

Alibaba Sentinel RESTful 接口流控处理优化

问题分析

通过上述的例子中,可以看到, Sentinel 将每一个Http 请求的URL当成了一个唯一的 资源名 ,用来做流控限制,这显然是非常不合适的。当大并发过来时,已经影响到了流控组件的性能消耗。

接下来我们将分析 sentinel 的工作机制,通过分析,来找到恰当的解决方案。

2. 当前Sentinel对于Web请求的处理原理

如果Spring Cloud 项目中,引入了 spring-cloud-starter-alibaba-sentinel ,那么该组件将自动创建一个拦截器,拦截所有的Http请求。当每一个请求进入系统后,该拦截器会获取到当前Http请求的 URL路径 ,并将该 URL路径 作为 资源名 ,用来做sentinel 基于资源名的流控操作。整体请求的的行为如下图所示:

Alibaba Sentinel RESTful 接口流控处理优化

Sentinel 对Web请求流控的抽象

工作流程大概如下:

  • Http请求经过sentinel自定义的拦截器 Sentinel CommonFilter ,该拦截器从请求中提取 URL , 然后将此当做 流控资源名 ;

  • 提取 流控资源名 之后,进入Sentinel 核心的代码段 entry.enterentry.exit 包裹处理;

  • 包裹后,开始执行Spring MVC自身的HandlerMapping映射处理机制,从上下文中挑选合适的 HandlerMethod ,即某一个合适的Controller的 @RequestMapping 方法上

  • 执行Controller方法,返回结果给 Sentinel CommonFilter ,根据结果执行 entry.exit ,完成单次资源访问控制逻辑

其拦截器的实现也非常简单,如下所示:

通过上面的流程来看,问题就出现在 Sentinel CommonFilter 上。那么,既然Request URL不适合做  资源名 ,那什么适合做资源名控制呢?

3. RESTful 风格的请求,应当怎么定义 Sentinel 的资源名?

首先应当明确的是: 资源名 的选取,要具备实际的可控制的意义,按照上面所示的RESTful接口而言:

GET /api/v1/{tenantId}/order/{orderId}/basic

顾名思义,上述的URL表意是:获取某一个 租户 的某一个 订单 的基本信息,请求中存在两个PathVariable变量;

基于上述的定义,我们可以有如下几种方式挑选:

  • 选取方式1 :上述的这个 RequestMapping 实际上是和对应的Controller的某一个方法是一一映射的,如果我们将 资源名 的定义界定为对某一个类的某一个方法的调用时,我们就可以选取 /api/v1/{tenantId}/order/{orderId}/basic 作为资源名。 这种维度是比较粗的,从定义上来看,我们限制的是当前系统内,对某一个Controller类的某一个方法的调用。

  • 选取方式2 根据自己的需要,决定 PathVariable 的值是否可以作为 资源创建 的参数 。实际上,如果定义成了 方式1 的资源名,通过系统的角度上而言,我可以控制系统内对这个资源的访问;而不能细化到某一个特定 租户 的请求访问;基于租户做流控限制这种需求很常见,假设在一个多租户的系统内,当某一个租户的请求猛增时,如果不加限制,可能会影响到其他租户的正常使用。所以从这个角度上,我希望的资源定义可以根据住户编号的不同,分别创建 资源名 ,如下所示:

资源名 解释
/api/v1/ 12345678 /order/{orderId}/basic 租户 12345678 的订单查询流控限制
/api/v1/ 88888888 /order/{orderId}/basic 租户 88888888 的订单查询流控限制
/api/v1/ 99999999 /order/{orderId}/basic 租户 99999999 的订单查询流控限制
  • 选取方式3 完全使用 Pathvariable 的真实值来构造 资源名 ,这种方式的结果就和问题举例一样,会导致 sentinel 性能极差。

上述三种方案,从产生的资源名数量来看 : 方式1方式2方式3 ,所以我们的策略应该尽可能往 方式1方式2 上靠。

另外,定义 资源名 时,应道考虑其 可流控性 ,像 /api/v1/99999999/order/3344455666/basic 这种查询某一个特定订单基本信息的请求,在实际的系统中,其并发访问实际上时非常低的,这样的就不具备 可流控性 ,而 /api/v1/99999999/order/{orderId}/basic 请求,可以限定某一个的租户的所有查询订单请求,这个就具备 可流控性

4.如何在过滤器(Filter)层获取到当前请求对应的 HandlerMethod ?

为了实现上述的 方式1方式2 ,则需要有一个能力:需要在过 滤器层 就能知道当前 请求 应当被哪一个Controller的哪一个方法执行,即能够找到对应的 HandlerMethod 。而在目前的SpringMVC框架模式下,请求的调用关系是先经过拦截器,然后才能通过 DispatchServlet 的机制找到对应 HandlerMethod ,并处理。

Alibaba Sentinel RESTful 接口流控处理优化

那问题来了,怎么在 Filter 中提前感知到对应请求的 HandlerMethod 呢?

问题的答案在SpringMVC上,

Spring MVC是如何为一个请求找对应的 HandlerMethod 的?

4.1 Spring MVC是如何为一个请求找对应的 HandlerMethod 的?

如下代码时 SpringMVCDispatchServlet 核心实现逻辑。整体流程会包含如下几步:

  • 步骤1:根据当前 请求 ,查找HandlerExecutionChain,该对象内部包含HandlerMethod

  • 步骤2:根据当前HandlerExecutionChain 找到合适的HandlerAdaptor,用于处理请求

  • 步骤3:调用HandlerAdaptor,处理请求

  • 步骤4:返回ModelAndView

很明显,在 步骤1 的时候,我们已经可以获取到 HandlerExecutionChain ,该对象就包含了 HandlerMethod 。我们再来看这一步骤是怎么实现的:

到这个代码处,我们已经能够获取到合适的 HandlerMethod 了,我们可以在拦截器内,执行上面的这段核心逻辑代码 ,这样我们就能狗在拦截器初期获取到 HandlerMethod 了。

4.2 修改Filter,使其支持在拦截器内部可以获取到 HandlerMethod

对默认的Sentinel CommonFilter 进行拓展,仿照 DispatchServlet 的实现逻辑,获取 HandlerMethod ,然后根据反射机制,获取到对应的Controller 方法调用引用上声明的 @RequestMapping 注解,然后将注解内容组合成资源名,并返回。

4.3. 覆盖掉 spring-cloud-starter-alibaba-sentinel 的默认实现

想要覆盖掉默认的 spring-cloud-starter-alibaba-sentinel 实现,需要版本 >= 0.2.1.RELEASE,低版本不能通过配置的方式实现覆盖。

## 关闭默认实现
spring.cloud.sentinel.filter.enabled = false

自定义 Configuration :

上述的变更,能够满足我们可以获取到Controller 方法上的注解表示作为 资源名 了。

4.4 优化结果展示

如下图所示,优化过的结果可以看到 大批量的URL请求已经成为 静态的资源名表示,和Controller注解上的@ReuqestMapping表示完全相同: /api/v1/{tenantId}/order/{orderId}/basic

Alibaba Sentinel RESTful 接口流控处理优化

实时监控

Alibaba Sentinel RESTful 接口流控处理优化

簇点链路

5. RESTful参数化进一步优化

上面的流程上,可以看到,我们已经将 RESTful 的PathVariable问题解决了。而实际上,我们可能是希望将部分的PathVariable替换掉,那我们应该怎么做?

Alibaba Sentinel RESTful 接口流控处理优化

如下图所示,如果希望可以根据特定的擦除规则,我们可以拓展一下 UrlCleaner 来实现这方面的定制,来完成对特定 PathVariable 的擦除:

通过这种改造方式,最终的效果如下图所示:

Alibaba Sentinel RESTful 接口流控处理优化

Alibaba Sentinel RESTful 接口流控处理优化

Alibaba Sentinel RESTful 接口流控处理优化


以上所述就是小编给大家介绍的《Alibaba Sentinel RESTful 接口流控处理优化》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Design for Hackers

Design for Hackers

David Kadavy / Wiley / 2011-10-18 / USD 39.99

Discover the techniques behind beautiful design?by deconstructing designs to understand them The term ?hacker? has been redefined to consist of anyone who has an insatiable curiosity as to how thin......一起来看看 《Design for Hackers》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具