使用Spring MVC的RateLimiter

时间:2018-03-14 09:44:07

标签: java spring-mvc rate-limiting

我的要求是,

我有一个REST API,它接受请求参数,从这个请求我们获得appId,userid和IP地址(来自的请求)我需要对每秒5个请求进行速率限制(对于给定的密钥)组合)。

我有这个示例代码限制用户允许最多5个请求/秒。当只有一个用户发送超过5个请求时,它工作正常,它只允许5个请求/秒,而对于剩余请求,它会抛出错误"请求太多"。但是当有多个用户(比如5个)并且每个用户发送超过5个请求/秒时,则无法限制用户的费率(这里所有5个用户应该能够发送5个请求/秒)即,总共25个请求应该是成功的,剩下的请求应该抛出错误,例如"太多的请求"。 请告诉我我失踪的地方。

import java.util.concurrent.ConcurrentMap;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.google.common.cache.CacheBuilder;
import com.google.common.util.concurrent.RateLimiter;

@Controller
public class HomeController {

    private int CACHESIZE = 1000;
    private int rateRequests = 5;

    private ConcurrentMap<Object, Object> cache = CacheBuilder.newBuilder().maximumSize(CACHESIZE).build().asMap();

    private RateLimiter rateLimiter;

    HomeController() {
        rateLimiter = RateLimiter.create(rateRequests);
    }

    @RequestMapping(value = "/getData", method = RequestMethod.GET)
    public String getData(HttpServletRequest request) {

        if (!preCheck(request)) {
            return null;
        } else {
            return "get userdata from data base";
        }
    }

    private boolean preCheck(HttpServletRequest request) {
        String key = request.getParameter("userId") + request.getParameter("applicationId") + request.getRemoteAddr();
        RateLimiter rateLimiter = getRateLimiter();
        if (cache.containsKey(key)) {
            rateLimiter = (RateLimiter) cache.get(key);
        } else {
            cache.put(key, rateLimiter);
        }

        boolean allow = rateLimiter.tryAcquire();
        if (!allow) {
            System.out.println("Too many request");
        }
        return allow;
    }

    public RateLimiter getRateLimiter() {
        return rateLimiter;
    }

}


import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class RateLimitTest {
    public static void main(String[] args) {
        RateLimitTest limitTest = new RateLimitTest();
        limitTest.scheduleJob();
    }

    static ExecutorService executorService = Executors.newFixedThreadPool(10);

    private void scheduleJob() {
        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
        scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    executorService.submit(new Runnable() {

                        @Override
                        public void run() {
                            // getUserData("user1");
                            getUserData(randomGen(5));

                        }
                    });
                }
            }
        }, 0, 20, TimeUnit.SECONDS);
    }

    private String randomGen(int count) {
        String value = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        StringBuilder builder = new StringBuilder();
        while (count-- != 0) {
            int charIndex = (int) (Math.random() * value.length());
            builder.append(value.charAt(charIndex));
        }
        return builder.toString();
    }

    private String getUserData(String user) {
        return "call rest api( /getData ) to get the user data";
    }
}

1 个答案:

答案 0 :(得分:0)

我认为这是因为所有请求共享一个速率限制器,因此您应该为每个密钥创建一个速率限制器。试试这个:

private boolean preCheck(HttpServletRequest request) {
    String key = request.getParameter("userId") + request.getParameter("applicationId") + request.getRemoteAddr();
    RateLimiter rl = cache.get(key);
    if(rl == null){
       rl = RateLimiter.create(rateRequests);
       RateLimiter old = cache.putIfAbsent(key , rl);
       if(old !=null){
            rl = old;
       }
    }
    boolean allow = rl.tryAcquire();
    if (!allow) {
        System.out.println("Too many request");
    }
    return allow;
}

不要忘记删除属性 rateLimiter

的定义