更多文章分享可以到我的个人博客查看:云端小站
限流 1、概念2、限流步骤 2.1、SpringBoot中集成Redis 2.1.1、引入Redis依赖限流是流量限速(Rate Limit)的简称,是指只允许指定的事件进入系统,超过的部分将被拒绝服务、排队或等待、降级等处理。
对于server服务而言,限流为了保证一部分的请求流量可以得到正常的响应,总好过全部的请求都不能得到响应,甚至导致系统雪崩。
2.1.2、在application.yml中配置Redisorg.springframework.boot spring-boot-starter-data-redis
#Redis配置
spring:
redis:
database: 0 #Redis索引0~15,默认为0
host: 127.0.0.1
port: 6379
password: #密码(默认为空)
timeout: 2000 #连接超时时间(毫秒)
lettuce: # 这里标明使用lettuce配置
pool:
max-active: 8 #连接池最大连接数(使用负值表示没有限制)
max-wait: -1ms #连接池最大阻塞等待时间(使用负值表示没有限制)
max-idle: 5 #连接池中的最大空闲连接
min-idle: 0 #连接池中的最小空闲连接
2.1.3、配置RedisTemplate
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
@SuppressWarnings("all")
public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
//自定义Jackson序列化配置
Jackson2JsonRedisSerializer jsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
jsonRedisSerializer.setObjectMapper(objectMapper);
//key使用String的序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
//hash的key也是用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
//value的key使用jackson的序列化方式
template.setValueSerializer(jsonRedisSerializer);
//hash的value也是用jackson的序列化方式
template.setHashValueSerializer(jsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
2.2、实现限流
2.2.1、编写自定义AccessLimit注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface GlobalLimit {
//方法名称 默认为空
String function() default "";
// 秒数
int seconds() default 1;
// 最大访问次数
int maxCount();
}
2.2.2、编写拦截器
限流的思路
- 通过路径:接口的路径作为key,访问次数为value的方式(可以根据业务需求更改key的值,实现其他效果)
- 每次访问的时候判断key是否存在,是否count超过了限制的访问次数
- 若访问超出限制,则应response返回msg:请求过于频繁给前端予以展示
import cn.hutool.json.JSONUtil;
import com.cqyti.wxzx.annotation.GlobalLimit;
import com.cqyti.wxzx.pojo.entity.ResultBody;
import com.cqyti.wxzx.util.RedisUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;
public class GlobalLimitInterceptor implements HandlerInterceptor {
private final Log log = LogFactory.getLog(GlobalLimitInterceptor.class);
//执行请求前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//如果不是映射到方法上的直接放行
if (!(handler instanceof HandlerMethod)){
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) handler;
//获取注解
GlobalLimit globalLimit = handlerMethod.getMethodAnnotation(GlobalLimit.class);
//没有注解,直接放行
if (globalLimit == null){
return true;
}
//获取注解属性
int seconds = globalLimit.seconds();
int maxCount = globalLimit.maxCount();
//拼接key
String key = request.getServletPath();
String count = RedisUtil.get(key);
log.info("=============全局限流"+handlerMethod.getMethod().getName()+":"+count+"=============");
if (count == null){
//如果没有记录则添加到redis
RedisUtil.set(key,"1",seconds, TimeUnit.SECONDS);
return true;
}
if (Integer.parseInt(count) <= maxCount){
count = String.valueOf(Integer.parseInt(count)+1);
RedisUtil.set(key,count,seconds,TimeUnit.SECONDS);
return true;
}
if (Integer.parseInt(count) >= maxCount){
response.setStatus(401);
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json;character=utf-8");
ResultBody resultBody = ResultBody.error(CodeConfig.ACCESS_LIMIT).message("当前访问人数过多,请稍后再试");
response.getWriter().write(JSONUtil.toJsonStr(resultBody));
return false;
}
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 {
}
}
2.2.3、添加拦截器到全局拦截器中
将限流拦截器加入到全局拦截器中、并设置拦截路径与白名单(此处省略)



