写在前面:暑期课云原生(云原神)期末大作业的bonus部分需要实现统一限流机制,组长大懒狗不想做交给我完成这个部分,期间踩了很多坑,故有了这篇文章。
一、导入依赖包:
在pom.xml
里导入以下依赖包,主要用了bucket4j:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <dependency> <groupId>org.redisson</groupId> <artifactId>redisson-spring-boot-starter</artifactId> <version>3.17.0</version> </dependency> <dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-core</artifactId> <version>7.6.0</version> </dependency>
<dependency> <groupId>com.github.vladimir-bukhtoyarov</groupId> <artifactId>bucket4j-jcache</artifactId> <version>7.6.0</version> </dependency>
|
记住需要导入jcache
包。
二、配置部分
在你的项目配置文件中配置redis
需要的配置(这里我的配置文件是properties格式):
1 2 3 4 5 6
| spring.redis.host=your_server_host_ip
spring.redis.port=6379 spring.redis.password=your_redis_password spring.redis.timeout=2000ms spring.redis.database=0
|
- 如果没有设置密码的话不用加上password的配置,但是不安全建议加上。
- 如果你的redis是开在另一台服务器上的话,记得开放那台服务器的6379端口。
三、代码部分
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| import io.github.bucket4j.Bandwidth; import io.github.bucket4j.Bucket; import io.github.bucket4j.BucketConfiguration; import io.github.bucket4j.Refill; import io.github.bucket4j.distributed.proxy.ProxyManager; import io.github.bucket4j.grid.jcache.JCacheProxyManager; import org.redisson.config.Config; import org.redisson.jcache.configuration.RedissonConfiguration; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration;
import javax.cache.CacheManager; import javax.cache.Caching; import java.time.Duration;
@Configuration public class RedisConfig {
@Value("${spring.redis.host:your_server_host_ip}") private String redisHost;
@Value("${spring.redis.port:6379}") private int redisPort;
@Value("${spring.redis.password:your_redis_password}") private String redisPassword;
@Bean public Config config() { Config config = new Config(); String address = "redis://" + redisHost + ":" + redisPort; config.useSingleServer() .setAddress(address) .setPassword(redisPassword); return config; }
@Bean public CacheManager cacheManager(Config config) { try { CacheManager cacheManager = Caching.getCachingProvider("org.redisson.jcache.JCachingProvider") .getCacheManager(); cacheManager.createCache("cache", RedissonConfiguration.fromConfig(config)); return cacheManager; } catch (Exception e) { e.printStackTrace(); throw e; } }
@Bean public ProxyManager<String> proxyManager(CacheManager cacheManager) { try { ProxyManager<String> manager = new JCacheProxyManager<>(cacheManager.getCache("cache")); return manager; } catch (Exception e) { e.printStackTrace(); throw e; } }
@Bean public Bucket bucket(ProxyManager<String> proxyManager, CacheManager cacheManager) { try { javax.cache.Cache<String, Object> cache = cacheManager.getCache("cache"); if (cache != null) cache.clear();
String bucketKey = "rate_limit:hello"; Bandwidth limit = Bandwidth.classic(40, Refill.greedy(40, Duration.ofMinutes(1))); BucketConfiguration configuration = BucketConfiguration.builder() .addLimit(limit) .build(); Bucket bucket = proxyManager.builder().build(bucketKey, () -> configuration); return bucket; } catch (Exception e) { e.printStackTrace(); throw e; } } }
|
错误示范:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| @Configuration public class RedisConfig {
@Bean public RedisTemplate<String, Integer> redisTemplate(RedisConnectionFactory redisConnectionFactory) { RedisTemplate<String, Integer> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer()); return template; }
@Bean public Bucket bucket(RedisTemplate<String, Integer> redisTemplate) { Bandwidth limit = Bandwidth.classic(20, Refill.greedy(20, Duration.ofMinutes(1))); return Bucket.builder().addLimit(limit).build(); } }
|
虽然没有爆红,但是实际上并没有用到redisTemplate
,尽管仍然能起到限流的作用,但是使用的是bucket的本地限流功能,并不是使用了redis
的统一限流。
四、检查redis是否存在bucket
使用命令redis-cli -h your_server_host_ip -p 6379 -a your_redis_password
来连接你的redis
(可能要先下载工具redis-tools
)。
查询后的结果应该如下:

如果到这里都没有问题的话就可以写脚本开始测试你的限流机制是否正确了。