9. [series] Redis Redis is an advanced application - limiting funnel

Original: 9. [series] Redis Redis is an advanced application - limiting funnel

Funnel current limit is one of the most commonly used method of limiting, as the name suggests, the inspiration comes from the structure of the algorithm funnel (funnel) of.

image.png

Hopper capacity is limited, if the discharge spout blocked, then irrigation has been entered, it will become full, until no longer pretend not to go. If the drain mouth open, the water will flow down, then flow away part of it and can continue to drive irrigation. If you slip into the water at a rate greater than the rate of irrigation, then the funnel never not fill. If the leakage rate is less than the rate of irrigation mouth water, then once the hopper is full, we need to pause and wait for irrigation funnel vacated.

Therefore, the remaining space of the funnel represents the number of this behavior can be continued, the flow rate of the discharge spout represents the maximum allowed frequency of the system behavior. Here we use the code to describe the single funnel algorithm.

public class FunnelRateLimiter {

  static class Funnel {
    int capacity;
    float leakingRate;
    int leftQuota;
    long leakingTs;

    public Funnel(int capacity, float leakingRate) {
      this.capacity = capacity;
      this.leakingRate = leakingRate;
      this.leftQuota = capacity;
      this.leakingTs = System.currentTimeMillis();
    }

    void makeSpace() {
      long nowTs = System.currentTimeMillis();
      long deltaTs = nowTs - leakingTs;
      int deltaQuota = (int) (deltaTs * leakingRate);
      if (deltaQuota < 0) { // 间隔时间太长,整数数字过大溢出
        this.leftQuota = capacity;
        this.leakingTs = nowTs;
        return;
      }
      if (deltaQuota < 1) { // 腾出空间太小,最小单位是1
        return;
      }
      this.leftQuota += deltaQuota;
      this.leakingTs = nowTs;
      if (this.leftQuota > this.capacity) {
        this.leftQuota = this.capacity;
      }
    }

    boolean watering(int quota) {
      makeSpace();
      if (this.leftQuota >= quota) {
        this.leftQuota -= quota;
        return true;
      }
      return false;
    }
  }

  private Map<String, Funnel> funnels = new HashMap<>();

  public boolean isActionAllowed(String userId, String actionKey, int capacity, float leakingRate) {
    String key = String.format("%s:%s", userId, actionKey);
    Funnel funnel = funnels.get(key);
    if (funnel == null) {
      funnel = new Funnel(capacity, leakingRate);
      funnels.put(key, funnel);
    }
    return funnel.watering(1); // 需要1个quota
  }
}

make_space method Funnel funnel object is the core of the algorithm, which is called to trigger leaking, to make room in the funnel before each irrigation will. How much space can make depends on how long later and the rate of flowing water. Funnel objects occupy the space no longer proportional to the frequency and behavior, its footprint is a constant.

The question is, how to funnel distributed algorithms that achieve? You can use to get the basic data structure of Redis?

Funnel objects we observe several fields, we can find the content object by the Funnel field stores a hash structure, will be taken out when irrigation hash field configuration after logic operation, then backfilled into the hash value of the new structure to complete the detection of the frequency of a behavior.

But there is a problem, we can not guarantee the atomicity of the whole process. Hash value from the structure, then the memory operation, then backfill to hash structure, not the three atomization process, the locking means need to be appropriately controlled. Once locked, it means there will be lock failure, lock failed you need to choose to retry or give up.

If the retry, it would cause performance degradation. If we give up, it will affect the user experience. At the same time, the complexity of the code also followed increased a lot. It was a difficult choice, how do we solve this problem? Redis-Cell savior coming!

Redis-Cell

4.0 Redis Redis provides a current limiting module, it is called redis-cell. The module also uses algorithms funnel, and a flow restrictor provides instructions atoms. With this module, limiting the problem is very simple.

The only one instruction module cl.throttle, its parameters and return values are slightly complicated, let's take a look at how this directive the specific use.

> cl.throttle laoqian:reply 15 30 60 1
                      ▲     ▲  ▲  ▲  ▲
                      |     |  |  |  └───── need 1 quota (可选参数,默认值也是1)
                      |     |  └──┴─────── 30 operations / 60 seconds 这是漏水速率
                      |     └───────────── 15 capacity 这是漏斗容量
                      └─────────────────── key laoqian

The above command means to allow a maximum frequency of 30 times per 60s (leak rate), the initial capacity of the hopper 15, that is a reply to continuously start message 15, and then began to leak rate affected. We see this instruction leak rate becomes two parameters instead of a single floating-point number before. Dividing the result with two parameters relative to the expression of a single floating-point leak rate to be more intuitive.

> cl.throttle laoqian:reply 15 30 60
1) (integer) 0   # 0 表示允许,1表示拒绝
2) (integer) 15  # 漏斗容量capacity
3) (integer) 14  # 漏斗剩余空间left_quota
4) (integer) -1  # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒)
5) (integer) 2   # 多长时间后,漏斗完全空出来(left_quota==capacity,单位秒)

Limiting instruction when executed, if rejected, it is necessary to discard or retry. cl.throttle instructions very thoughtful consideration, even retry time are considered good for you, take a direct result of the return value of a fourth array of sleep can be, if you do not block threads can also be asynchronous timed task to try again.

Guess you like

Origin www.cnblogs.com/lonelyxmas/p/12515051.html