Springmvc throttling interceptor sample code and springmvc sample code
Throttling Algorithm
Currently, two common traffic limiting algorithms are used: the token bucket algorithm and the bucket leakage algorithm. The main difference is that the bucket leakage algorithm can forcibly limit the Request Rate and smooth burst requests, the token bucket algorithm allows a certain amount of burst requests when the average rate is limited.
The following figure shows two algorithms found on the Internet. It is easy to differentiate the features of these two algorithms.
Bucket leakage Algorithm
Token Bucket Algorithm
For the interface, it is generally allowed to process a certain amount of burst requests, only the average rate is required, so the token bucket algorithm is more common.
Token bucket algorithm tool RateLimiter
Currently, my commonly used token bucket Algorithm Implementation class is google guava's RateLimiter. guava not only implements the token bucket algorithm, but also caches, new collection classes, concurrent tools, and string processing classes. Is a powerful tool set
The RateLimiter api allows you to view the introduction of the concurrent programming network guava RateLimiter.
RateLimiter source code analysis
By default, RateLimiter has two core attributes: nextFreeTicketMicros. The next time you get the token, the number of tokens in the storedPermits bucket.
Determine whether tokens can be obtained:
Each time you obtain a token, calculate the fastest time to obtain the token based on the number of tokens in the bucket nextFreeTicketMicros. when determining whether resources can be obtained, you only need to compare nextFreeTicketMicros with the current time. so easy
Token acquisition operation:
For obtaining tokens, calculate the number of new tokens Based on nextFreeTicketMicros and the current time, write the number of tokens in the current token bucket, re-calculate nextFreeTicketMicros, and write the token in the bucket, and reduce the number of tokens obtained in this request.
Like the AQS class in java, the core of RateLimiter is the tryAcquire method.
Public boolean tryAcquire (int permits, long timeout, TimeUnit unit) {// The maximum waiting time for trying to obtain a resource long timeoutMicros = max (unit. toMicros (timeout), 0); // check whether the number of retrieved resources is correct checkPermits (permits); long microsToWait; // lock synchronized (mutex ()) {// current time long nowMicros = stopwatch. readMicros (); // determines whether resources can be obtained within the timeout time if (! CanAcquire (nowMicros, timeoutMicros) {return false;} else {// Obtains resources, recalculates resources, and returns the time required for the current thread to sleep microsToWait = reserveAndGetWaitLength (permits, nowMicros) ;}/// sleep stopwatch. sleepMicrosUninterruptibly (microsToWait); return true ;}
Determine whether tokens can be obtained:
Private boolean canAcquire (long nowMicros, long timeoutMicros) {// the earliest available resource time-wait time <= the current time before obtaining the resource return queryEarliestAvailable (nowMicros)-timeoutMicros <= nowMicros ;}
The queryEarliestAvailable of the default RateLimiter implementation class is to take the member variable nextFreeTicketMicros.
Get the token and calculate the wait time operation:
Final long reserveAndGetWaitLength (int permits, long nowMicros) {// obtain the next available time long momentAvailable = reserveEarliestAvailable (permits, nowMicros ); // return max (momentAvailable-nowMicros, 0 );}
Final long reserveEarliestAvailable (int requiredPermits, long nowMicros) {// recalculate the number of tokens in the bucket storedPermits resync (nowMicros); long returnValue = nextFreeTicketMicros; // The number of tokens consumed this time. double storedPermitsToSpend = min (requiredPermits, this. storedPermits); // re-calculate the next available time nextFreeTicketMicros double freshPermits = requiredPermits-storedPermitsToSpend; long waitMicros = storedPermitsToWaitTime (this. storedPermits, storedPermitsToSpend) + (long) (freshPermits * stableIntervalMicros); this. nextFreeTicketMicros = LongMath. saturatedAdd (nextFreeTicketMicros, waitMicros); // reduce the number of tokens in the bucket this. storedPermits-= storedPermitsToSpend; return returnValue ;}
Implement simple spring mvc throttling interceptor
Implement a HandlerInterceptor and create a RateLimiter Current Limiter in the constructor.
public SimpleRateLimitInterceptor(int rate) { if (rate > 0) globalRateLimiter = RateLimiter.create(rate); else throw new RuntimeException("rate must greater than zero");}
Call the tryAcquire method of the throttling in preHandle to determine whether the throttling rate has exceeded.
Public boolean preHandle (HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (! GlobalRateLimiter. tryAcquire () {LoggerUtil. log (request. getRequestURI () + "request exceeds current limit rate"); return false;} return true ;}
Configure throttling interceptor in dispatcher-servlet.xml
<Mvc: interceptors> <! -- Throttling interceptor --> <mvc: interceptor> <mvc: mapping path = "/**"/> <bean class = "limit. simpleRateLimitInterceptor "> <constructor-arg index =" 0 "value =" $ {totalRate} "/> </bean> </mvc: interceptor> </mvc: interceptors>
Complex version of spring mvc throttling interceptor
Use Properties to pass in the intercepted url expression-> rate
<Mvc: interceptor> <mvc: mapping path = "/**"/> <bean class = "limit. RateLimitInterceptor"> <! -- Single url throttling --> <property name = "urlProperties"> <props> <prop key = "/get/{id}"> 1 </prop> <prop key =" /post "> 2 </prop> </props> </property> </bean> </mvc: interceptor>
Create a corresponding RateLimiter Limiter for each url expression. The url expression is encapsulated as org. springframework. web. servlet. mvc. condition. PatternsRequestCondition. PatternsRequestCondition is a class used in spring MVC's DispatcherServlet to match requests and controllers. It can be used to determine whether a request meets these url expressions.
In the preHandle method of the interceptor
// The current request path String lookupPath = urlPathHelper. getLookupPathForRequest (request); // iterate the PatternsRequestConditionfor (PatternsRequestCondition patternsRequestCondition: urlRateMap. keySet () {// Matched List <String> matches = patternsRequestCondition. getMatchingPatterns (lookupPath); if (! Matches. isEmpty () {// if the matching succeeds, the token if (urlRateMap. get (patternsRequestCondition ). tryAcquire () {LoggerUtil. log (lookupPath + "request matched to" + Joiner. on (","). join (patternsRequestCondition. getPatterns () + "throttling");} else {// LoggerUtil failed to get the token. log (lookupPath + "request exceeds" + Joiner. on (","). join (patternsRequestCondition. getPatterns () + "throttling rate"); return false ;}}}
Specific implementation class
Please refer to github
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.