Use Ratelimiter to complete a simple large flow limit, snapping up seconds to kill limit Flow _ current & downgrade

Source: Internet
Author: User

Https://www.cnblogs.com/yeyinfu/p/7316972.html

Ratelimiter is an implementation class provided by guava based on the token bucket algorithm, which can be very simple to complete the current limiting effects and adjust the rate of generation token according to the actual situation of the system.

It can usually be used in snapping up the limit flow to prevent the burst system, restricting the amount of traffic in an interface, service unit time, for example, some third-party services will restrict the user's traffic, restrict the speed, and only allow the number of downloads per unit time.

Here are some simple practices that need to introduce guava Maven dependencies first. Do you have a lot of tasks, but want to be no more than n [java]  view plain copy print per second? import com.google.common.util.concurrent.ratelimiter;      import  java.util.arraylist;   import java.util.list;   import  java.util.concurrent.executorservice;   import java.util.concurrent.executors;      /**   * Created by wuwf on 17/7/11.   *  has a number of tasks, But do not want to exceed X per second, available in this category    */   public class demo1 {           public static void main (String[] args)  {            //0.5 represents the maximum number of             in a second Ratelimiter ratelimiter = ratelimiter.create (0.5);            list<runnable> tasks = new arraylIst<runnable> ();           for  (int i =  0; i < 10; i++)  {                tasks.add (New userrequest (i));            }           executorservice threadpool  = executors.newcachedthreadpool ();           for   (runnable runnable : tasks)  {                system.out.println ("Wait Time:"  + ratelimiter.acquire ());                threadpool.execute (runnable);           }       }          private static class userrequest implements runnable {           private int id;               public userrequest (int id)  {                this.id = id;            }              public  void run ()  {                system.out.println (ID);           }        }     }   The example is that multiple threads execute sequentially, limiting the execution of up to one per 2 second. Run look at the results
We have limited 2 seconds to release one, we can see that the first one is directly executed, followed by every 2 seconds will release one.
Ratelimiter.acquire () This method blocks the thread until the token can be fetched in the token to continue down execution and returns the wait time.
Two-snapped scene limit flow For example, we estimate that the database can withstand concurrent 10, exceeding the possible failure, we can limit the flow of the request interface.
[java]  View plain copy print? package com.tianyalei.controller;      import  com.google.common.util.concurrent.ratelimiter;   import com.tianyalei.model.goodinfo;    import com.tianyalei.service.goodinfoservice;   import  org.springframework.web.bind.annotation.requestmapping;   import  org.springframework.web.bind.annotation.restcontroller;      import  javax.annotation.resource;     /**   * created by wuwf on  17/7/11.   */   @RestController    Public class indexcontroller  {        @Resource (name =  "db")         private GoodInfoService goodInfoService;           Ratelimiter ratelimiter = ratelimiter.create (;  )         @RequestMapping ("/miaosha")        public Object  Miaosha (int count, string code)  {            system.out.println ("Wait Time"  + ratelimiter.acquire ());            if  (Goodinfoservice.update (code, count)  > 0)  {                return  "Purchase Success";            }           return  " Purchase Failure ";       }                  @RequestMapping ("/add")        public object add ()  {            for  (int i = 0; i <  100;  i++)  {               GoodInfo  Goodinfo = new goodinfo ();                goodinfo.setcode ("iphone"  + i);                goodinfo.setamount (;  )              goodinfoservice.add (goodinfo);            }              return  "add success";        }  }   This is the previous article (Seconds Kill system db,http://blog.csdn.net/ tianyaleixiaowu/article/details/74389273) added a controller code is very simple, that is, when the request comes, call Ratelimiter.acquire, if more than 10 requests per second, blocking the wait. We used JMeter to simulate 100 concurrent. Create a thread group with a thread count of 100 and a start interval of 0, representing 100 concurrent requests.


Start the JMeter request and see the console results



Initialize 10 capacity, so the first 10 requests do not have to wait for direct success, the beginning of the following is 1 seconds 10 limit flow, basically every 0.1 seconds release one. Three snapping scenes the above example restricts the operation of DB within a unit of time, but is unfriendly to the user because he needs to wait and not respond quickly. When you have 10,000 concurrent requests, one second can handle only 10, the rest of the user will be in a long wait. So we need to downgrade the application, once you have to determine that some requests are not the token, we quickly return to failure, to avoid unnecessary waiting. Since Ratelimiter is the way to generate a number of tokens per unit of time, For example, 0.1 seconds to generate 1, the snapping is to see the luck, you just in the generation of 1 came in, then you can grab, in this 0.1 seconds other requests, even if blind, can only hope that the next 0.1 seconds, and from the user experience, can not let him stay there blocking waiting, so it is necessary to quickly judge, the user in a certain period of time, there are There is no chance of getting a token, here you need to use the Tryacquire (long timeout, Timeunit unit) method to specify a time-out, and return false once you have determined that the token cannot be obtained within the timeout time. Note that this is not really waiting for the timeout time, but is judged to be unable to obtain a token even after timeout time. There is no need to wait for this.
See implementation:
[java]  View plain copy print? /**       * tryacquire (long timeout, timeunit unit)        *  obtain license from ratelimiter  if the license can be obtained within timeout time,        *  or if you cannot get permission before timeout  expires, return false immediately (no need to wait)         */        @RequestMapping ("/buy")        public  object miao (int count, string code)  {            //Determines whether a token can be obtained within 1 seconds, returns false immediately if not, and does not block programs             if  (!ratelimiter.tryacquire (1000, timeunit.milliseconds))  {                system.out.println ("Can't get a token in the short term, it's unfortunate, line up");               return  "Failure";            }            if  (Goodinfoservice.update (code, count)  > 0)  {      &NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;SYSTEM.OUT.PRINTLN ("Purchase Success");                return  "Success";            }            SYSTEM.OUT.PRINTLN ("Insufficient data, failure");           return  "Failure";        }   without looking at the execution results, we can first analyze, 10 tokens a second, 0.1 seconds out of one, 100 requests come in, if 100 are at the same time, In the end, only 10 are sold, and 90 will fail because of a timeout. In fact, it does not arrive exactly at the same time, and is bound to arrive in 0.1 seconds, and will be grouped into the next cycle. This is a very complicated mathematical problem, and each request is computed to calculate the probability that the token will be acquired in the future. Fortunately, Ratelimiter has its own way to make judgments. We run to see the results

Perform several more times and find that the order is different each time. After many experiments, when the thread group was set at 0 o'clock, the number of successful purchases was always 22. The other 78 were failures. But basically are the beginning and the end of continuous success, the middle of a large segment failure. I'm modifying the JMeter thread group. These 100 requests have a production time of 1 seconds, and the results are as follows

In addition to the previous and last few requests for continuous success, the middle of the more stable, are 8 or 9 will be successful once.
When I change to 2 seconds to produce 100 requests, the result is more evenly

Basically it was the top 10 successes, and the rest began to succeed at a fixed rate. This scene is more in line with the actual application scene, according to the fixed unit time to split, each unit time produces a token, available for purchase. See here is not a bit understand Rob Millet situation, a lot of time is not your speed fast, hand speed can grab, you need to see the distribution of the background system. So you can get, it is best to open a lot of accounts, rather than always with an account in the fierce point, because you dot also white, backstage has put your qualifications out of the outside. Of course, the real rush is not so simple, instantaneous flow of traffic will be overwhelmed by the load of the server, when 1 million people rob 10,000 millet, the connection mouth are requested not to come in, let alone the interface of the token distribution. At this point, you need to do a layer of limit flow, we can choose to do in the previous layer of distributed, open multiple services, first to do a limited flow, eliminate the vast majority of bad luck users, and even randomly discard some of the rules of the user, quickly intercept 90% of the request, let you go to the Web page to see single line animation, there are 100,000. 100,000 is too big enough to burst the data layer, then into the queue MQ, with MQ Cutting peak, and then into the business logic, and then ratelimiter limit flow, at this time can intercept the unfortunate 90%, there are 10,000, 10,000 to the business logic and data layer, with Redis and DB to deal with inventory. Congratulations, you're the one that slipped through. The point is to quickly intercept 99% of the unfortunate and avoid getting them to touch the data tier. And can not wait too long, it is best to request the moment you can be sure you are always watching the best single animation.
/***************************************************************************************************/added: The effect is not so obvious on the local only, I put this small project into the online server and measured it. First tried to remove the ratelimiter, only the DB service processing data, found that MySQL services accounted for about 20% of the CPU, the overall request failure rate is high. Many are Tomcat timeouts. Using Ratelimiter blocking, the database CPU basically no movement, the pressure is almost no, tomcat timeout still some, because the concurrent number of large, can not handle. With Ratelimiter non-blocking, timeouts and requests fail very little, overall QPS up a lot. The test is not very formal, you probably ran.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.