<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
spring: redis: database: 0 host: localhost port: 6379 lettuce: # 這裏標明使用lettuce配置 pool: max-active: 8 # 鏈接池最大鏈接數 max-wait: -1ms # 鏈接池最大阻塞等待時間(使用負值表示沒有限制 max-idle: 5 # 鏈接池中的最大空閒鏈接 min-idle: 0 # 鏈接池中的最小空閒鏈接 timeout: 10000ms # 鏈接超時時間
方法一:java
Long size = redisTemplate.opsForList().size("apiRequest"); if (size < 1000) { redisTemplate.opsForList().leftPush("apiRequest", System.currentTimeMillis()); } else { Long start = (Long) redisTemplate.opsForList().index("apiRequest", -1); if ((System.currentTimeMillis() - start) < 60000) { throw new RuntimeException("超過限流閾值"); } else { redisTemplate.opsForList().leftPush("apiRequest", System.currentTimeMillis()); redisTemplate.opsForList().trim("apiRequest", -1, -1); } }
核心思路:用一個list來存放一串值,每次請求都把當前時間放進,若是列表長度爲1000,那麼調用就是1000次。若是第1000次調用時的當前時間和最初的時間差小於60s,那麼就是1分鐘裏調用超1000次。不然,就清空列表以前的值redis
方法二:spring
Integer count = (Integer) redisTemplate.opsForValue().get("apiKey"); Integer integer = Optional.ofNullable(count).orElse(0); if (integer > 1000) { throw new RuntimeException("超過限流閾值"); } if (redisTemplate.getExpire("apiKey", TimeUnit.SECONDS).longValue() < 0) { redisTemplate.multi(); redisTemplate.opsForValue().increment("apiKey", 1); redisTemplate.expire("apiKey", 60, TimeUnit.SECONDS); redisTemplate.exec(); } else { redisTemplate.opsForValue().increment("apiKey", 1); }
核心思路:設置key,過時時間爲1分鐘,其值是api這分鐘內調用次數apache
對比:方法一耗內存,限流準確。方法二結果有部分偏差,只限制key存在的這一分鐘內調用次數低於1000次,不表明任意時間段的一分鐘調用次數低於1000api