admin 管理员组

文章数量: 1086019

springboot工程中限流方式

限流,是防止用户恶意刷新接口。常见的限流方式有阿里开源的sentinel、redis等。
1、google的guava,令牌桶算法实现限流
Guava的 RateLimiter提供了令牌桶算法实现:平滑突发限流(SmoothBursty)和平滑预热限流(SmoothWarmingUp)实现。

// RateLimiter提供了两个工厂方法,最终会调用下面两个函数,生成RateLimiter的两个子类。
static RateLimiter create(SleepingStopwatch stopwatch, double permitsPerSecond) {RateLimiter rateLimiter = new SmoothBursty(stopwatch, 1.0 /* maxBurstSeconds */);rateLimiter.setRate(permitsPerSecond);return rateLimiter;
}
static RateLimiter create(SleepingStopwatch stopwatch, double permitsPerSecond, long warmupPeriod, TimeUnit unit,double coldFactor) {RateLimiter rateLimiter = new SmoothWarmingUp(stopwatch, warmupPeriod, unit, coldFactor);rateLimiter.setRate(permitsPerSecond);return rateLimiter;
}

平滑突发限流:使用 RateLimiter的静态方法创建一个限流器,设置每秒放置的令牌数为10个。返回的RateLimiter对象可以保证1秒内不会给超过10个令牌,并且以固定速率进行放置,达到平滑输出的效果。
平滑预热限流:RateLimiter的 SmoothWarmingUp是带有预热期的平滑限流,它启动后会有一段预热期,逐步将分发频率提升到配置的速率。

@RestController
public class HomeController {// 这里的10表示每秒允许处理的量为10个private RateLimiter limiter = RateLimiter.create(10);private RateLimiter limiter2 = RateLimiter.create(2, 1000, TimeUnit.SECONDS);//permitsPerSecond: 表示 每秒新增 的令牌数;warmupPeriod: 表示在从 冷启动速率 过渡到 平均速率 的时间间隔@GetMapping("/test/{name}")public String test(@PathVariable("name") String name) {// 请求RateLimiter, 超过permits会被阻塞final double acquire = limiter.acquire();System.out.println("acquire=" + acquire);//判断double是否为空或者为0if (acquire == 0) {return name;} else {return "操作太频繁";}}@AccessLimit(limit = 2, sec = 10)@GetMapping("/test2/{name}")public String test2(@PathVariable("name") String name) {return name;}
}

2、interceptor+redis根据注解限流

public class AccessLimitInterceptor implements HandlerInterceptor {@Resourceprivate RedisTemplate<String, Integer> redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;Method method = handlerMethod.getMethod();if (!method.isAnnotationPresent(AccessLimit.class)) {return true;}AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);if (accessLimit == null) {return true;}int limit = accessLimit.limit();int sec = accessLimit.sec();String key = IPUtil.getIpAddress(request) + request.getRequestURI();//资源唯一标识Integer maxLimit = redisTemplate.opsForValue().get(key);if (maxLimit == null) {//set时一定要加过期时间redisTemplate.opsForValue().set(key, 1, sec, TimeUnit.SECONDS);} else if (maxLimit < limit) {redisTemplate.opsForValue().set(key, maxLimit + 1, sec, TimeUnit.SECONDS);} else {output(response, "请求太频繁!");return false;}}return true;}public void output(HttpServletResponse response, String msg) throws IOException {response.setContentType("application/json;charset=UTF-8");ServletOutputStream outputStream = null;try {outputStream = response.getOutputStream();outputStream.write(msg.getBytes("UTF-8"));} catch (IOException e) {e.printStackTrace();} finally {outputStream.flush();outputStream.close();}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {@Beanpublic AccessLimitInterceptor accessLimitInterceptor() {return new AccessLimitInterceptor();}@Overridepublic void addInterceptors(InterceptorRegistry registry) {//addPathPatterns 添加拦截规则registry.addInterceptor(accessLimitInterceptor()).addPathPatterns("/**");}@Overridepublic void addViewControllers(ViewControllerRegistry registry) {registry.addViewController("/");}
}
@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();template.setConnectionFactory(redisConnectionFactory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new GenericJackson2JsonRedisSerializer());template.setHashKeySerializer(new GenericJackson2JsonRedisSerializer());template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());template.afterPropertiesSet();return template;}
}

限流方式还有很多,后续继续尝试。

本文标签: springboot工程中限流方式