高危接口访问熔断机制-阿里云开发者社区

开发者社区> 数据库> 正文

高危接口访问熔断机制

简介:

背景介绍

阿里云ECS实例的停机、重启、释放等操作都属于高风险动作,直接影响到ECS实例本身所承载的计算任务和业务。特别是大批量操作时,更加需要明确停机或释放机器的准确性,一旦批量误操作甚至导致整个网站业务线的崩溃。为了防止造成这种巨大损失的局面,针对高风险操作接口提供一种限流熔断机制,批量操作时可以有效的制止过度的错误停机、重启及释放。

设计方案

ECS实例的停机、重启及释放属于高风险操作,未来随着接口应用的深入,其他接口也可能处于高风险状态,例如安全组ACL授权操作等。为了能够让所有高风险操作都可以使用该熔断机制,所以设计的首要原则是高风险接口与熔断机制本身解耦。
需要统计添加熔断机制接口单位时间内的调用频次,设置合理的熔断上限,在不影响日常合理调用的前提下,还需要考虑正常且正确的批量调用,需要提供临时白名单策略。白名单内的用户不受熔断机制限制,需要认真确认实例信息,以免造成大量错误停机、重启或释放的情况。
针对接口熔断类功能需要添加开关进行控制,并逐个地域进行开放,便于观察各接口的熔断情况和调整接口的熔断上限,通关开关的控制可以应对熔断机制带来的突发状况。比如熔断机制不合理,导致接口调用收到严重限制,没有接口只能通过回滚代码的方式进行恢复,时效性低并且存在风险。
设计方案的基本原则:

  • 需要熔断的接口与限流熔断机制之间解耦合;
  • 提供白名单策略,应对超过熔断上限的合理调用;
  • 提供开关策略统一控制各接口的熔断机制;

注解和切面(Annotation+Spect)的方式实现熔断

在不对需要熔断接口的代码做任何修改的前提下,只需要在接口的方法上添加熔断注解就可以实现熔断限流的作用,熔断功能模块独立,接口本身的逻辑和代码独立,通过添加注解的方式实现高内聚低耦合。
首先,定义一个熔断的注解AccessLimit:

/**
 * 高危接口熔断机制
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AccessLimit {}

通过注解@AccessLimit获取方法参数中调用者的信息,实现一个以@AccessLimit为入口的切面,该切面实现了熔断机制的全部逻辑。以调用者的信息作为Key,利用Tair天生Key/Value存储结构的统计该调用者在当前小时内的调用次数,Tair中提供累加的incr方法:

/**
 * @param namespace
 * @param key
 * @param value
 * @param defaultValue
 * @param expireTime
 * @return
 */
Result<Integer> incr(int namespace, Serializable key, int value, int defaultValue, int expireTime);

Tair统计每个调用者Key值过期之前的调用次数,达到熔断上限时抛出异常信息,提示达到风控上线的信息,并不再进一步调用该方法,达到熔断限流的效果。

Tair中incr方法的过期时间

针对同一个调用者,在过期之前的单位时间内,同一方法调用一次访问次数累加一次。incr方法需要注意的是过期时间参数每次调用都会更新的,这样就会存在问题:
1、如果已经达到访问上线,并可以继续执行incr,那么从最后一次访问开始重新记录过期时间。如果在释放Key之前继续访问,将导致该接口针对该用户无法访问。
2、如果已经达到访问上线,并阻止继续执行incr,那么从最后一次访问开始重新记录过期时间,直到该Key过期才能重新继续访问。
以上两种情况统计同一用户对同一接口的访问次数均不准确。解决该问题的方式需要将调用者的Key与当前时间相结合,熔断机制限制方式为每小时N次,任意小时内任何时间取整到小时都是同一个值,将该值与调用者信息拼接作为CacheKey,一天内同一调用者在任意小时内的CacheKey都不一样,这样即使incr方法中的过期时间每次更新,仍然可以准确统计每小时内的访问上线次数。不会出现以上两种请款带来的时间延迟或持续无法访问的情况。关于Key值处理:

/**
 * 获取 cache key
 * @param key
 * @return
 */
private String getCacheKey(String key) {
    Calendar calendar = Calendar.getInstance();
    String hour = String.valueOf(calendar.get(Calendar.HOUR_OF_DAY));
    StringBuilder cacheKey = new StringBuilder(key);
    cacheKey.append("#");
    cacheKey.append(hour)
    return cacheKey.toString();
}

总结

关于接口限流的熔断机制,利用注解和切面的方式进行实现,不仅设计逻辑明确、容易理解,而且很好的体现了高内聚低耦合的设计思想。结合白名单及开关的策略,使得熔断机制的使用更加广泛、安全、灵活。

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
数据库
使用钉钉扫一扫加入圈子
+ 订阅

分享数据库前沿,解构实战干货,推动数据库技术变革

其他文章