Springboot+Redis实现API接口限流的示例代码

网友投稿 987 2022-12-24

Springboot+Redis实现API接口限流的示例代码

Springboot+Redis实现API接口限流的示例代码

添加Redis的jar包.

org.springframework.boot

spring-boot-starter-data-redis

在application.yml中配置redis

spring:

## Redis

redis:

database: 0

host: 127.0.0.1

port: 6379

password:

jedis:

pool:

max-active: 8

max-wait: -1ms

max-idle: 8

min-idle: 0

timeout: 2000ms

添加自定义注解

@Inherited

@Documented

@Target({ElementType.FIELD,ElementType.TYPE,ElementType.METHOD})

@Retention(RetentionPolicy.RUNTIME)

public @interface AccessLimit {

//指定second 时间内 API请求次数

int times() default 4;

// 请求次数的指定时间范围 秒数(redis数据过期时间)

int second() default 10;

}

编写-

import com.ys.xlb.annotation.AccessLimit;

import com.ys.xlb.bean.Code;

import com.ys.xlb.exception.GlobalException;

import com.ys.xlb.utils.IpUtils;

import com.ys.xlb.utils.RequestUtils;

import com.ys.xlb.utils.ResultUtils;

import lombok.extern.slf4j.Slf4j;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Component;

import org.springframework.web.method.HandlerMethod;

import org.springframework.web.servlet.HandlerInterceptor;

import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import java.lang.reflect.Method;

import java.util.concurrent.TimeUnit;

/**

* @ClassName AccessLimitInterceptor

* @description: API请求限流-

* @time 2019-04-20 11:08

**/

@Slf4j

@Component

public class AccessLimitInterceptor implements HandlerInterceptor {

@Resource

private RedisTemplate redisTemplate;

@Override

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

try{

// Handler 是否为 HandlerMethod 实例

if(handler instanceof HandlerMethod){

// 强转

HandlerMethod handlerMethod = (HandlerMethod) handler;

// 获取方法

Method method = handlerMethod.getMethod();

// 是否有AccessLimit注解

if(!method.isAnnotationPresent(AccessLimit.class)){

return true;

}

// 获取注解内容信息

AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);

if(accessLimit == null){

return true;

}

int times = accessLimit.times();//请求次数

int second = accessLimit.second();//请求时间范围

//根据 IP + API 限流

String key = IpUtils.getIpAddr(request) + request.getRequestURI();

//根据key获取已请求次数

Integer maxTimes = redisTemplate.opsForValue().get(key);

if(maxTimes == null){

//set时一定要加过期时间

redisTemplate.opsForValue().set(key, 1, second, TimeUnit.SECONDS);

}else if(maxTimes < times){

redisTemplate.opsForValue().set(key, maxTimes+1, second, TimeUnit.SECONDS);

}else{

// 30405 API_REQUEST_TOO_MUCH 请求过于频繁

RequestUtils.out(respohttp://nse, ResultUtils.error(Code.API_REQUEST_TOO_MUCH));

return false;

}

}

}catch (Exception e){

log.error("API请求限流拦截异常,请检查Redis是否开启!",e);

throw new GlobalException(Code.BAD_REQUEST,e.getMessage());

}

return true;

}

@Override

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

}

@Override

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

}

}

方法中的IP工具类方法

/**

* IpUtils工具类方法

* 获取真实的ip地址

* @param request

* @return

*/

public static String getIpAddr(HttpServletRequest request) {

String ip = request.getHeader("X-Forwarded-For");

if(org.apache.commons.lang.StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){

//多次反向代理后会有多个ip值,第一个ip才是真实ip

int index = ip.indexOf(",");

if(index != -1){

return ip.substring(0,index);

}else{

return ip;

}

}

ip = request.getHeader("X-Real-IP");

if(UkCgucIDorg.apache.commons.lang.StringUtils.isNotEmpty(ip) && !"unKnown".equalsIgnoreCase(ip)){

return ip;

}

return request.getRemoteAddr();

}

RequestUtils.out()方法

/**

* @Title: out

* @Description: response输出jsON数据

* @param response : 响应请求

* @param object: object

* @return void

**/

public static void out(ServletResponse response, Object object){

PrintWriter out = null;

try {

response.setContentType("application/json;charset=UTF-8");

response.setCharacterEncoding("UTF-8");

out = response.getWriter();

out.println(JSONObject.fromObject(resultMap).toString());

} catch (Exception e) {

log.error("输出JSON报错!"+e);

}finally{

if(null != out){

out.flush();

out.close();

}

}

}

配置-

@Configuration

public class ApplicationConfig implements WebMvcConfigurer {

//这里需要注入- 否则无法获取到-注入的RedisTemplate redisTemplate;

@Bean

public AccessLimitInterceptor accessLimitInterceptor(){

return new AccessLimitInterceptor();

}

/**

* 配置-

* @author lance

* @param registry

*/

@Override

public void addInterceptors(InterceptorRegistry registry) {

registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html","/user/login");

//API限流拦截

registry.addInterceptor(accessLimitInterceptor()).addPathPatterns("/**").excludePathPatterns("/static/**","/login.html");

}

}

配置-的类中必须先注入这个-否则无法获取到-注入的RedisTemplate redisTemplate

使用注解

/**

* @Title: selectAll

* @UkCgucIDDescription: 查询文章信息

**/

@AccessLimit(times = 5)

@RequestMapping(value = "selectAll" , method = {RequestMethod.GET,RequestMethod.POST})

//GetMapping(value = "selectAll")

public ResultBody selectAll(Article article) {

return articleService.selectAll(article);

}

请求测试

时间间隔为默认的10s, 10s内请求第6次出现此返回值,完成.

参考博客:

https://blog.csdn-/zrg523/article/details/82185088

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:绵阳一体化政务服务平台(绵阳一体化政务服务平台app)
下一篇:智能车载终端系统下载(最新智能车载系统)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~