Redis+Luaに基づく分散型ストリーム制限
一、Mavneプロジェクトを新規作成し、rateと名付けた.Limiterは,Lombokとguavaの依存性を導入した.
二、rate_Limiterプロジェクトの下にratelimiterという名前の新しいannotationのサブモジュールで、モジュールのpomファイルにredisの依存を追加します.
三、ratelimiter_annotationモジュールのsrc/main/javaディレクトリの下にサービスパッケージを作成し、サービスパッケージの下にAccessLimiterというクラスを作成します.
四、configパッケージを新規作成し、RedisConfigurationという構成クラスを作成する
五、resourcesディレクトリの下でluaスクリプトファイルratelimiterを新規作成する.lua.
六、rate_Limiterプロジェクトにratelimiterを追加testのサブモジュールは、前のスクリプトをテストするために使用されます.ratelimiter_testには以下の依存性が導入されている.
七、ratelimiter_testのsrc/main/javaでcontrollerパッケージを新規作成し、controllerパッケージの下でTestControllerのクラスを作成します.
八、アプリケーションで.propertiesにredisを追加する構成
九、起動クラスを作成し、プロジェクトを起動し、postmanで制限フローの結果をテストします.
十、以上のいくつかのステップを通じて、Redis+Luaに基づくストリーム制限を実現したが、コードはまだ完璧ではない.今、プロジェクトを改造して、カスタムの注釈でプロジェクトのどこでもストリーム制限を実現することができる.
まずratelimiter_annotationモジュールにaopの依存性を導入する.
そしてratelimiter_annotationモジュールにannotationのパッケージを新規作成し、annotationパッケージの下にAccessLimiterという注釈を作成します.
もう1つのaspectのパッケージを作成し、AccessLimiterAspectというクラスを作成します.
これで9つの注釈を使用することができます.TestControllerで新しい方法を追加します.
クラスを起動してプロジェクトを再起動し、testAnnotationインタフェースをテストします.
org.projectlombok
lombok
com.google.guava
guava
29.0-jre
二、rate_Limiterプロジェクトの下にratelimiterという名前の新しいannotationのサブモジュールで、モジュールのpomファイルにredisの依存を追加します.
org.springframework.boot
spring-boot-starter-data-redis
三、ratelimiter_annotationモジュールのsrc/main/javaディレクトリの下にサービスパッケージを作成し、サービスパッケージの下にAccessLimiterというクラスを作成します.
@Service
@Slf4j
public class AccessLimiter {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* DefaultRedisScript , lua ,
* , 。
* 4 (Long, Boolean, List, or deserialized value type)
*/
@Autowired
private DefaultRedisScript rateLimiterLua;
public void limitAccess(String key,Integer limit){
// lua
boolean acquire=redisTemplate.execute(
rateLimiterLua,
Lists.newArrayList(key),
limit.toString());
if (!acquire){
log.error("your access is blocked,key={}",key);
throw new RuntimeException("your access is blocked");
}
}
}
四、configパッケージを新規作成し、RedisConfigurationという構成クラスを作成する
@Configuration
public class RedisConfiguration {
@Bean
public RedisTemplate redisTemplate(
RedisConnectionFactory factory
){
return new StringRedisTemplate(factory);
}
@Bean
public DefaultRedisScript loadRedisScript(){
DefaultRedisScript redisScript=new DefaultRedisScript();
// lua
redisScript.setLocation(new ClassPathResource("ratelimiter.lua"));
//
redisScript.setResultType(java.lang.Boolean.class);
return redisScript;
}
}
五、resourcesディレクトリの下でluaスクリプトファイルratelimiterを新規作成する.lua.
--
-- Created by IntelliJ IDEA.
-- User: wanglei
--
-- lua , , redis ,
-- KEYS、ARGV。
-- KEYS , lua 。
-- , ARGV , , Lua ,
-- ARGV , 。
-- KEYS
local methodKey = KEYS[1]
redis.log(redis.LOG_DEBUG, 'key is', methodKey)
-- ARGV
local limit = tonumber(ARGV[1])
--
local count = tonumber(redis.call('get', methodKey) or "0")
--
if count + 1 > limit then
--
return false
else
--
-- +1
redis.call("INCRBY", methodKey, 1)
--
redis.call("EXPIRE", methodKey, 1)
--
return true
end
六、rate_Limiterプロジェクトにratelimiterを追加testのサブモジュールは、前のスクリプトをテストするために使用されます.ratelimiter_testには以下の依存性が導入されている.
org.springframework.boot
spring-boot-starter-web
${project.groupId}
ratelimiter_annotation
${project.version}
七、ratelimiter_testのsrc/main/javaでcontrollerパッケージを新規作成し、controllerパッケージの下でTestControllerのクラスを作成します.
@RestController
@Slf4j
public class TestController {
@Autowired
private AccessLimiter accessLimiter;
@GetMapping("test")
public String test(){
accessLimiter.limitAccess("ratelimiter-test",1);
return "success";
}
}
八、アプリケーションで.propertiesにredisを追加する構成
spring.redis.database=0
spring.redis.host=localhsot
spring.redis.port=6379
spring.redis.password=root
九、起動クラスを作成し、プロジェクトを起動し、postmanで制限フローの結果をテストします.
@SpringBootApplication
public class RatelimiterTestApplication {
public static void main(String[] args) {
SpringApplication.run(RatelimiterTestApplication.class, args);
}
}
十、以上のいくつかのステップを通じて、Redis+Luaに基づくストリーム制限を実現したが、コードはまだ完璧ではない.今、プロジェクトを改造して、カスタムの注釈でプロジェクトのどこでもストリーム制限を実現することができる.
まずratelimiter_annotationモジュールにaopの依存性を導入する.
org.springframework.boot
spring-boot-starter-aop
そしてratelimiter_annotationモジュールにannotationのパッケージを新規作成し、annotationパッケージの下にAccessLimiterという注釈を作成します.
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AccessLimiter {
int limit();
String methodKey() default "";
}
もう1つのaspectのパッケージを作成し、AccessLimiterAspectというクラスを作成します.
@Slf4j
@Aspect
@Component
public class AccessLimiterAspect {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private DefaultRedisScript rateLimiterLua;
@Pointcut("@annotation(com.wl.annotation.AccessLimiter)")
public void cut(){
log.info("cut");
}
@Before("cut()")
public void before(JoinPoint joinPoint){
//1、 , method key
MethodSignature methodSignature= (MethodSignature) joinPoint.getSignature();
Method method=methodSignature.getMethod();
AccessLimiter annotation=method.getAnnotation(AccessLimiter.class);
if (annotation==null){
return;
}
String key=annotation.methodKey();
Integer limit=annotation.limit();
// methodKey,
if (StringUtils.isEmpty(key)){
Class[] type=method.getParameterTypes();
key=method.getName();
if (type!=null){
String paramTypes= Arrays.stream(type)
.map(Class::getName)
.collect(Collectors.joining(","));
log.info("param types: "+paramTypes);
key+="#"+paramTypes;
}
}
//2、 redis
boolean acquire=redisTemplate.execute(
rateLimiterLua,
Lists.newArrayList(key),
limit.toString());
if (!acquire){
log.error("your access is blocked,key={}",key);
throw new RuntimeException("your access is blocked");
}
}
}
これで9つの注釈を使用することができます.TestControllerで新しい方法を追加します.
@GetMapping("test-annotation")
@com.wl.annotation.AccessLimiter(limit = 1)
public String testAnnotation(){
return "success";
}
クラスを起動してプロジェクトを再起動し、testAnnotationインタフェースをテストします.