>데이터 베이스 >Redis >Redis에서 전류 제한기를 구현하는 세 가지 방법(요약 공유)

Redis에서 전류 제한기를 구현하는 세 가지 방법(요약 공유)

WBOY
WBOY앞으로
2022-09-08 17:50:322422검색

추천 학습: Redis 비디오 튜토리얼

방법 1: Redis 기반 Setnx 작업

Redis의 분산 잠금을 사용할 때 CAS(비교 및 교체)에서 setnx 명령에 의존한다는 것을 모두가 알고 있습니다. 만료 관행(만료)은 지정된 키에 대해 동시에 설정됩니다. 현재 제한의 주요 목적은 단위 시간 내에 N개의 요청만 내 코드 프로그램에 액세스하도록 허용하는 것입니다. 따라서 setnx를 사용하면 이 기능을 쉽게 얻을 수 있습니다.

예를 들어 10초 내에 20개의 요청을 제한해야 한다면 nx를 설정할 때 만료 시간을 10으로 설정할 수 있습니다. 요청된 setnx 수가 20에 도달하면 현재 제한 효과가 달성됩니다. 코드는 비교적 간단하므로 표시하지 않습니다.

물론 이 접근 방식에는 많은 단점이 있습니다. 예를 들어 1~10초를 계산할 때 N초 내에 M개의 요청을 계산해야 한다면 N을 유지해야 합니다. Redis Key 및 기타 문제에서.

특정 구현에서는 인터셉터 HandlerInterceptor:

public class RequestCountInterceptor implements HandlerInterceptor {

    private LimitPolicy limitPolicy;

    public RequestCountInterceptor(LimitPolicy limitPolicy) {
        this.limitPolicy = limitPolicy;
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!limitPolicy.canDo()) {
            return false;
        }
        return true;
    }
}

사용을 고려할 수 있습니다. 동시에 LimitConfiguration:

@Configuration
public class LimitConfiguration implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new RequestCountInterceptor(new RedisLimit1())).addPathPatterns("/my/increase");
    }
}

구성을 추가합니다. 이런 식으로 /my/increase 요청이 컨트롤러에 도달하기 전에 항상 흐름은 RedisLimit1 정책에 따라 제한됩니다. 원래 컨트롤러 코드는 수정할 필요가 없습니다:

@RestController
@RequestMapping("my")
public class MyController {
    int i = 0;
    @RequestMapping("/increase")
    public int increase() {
        return i++;
    }
}

특정 전류 제한 논리 코드는 RedisLimit1 클래스에 있습니다:

/**
* 方法一:基于Redis的setnx的操作
*/
public class RedisLimit1 extends LimitPolicy {

    static {
        setNxExpire();
    }

    private static boolean setNxExpire() {
        SetParams setParams = new SetParams();
        setParams.nx();
        setParams.px(TIME);
        String result = jedis.set(KEY, COUNT + "", setParams);


        if (SUCCESS.equals(result)) {
            return true;
        }
        return false;
    }

    @Override
    public boolean canDo() {

        if (setNxExpire()) {
            //设置成功,说明原先不存在,成功设置为COUNT
            return true;
        } else {
            //设置失败,说明已经存在,直接减1,并且返回
            return jedis.decrBy(KEY, 1) > 0;
        }
    }
}

public abstract class LimitPolicy {
    public static final int COUNT = 10; //10 request
    public static final int TIME= 10*1000 ; // 10s
    public static final String SUCCESS = "OK";
    static Jedis jedis = new Jedis();
    abstract boolean canDo();
}

이러한 방식으로 달성되는 효과는 최대 10개입니다. 초당.

방법 2: Redis를 기반으로 한 데이터 구조 zset

실제로 전류 제한과 관련된 가장 중요한 것은 슬라이딩 윈도우입니다. 위에서도 1-10이 2-11이 되는 방식을 언급했습니다. 실제로 시작 값과 끝 값은 모두 각각 +1입니다.
Redis의 목록 데이터 구조를 사용하면 이 기능을 쉽게 구현할 수 있습니다.
요청을 zset 배열로 생성할 수 있습니다. 각 요청이 들어올 때 값은 고유하게 유지되며 UUID를 사용하여 생성될 수 있으며 점수는 현재 시간 스탬프 표현을 사용하여 생성됩니다. 점수를 사용하여 현재 타임스탬프 내의 요청 수를 계산할 수 있기 때문입니다. zset 데이터 구조는 또한 zrange 메소드를 제공하므로 두 타임스탬프 내에 요청 수를 쉽게 얻을 수 있습니다

/**
* 方法二:基于Redis的数据结构zset
*/
public class RedisLimit2 extends LimitPolicy {
    public static final String KEY2 = "LIMIT2";

    @Override
    public boolean canDo() {
        Long currentTime = new Date().getTime();
        System.out.println(currentTime);
        if (jedis.zcard(KEY2) > 0) { // 这里不能用get判断,会报错:WRONGTYPE Operation against a key holding the wrong kind of value
            Integer count = jedis.zrangeByScore(KEY2, currentTime - TIME, currentTime).size(); // 注意这里使用zrangeByScore,以时间作为score。zrange key start stop 命令的start和stop是序号。
            System.out.println(count);
            if (count != null && count > COUNT) {
                return false;
            }
        }
        jedis.zadd(KEY2, Double.valueOf(currentTime), UUID.randomUUID().toString());
        return true;

    }
}

위 코드는 슬라이딩 윈도우 효과를 달성하고 N초마다 최대 M개의 요청이 있는지 확인할 수 있습니다. , 단점은 zset의 데이터 구조가 점점 더 커진다는 것입니다. 구현 방법은 비교적 간단합니다.

방법 3: Redis 기반 토큰 버킷 알고리즘

전류 제한에 관해서라면 토큰 버킷 알고리즘을 언급해야 합니다. 토큰 버킷 알고리즘에는 입력 속도와 출력 속도가 언급되어 있습니다. 출력 속도가 입력 속도보다 크면 트래픽 제한이 초과됩니다. 즉, 요청에 액세스할 때마다 Redis에서 토큰을 얻을 수 있습니다. 토큰을 얻으면 한도를 초과하지 않은 경우 결과는 반대가 됩니다.
위의 아이디어를 바탕으로 Redis의 List 데이터 구조를 결합하여 이러한 코드를 쉽게 구현할 수 있습니다. 이는 토큰을 얻기 위해 List의 leftPop을 사용하는 간단한 구현일 뿐입니다.

먼저 예약된 작업을 구성하고 redis 목록의 rpush 메서드를 통해 매초마다 토큰을 삽입합니다.

@Configuration      //1.主要用于标记配置类,兼备Component的效果。
@EnableScheduling   // 2.开启定时任务
public class SaticScheduleTask {
    //3.添加定时任务
    @Scheduled(fixedRate = 1000)
    private void configureTasks() {
        LimitPolicy.jedis.rpush("LIMIT3", UUID.randomUUID().toString());
    }
}

현재가 제한되면 목록의 lpop 메서드를 통해 redis에서 해당 토큰을 가져옵니다. , 요청이 실행될 수 있습니다. :

/**
* 方法三:令牌桶
*/
public class RedisLimit3 extends LimitPolicy {
    public static final String KEY3 = "LIMIT3";

    @Override
    public boolean canDo() {

        Object result = jedis.lpop(KEY3);
        if (result == null) {
            return false;
        }
        return true;
    }
}

추천 학습:Redis 비디오 튜토리얼

위 내용은 Redis에서 전류 제한기를 구현하는 세 가지 방법(요약 공유)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 jb51.net에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제