SpingBoot简单限流插件开发
一、限流方案
1、 Google的Guava工具包中就提供了一个限流工具类——RateLimiter,本文也是通过使用该工具类来实现限流功能。RateLimiter是基于“令牌通算法”来实现限流的。
2、目前返回信息是 json 格式的数据,如果需要 xml ,可以将返回的类型放到 配置文件中,根据需要定制
3、本文只给出核心代码,详细代码,前往码云查看:https://gitee.com/wsy_13926404489/spring-boot-learning
二、限流器实现
1、导入 jar 包
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>23.0</version> </dependency>
2、继承WebMvcConfigurerAdapter来添加自定义拦截器,主要实现 addInterceptors(InterceptorRegistry registry) 方法
@Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(limitInterceptor) .addPathPatterns("/**"); //.excludePathPatterns() 不拦截路径 }
3、自定义拦截器,实现 HandlerInterceptor 接口,并在拦截器中实现限流
/** * 处理请求之前(保证业务系统的稳定) * @param request * @param response * @param handler * @return * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 统一设置字符编码 (中文、UTF-8) response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); response.setContentType("text/html; charset=UTF-8"); // 限流 boolean limit = limiter.tryAcquire(); if(!limit){ int code = Optional.ofNullable(limitProperties.getCode()).orElse(-1); String msg = Optional.ofNullable(limitProperties.getMsg()).orElse("服务繁忙,请稍后再试!"); String result = "{\"msg\":\"" + msg + "\",\"code\":" + code +"}"; response.getWriter().println(new String(result.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); log.error("请求被拦截,返回消息:{}" , new String(result.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8)); return false; } return true; }
4、实例化 RateLimiter 对象,用于拦截
/** * 限流实例 * @return */ @Bean public RateLimiter getRateLimiter(@Autowired ApplicationContext applicationContext){ //判断是否存在 limit.count 存在获取该值,且该值必须大于 0 ,不存在将该值设置为 int 的最大值(即不限流) int limitCount = DEFAULT_LIMITCOUNT; String limitCountStr = applicationContext.getEnvironment().getProperty("limit.count"); if(!StringUtils.isEmpty(limitCountStr) && !Objects.equals(limitCountStr.trim(), "0")){ if(Integer.parseInt(limitCountStr.trim()) > 0){ limitCount = Integer.parseInt(limitCountStr.trim()); } } log.info("配置的最大QPS: {}", limitCount); return RateLimiter.create(limitCount); }
5、将一些入参信息放到配置文件中,根据使用时需要
@Data @ConfigurationProperties(prefix = "limit") @Component public class LimitProperties { // 每秒产生别的令牌数 private Integer count; // 超时返回的消息(快速失败消息) private String msg; //返回给前端的编码() private Integer code; }
三、在项目中使用
1、引入组件的 jar
<dependency> <groupId>org.example</groupId> <artifactId>limit-spring-boot-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
2、添加必要的参数配置
limit.count # 最大 QPS limit.msg # 超时后返回给调用者的消息说明 limit.code # 超时返回调用者的响应码(不是 httpCode、不管请求是否拦截 httpCode 都是返回 200)
3、新建一个 controller ,简单的接口即可,如下:
@RestController
public class LimitCtrl {
@GetMapping("/limit")
public JSONObject limit(){
JSONObject result = new JSONObject();
result.put("code", 0);
result.put("msg", "请求成功");
return result;
}
}
4、使用 JMeter 对测试接口进行压测,返回消息如下:
{"msg":"服务繁忙,请稍后再试!","code":1}
5、查看日志:
2021-05-24 14:38:42.440 ERROR 7976 --- [nio-8080-exec-7] c.w.limit.interceptor.LimitInterceptor : 请求被拦截,返回消息:{"msg":"服务异常,请稍后再试!","code":1}
项目路径:https://gitee.com/wsy_13926404489/spring-boot-learning.git