JAVA learning-limited algorithm

1. Introduction to basic knowledge of current limiting

Why do you want to limit the current, I believe I don't need to say more.

  • For example, I go to a restaurant for dinner on weekends, but there are too many people . I can only go to the front desk to get a number and wait until the number reaches me before I can eat in the restaurant. What if there is no current limit in the hotel? When the meal arrives, people rush in, and the restaurant can’t handle the flow of people, it’s easy to have an accident (the restaurant is full of people and there is no way to go. The hotel staff collapsed and they couldn’t handle it)

  • The same goes back to the code world. The number of requests that the server can handle is limited. If the amount of requests is particularly large, we need to limit the flow (either let the request wait, or throw the request)

image

Limiting

In the code world, there are two common algorithms for current limiting:

  • Token bucket algorithm

  • Leaky bucket algorithm

1.1 What is the leaky bucket algorithm

For example, now I have a bucket, and the green one is the capacity that I can hold water. If it exceeds the capacity that I can hold, and then pour water into the bucket, it will overflow (current limit):

image

Bucket

What we can currently know is:

  • The capacity of the bucket is fixed (it is the green one on the picture)

  • If the capacity of the bucket is exceeded, it will overflow (either wait or discard directly)

OK, now we dig a hole in the bucket so that water can flow out of the hole:

image

I dug a hole and water came out of the hole

The size of the opening of the bucket is fixed, so the rate of water flowing out of the opening is also fixed .

So to sum up , there are only two parameters required by the algorithm :

  • Bucket capacity

  • Rate of leakage

There are two implementations of the leaky bucket algorithm :

  1. The situation of sudden flow is not allowed : if the rate of water inflow is greater than the rate of water out, the excess water is directly discarded. For example, my bucket can hold 100L, but my bucket has a water output rate of 10L/s. At this time, if there is 100L/s of water coming in, I only let 10L of water into the bucket, and the rest are restricted. ( Limits the speed of the request )

  2. Allow a certain burst of traffic: my bucket can hold 100L, if my bucket is empty now, then the 100L of water can instantly enter my bucket. I will flow out the water at a rate of 10L/s. If there is still 100L of water coming in, I can only limit the flow.

After the above analysis, we know:

The leaky bucket algorithm can smooth the burst traffic on the network (because the rate of water leakage is fixed)

1.2 What is the token bucket algorithm

Now I have another bucket. This bucket is not used for water, but for tokens:

image

The bucket is filled with tokens

Tokens will be thrown into the bucket at a certain rate. For example, I throw 10 tokens into the bucket per second:

image

Throw tokens into the bucket at a certain rate

There is an upper limit on the number of tokens that can be stored in a bucket. For example, my bucket can only hold 1000 tokens at most.

Every request comes in, it will go to the bucket to get a token

  • For example, if I have 1001 requests this second, I will go to the bucket and get 1001 tokens. At this time, there may be two situations:

    • There are no 1001 tokens in the bucket, only 1000 tokens, then the request that does not get the token can only be blocked (waiting)

    • There are 1001 tokens in the bucket, and all requests can be executed.

image

Token bucket algorithm supports burst traffic on the network

The difference between a leaky bucket and a token bucket: As you can see from the above example, the leaky bucket can only process requests at a fixed rate, while the token bucket can process requests with the maximum number of tokens in the bucket.

Second, use Rate Limiter

RateLimiter is a current-limiting component of Guava. The system on my side uses this current-limiting component, which is very convenient to use.

Introduce pom dependency:

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>20.0</version>
</dependency>

RateLimiter is based on the token bucket algorithm , the API is very simple, see the following Demo:

public static void main(String[] args) {
        //线程池
        ExecutorService exec = Executors.newCachedThreadPool();
        //速率是每秒只有3个许可
        final RateLimiter rateLimiter = RateLimiter.create(3.0);

        for (int i = 0; i < 100; i++) {
            final int no = i;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        //获取许可
                        rateLimiter.acquire();
                        System.out.println("Accessing: " + no + ",time:"
                                + new SimpleDateFormat("yy-MM-dd HH:mm:ss").format(new Date()));

                    } catch (Exception e) {
                        e.printStackTrace();
                    }

                }
            };
            //执行线程
            exec.execute(runnable);
        }
        //退出线程池
        exec.shutdown();
    }

We can see from the results that only three can be executed per second:

image

Execute three per second

Three, distributed current limit

RateLimiter is a single-machine current-limiting component. If it is a distributed application, what should I do?

It can be achieved by using Redis+Lua. The approximate lua script code is as follows:

local key = "rate.limit:" .. KEYS[1] --限流KEY
local limit = tonumber(ARGV[1])        --限流大小
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then --如果超出限流大小
  return 0
else  --请求数+1,并设置1秒过期
  redis.call("INCRBY", key,"1")
   redis.call("expire", key,"1")
   return current + 1
end

The Java code is as follows:

public static boolean accquire() throws IOException, URISyntaxException {
    Jedis jedis = new Jedis("127.0.0.1");
    File luaFile = new File(RedisLimitRateWithLUA.class.getResource("/").toURI().getPath() + "limit.lua");
    String luaScript = FileUtils.readFileToString(luaFile);

    String key = "ip:" + System.currentTimeMillis()/1000; // 当前秒
    String limit = "5"; // 最大限制
    List<String> keys = new ArrayList<String>();
    keys.add(key);
    List<String> args = new ArrayList<String>();
    args.add(limit);
    Long result = (Long)(jedis.eval(luaScript, keys, args)); // 执行lua脚本,传入参数
    return result == 1;
}

Explanation:

  • Java code passes the key and the maximum limit parameter into the lua script

  • Execute the lua script (the lua script determines whether the current key exceeds the maximum limit)

    • If it exceeds, return 0 (current limit)

    • If it is not exceeded, return 1 (the program continues to execute)

Guess you like

Origin blog.csdn.net/qq_39331713/article/details/113885247