最近客户现场的实施工程师反馈,系统在某一时间会特别的卡顿,导致医生无法进行操作,特别是在业务系统对接我们自己的接口程序的时候,老是访问超时导致无法进行下一把操作。
经过追踪发现,这一时间段中,接口程序会频繁刷新导致CPU飙升,内存被大批量占用,经过排查日志发现,第三方程序再这一时间段频繁访问同一个接口,几乎是一秒一次,
跟第三方沟通后才知道他们改了接口访问的频次才导致的问题,而我们的接口也没有进行限流的,所以再后续的更新中给接口加上限流。
所以对于此次的处理进行记录。以下是相关的代码:
限流的方式
- 固定窗口
- 滑动窗口
- 漏桶
- 令牌桶(此次所展示的算法)
使用的是Redis + Lua方式
Lua介绍
Lua官网
- 特性:
轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。 - 其它特性:
- 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
- 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
- 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
- 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。
自定义注解:RateLimiter.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
(ElementType.METHOD)
(RetentionPolicy.RUNTIME)
public RateLimiter {
/**
* 限流key
*/
String key() default "rate_limit:";
/**
* 限流时间,单位秒
*/
int time() default 60;
/**
* 限流次数
*/
int count() default 60;
/**
* 返回类型 0 为xml 1为 json
* @return
*/
String resultType() default "0";
/**
* 限流类型
*/
LimitType limitType() default LimitType.DEFAULT;
/**
* 厂商返回类型
* @return
*/
LimitResultType limitResultType() default LimitResultType.NAME_DEFAULT;
}
AOP切面实现:RateLimiterAspect.java
1 | /** |
以上就是防止重复提交的AOP方法,具体使用如图所示:
运行
单次访问
缓存显示如图:
后端打印出来的令牌桶总数量:
模拟多次访问:
接口我次数设置的是5次时间是2秒
接口访问的结果
成功详情:
失败详情:
后台显示:
综合结果得知成功了5次,失败了1次,所以限流成功了,因为我还设置了缓存key的过期为一分钟,下次再次访问的话会重新构建的,具体的话根据实际业务进行配置;
结束
此次只展示了限流的令牌桶的算法,这只是其中解决方法的其中之一,本次使用到的是Redis缓存,其他方式如:JUC中的原子类用自旋+cas实现、guava的限流器(synchronized实现),
具体使用那种算法需要根据自身的业务进行选择,还有其他的算法后面有时间了再进行展示。