Maison  >  Article  >  base de données  >  Trois façons d'implémenter un limiteur de courant dans Redis (partage de résumé)

Trois façons d'implémenter un limiteur de courant dans Redis (partage de résumé)

WBOY
WBOYavant
2022-09-08 17:50:322331parcourir

Apprentissage recommandé : Tutoriel vidéo Redis

Méthode 1 : Opération Setnx basée sur Redis

Lorsque nous utilisons le verrou distribué de Redis, tout le monde sait que nous nous appuyons sur l'instruction setnx, dans CAS ( Compare and swap), la pratique d'expiration (expire) est définie pour la clé spécifiée en même temps. Notre objectif principal de limitation de courant est d'avoir et seul un nombre N de requêtes peut accéder à mon programme de code dans le temps unitaire. Donc, s'appuyer sur setnx peut facilement réaliser cette fonction.

Par exemple, si nous devons limiter 20 requêtes dans un délai de 10 secondes, nous pouvons alors définir le délai d'expiration à 10 lors du réglage de nx. Lorsque le nombre de setnx demandé atteint 20, l'effet de limitation actuel sera atteint. Le code est relativement simple et ne sera pas affiché.

Bien sûr, cette approche présente de nombreux inconvénients. Par exemple, lorsque l'on compte 1 à 10 secondes, il est impossible de compter 2 à 11 secondes. Si nous devons compter M requêtes en N secondes, alors nous devons conserver N. dans notre clé Redis et d'autres problèmes.

Dans l'implémentation spécifique, vous pouvez envisager d'utiliser l'intercepteur 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;
    }
}

En même temps, ajoutez une configuration LimitConfiguration :

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

De cette façon, à chaque fois avant que la requête /my/increase n'atteigne le Controller, le flux est limité selon la politique RedisLimit1. Le contrôleur d'origine Le code n'a pas besoin d'être modifié :

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

Le code logique de limitation de courant spécifique est dans la classe 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();
}

Un effet obtenu de cette manière est un maximum de 10 requêtes. par seconde.

Méthode 2 : Structure de données zset basée sur Redis

En fait, la chose la plus importante impliquée dans la limitation de courant est la fenêtre glissante. Il est également mentionné ci-dessus comment 1-10 devient 2-11. En fait, la valeur de départ et la valeur de fin sont toutes deux de +1 chacune.
Si nous utilisons la structure de données de liste de Redis, nous pouvons facilement implémenter cette fonction
Nous pouvons créer la requête dans un tableau zset Lorsque chaque requête arrive, la valeur reste unique et peut être générée à l'aide de l'UUID, tandis que le score peut l'être. généré en utilisant l'heure actuelle. Représentation du tampon, car le score que nous pouvons utiliser pour calculer le nombre de requêtes dans l'horodatage actuel. La structure de données zset fournit également la méthode zrange afin que nous puissions facilement obtenir le nombre de requêtes dans deux horodatages

/**
* 方法二:基于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;

    }
}

Le code ci-dessus peut obtenir l'effet de fenêtres coulissantes et garantir qu'il y a au plus M requêtes toutes les N secondes. l'inconvénient est que la structure de données de zset deviendra de plus en plus grande. La méthode de mise en œuvre est relativement simple.

Méthode 3 : Algorithme de compartiment de jetons basé sur Redis

En ce qui concerne la limitation de courant, nous devons mentionner l'algorithme de compartiment de jetons. L'algorithme du compartiment à jetons mentionne le débit d'entrée et le débit de sortie. Lorsque le débit de sortie est supérieur au débit d'entrée, la limite de trafic est dépassée. C'est-à-dire que chaque fois que nous accédons à une requête, nous pouvons obtenir un jeton de Redis. Si nous obtenons le jeton, cela signifie que la limite n'a pas été dépassée. Si nous ne pouvons pas l'obtenir, le résultat sera le contraire.
En nous appuyant sur les idées ci-dessus, nous pouvons facilement implémenter un tel code en combinant la structure de données List de Redis. Il s'agit simplement d'une implémentation simple qui s'appuie sur le leftPop de List pour obtenir le jeton.

Configurez d'abord une tâche planifiée et insérez un token toutes les secondes via la méthode rpush de la liste redis :

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

Lorsque le courant est limité, obtenez le token correspondant auprès de redis via la méthode lpop de la liste Si l'acquisition est réussie. , la requête peut être exécutée :

/**
* 方法三:令牌桶
*/
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;
    }
}

Apprentissage recommandé :Tutoriel vidéo Redis

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer