Redisベースの分散型ストリーム制限の実装

7125 ワード

本節ではRedisに基づいてシステムの限流制御を実現する.

Luaスクリプト準備

local val = redis.call('incr', KEYS[1])
local ttl = redis.call('ttl', KEYS[1])

redis.log(redis.LOG_NOTICE, "incr "..KEYS[1].." "..val);
if val == 1 then
    redis.call('expire', KEYS[1], tonumber(ARGV[1]))
else
    if ttl == -1 then
        redis.call('expire', KEYS[1], tonumber(ARGV[1]))
    end
end

if val > tonumber(ARGV[2]) then
    return 0
end

return 1

着信したkeyに対してincr操作を行い、keyが初めて生成された場合、タイムアウト時間ARGVを設定する[1].
ttlは、タイムアウト時間が設定されておらず、長時間存在する場合に、あるkeyが行う保護の判断を防止するためである.
現在のkeyのvalが制限回数ARGVを超えているか否かを判断する[2].

Redisクライアント

public Long limit(String key) {
    return redisClient.eval(key, expire, reqCount);
}

luaスクリプトをロードし、Redis呼び出しのインタフェースを外部に提供します.

AOP

@Before("@annotation(com.snatch.deal.shop.common.annotations.RateLimit)")
public void before(JoinPoint pjp) throws Throwable {
    Signature sig = pjp.getSignature();
    if (!(sig instanceof MethodSignature)) {
        throw new IllegalArgumentException("          ");
    }
    MethodSignature msig = (MethodSignature) sig;
    String methodName = pjp.getTarget().getClass().getName() + "." + msig.getName();
    String limitKey = Md5Utils.encrypt(methodName);

    if (rateLimiter.limit(limitKey) != 1){
        throw new OverLimitException("      ");
    }
}
@Target({ElementType.METHOD})  
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {
    String value() default "";
}

AOPのbeforeインプリメンテーションメソッドの実行前のブロックにより,RateLimitを注釈したすべてのメソッドがキャッシュされてストリーム制限される.いったん限流をトリガすると、外に異常が放出される.

統合例外処理

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(OverLimitException.class)
    @ResponseBody
    public String OverLimitExceptionHandler(OverLimitException ole){
        return ole.getMessage();
    }
}

ControllerAdviceで対応する異常の処理を行います.
詳細コードhttps://gitee.com/lawlet/shop