微前端架构如何改变企业的开发模式与效率提升
987
2022-12-24
Springboot+Redis实现API接口限流的示例代码
添加Redis的jar包.
在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
@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
@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
使用注解
/**
* @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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~