限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务。
前几天在DD的公众号,看了一篇关于使用 瓜娃 实现单应用限流的方案 --》原文,参考《redis in action》 实现了一个jedis版本的,都属于业务层次限制。 实际场景中常用的限流策略:
Nginx接入层限流
按照一定的规则如帐号、IP、系统调用逻辑等在Nginx层面做限流
业务应用系统限流
通过业务代码控制流量这个流量可以被称为信号量,可以理解成是一种锁,它可以限制一项资源最多能同时被多少进程访问。
代码实现
import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; import redis.clients.jedis.ZParams; import java.util.List; import java.util.UUID; /** * @email wangiegie@gmail.com * @data 2017-08 */ public class RedisRateLimiter { private static final String BUCKET = "BUCKET"; private static final String BUCKET_COUNT = "BUCKET_COUNT"; private static final String BUCKET_MONITOR = "BUCKET_MONITOR"; static String acquireTokenFromBucket( Jedis jedis, int limit, long timeout) { String identifier = UUID.randomUUID().toString(); long now = System.currentTimeMillis(); Transaction transaction = jedis.multi(); //删除信号量 transaction.zremrangeByScore(BUCKET_MONITOR.getBytes(), "-inf".getBytes(), String.valueOf(now - timeout).getBytes()); ZParams params = new ZParams(); params.weightsByDouble(1.0,0.0); transaction.zinterstore(BUCKET, params, BUCKET, BUCKET_MONITOR); //计数器自增 transaction.incr(BUCKET_COUNT); List<Object> results = transaction.exec(); long counter = (Long) results.get(results.size() - 1); transaction = jedis.multi(); transaction.zadd(BUCKET_MONITOR, now, identifier); transaction.zadd(BUCKET, counter, identifier); transaction.zrank(BUCKET, identifier); results = transaction.exec(); //获取排名,判断请求是否取得了信号量 long rank = (Long) results.get(results.size() - 1); if (rank < limit) { return identifier; } else {//没有获取到信号量,清理之前放入redis 中垃圾数据 transaction = jedis.multi(); transaction.zrem(BUCKET_MONITOR, identifier); transaction.zrem(BUCKET, identifier); transaction.exec(); } return null; } }
调用
测试接口调用
@GetMapping("/") public void index(HttpServletResponse response) throws IOException { Jedis jedis = jedisPool.getResource(); String token = RedisRateLimiter.acquireTokenFromBucket(jedis, LIMIT, TIMEOUT); if (token == null) { response.sendError(500); }else{ //TODO 你的业务逻辑 } jedisPool.returnResource(jedis); }
优化
使用拦截器 + 注解优化代码
拦截器
@Configuration static class WebMvcConfigurer extends WebMvcConfigurerAdapter { private Logger logger = LoggerFactory.getLogger(WebMvcConfigurer.class); @Autowired private JedisPool jedisPool; public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new HandlerInterceptorAdapter() { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HandlerMethod handlerMethod = (HandlerMethod) handler; Method method = handlerMethod.getMethod(); RateLimiter rateLimiter = method.getAnnotation(RateLimiter.class); if (rateLimiter != null){ int limit = rateLimiter.limit(); int timeout = rateLimiter.timeout(); Jedis jedis = jedisPool.getResource(); String token = RedisRateLimiter.acquireTokenFromBucket(jedis, limit, timeout); if (token == null) { response.sendError(500); return false; } logger.debug("token -> {}",token); jedis.close(); } return true; } }).addPathPatterns("/*"); } }
定义注解
/** * @email wangiegie@gmail.com * @data 2017-08 * 限流注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimiter { int limit() default 5; int timeout() default 1000; }
使用
@RateLimiter(limit = 2, timeout = 5000) @GetMapping("/test") public void test() { }
并发测试
工具:apache-jmeter-3.2
说明: 没有获取到信号量的接口返回500,status是红色,获取到信号量的接口返回200,status是绿色。
当限制请求信号量为2,并发5个线程:
当限制请求信号量为5,并发10个线程:
资料
基于reids + lua的实现
总结
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持小牛知识库。
本文向大家介绍Redis和Lua实现分布式限流器的方法详解,包括了Redis和Lua实现分布式限流器的方法详解的使用技巧和注意事项,需要的朋友参考一下 主要是依靠 redis + lua 来实现限流器, 使用 lua 的原因是将多条命令合并在一起作为一个原子操作, 无需过多考虑并发. 计数器模式 原理 计数器算法是指在一段窗口时间内允许通过的固定数量的请求, 比如10次/秒, 500次/30秒.
本文向大家介绍使用nginx实现分布式限流的方法,包括了使用nginx实现分布式限流的方法的使用技巧和注意事项,需要的朋友参考一下 1.前言 一般对外暴露的系统,在促销或者黑客攻击时会涌来大量的请求,为了保护系统不被瞬间到来的高并发流量给打垮, 就需要限流 . 本文主要阐述如何用nginx 来实现限流. 听说 Hystrix 也可以, 各位有兴趣可以去研究哈 . 2.首先部署一个对外暴露接口的程序
背景 在很多互联网产品应用中,有些场景需要加锁处理,比如:秒杀,全局递增ID,楼层生成等等。大部分的解决方案是基于DB实现的,Redis为单进程单线程模式,采用队列模式将并发访问变成串行访问,且多客户端对redis的连接并不存在竞争关系。其次Redis提供一些命令SETNX,GETSET,可以方便实现分布式锁机制。 一、使用分布式锁要满足的几个条件: 系统是一个分布式系统(关键是分布式,单机的可以
本文向大家介绍基于jquery实现瀑布流布局,包括了基于jquery实现瀑布流布局的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家介绍了基于jquery实现瀑布流布局的关键代码,分享给大家供大家参考,具体内容如下 效果图: 具体代码: 使用jquery-1.8.3.min.js,waterfall.js代码如下: 希望本文所述对大家学习有所帮助,谢谢大家的阅读。
本文向大家介绍浅析PHP分布式中Redis实现Session的方法,包括了浅析PHP分布式中Redis实现Session的方法的使用技巧和注意事项,需要的朋友参考一下 本文介绍的是PHP分布式中Redis实现Session的方法,下面话不多说,直接先来看两个方法是什么 方法一: 找到配置文件php.ini,修改为下面内容,保存并重启服务 方法二: 直接在代码中加入以下内容: 注:如果配置文件red
使用Redis实现分布式锁 redis命令:set users 10 nx ex 12 原子性命令 //使用uuid,解决锁释放的问题 @GetMapping public void testLock() throws InterruptedException { String uuid = UUID.randomUUID().toString(); Boolean b_loc