使用RxJava

时间:2016-11-01 10:14:12

标签: http jersey rx-java vert.x throttling

我正在尝试使用Jersey客户端来限制传出的http请求。由于我正在运行Vertx Verticle,因此我创建了一个特殊的RateLimiter类来处理限制。

我的目标是防止HTTP调用的速度超过每秒1次。我们的想法是,使用单线程ExecutorService运行提交的可调用对象,以便我可以阻止该单个线程,以保证不会以更高的速率处理这些任务。

基本上这个类中唯一的公共方法是“call”:

 public <T> Observable<T> call(Callable<Observable<T>> action) {
    return Observable.create(subscriber -> {

        Observable<Observable<T>> observed =
                Observable.from(executor.submit(() -> {                         
                        return action.call();
                    })
                ).doOnError(throwable -> {
                               logger.error(throwable);
                           }
                );

        observed.subscribe(t -> {
            try {
                Thread.sleep(1000); 
                t.subscribe(data -> {
                    try {
                        subscriber.onNext(data);
                    } catch (Throwable e) {
                        subscriber.onError(e);
                    }
                    subscriber.onCompleted();
                });
            } catch (Exception e) {
                logger.error(e);
            }

        });
    });
} 

这是我当前的实现,无论自上一次调用以来已经过了多少时间,它都会使用1秒钟的睡眠时间。最初我尝试使用ScheduledExecutorService并计算延迟时间,以便我将以每秒1的速率提交请求。但是,在这两种情况下,它经常无法满足速率限制,我会一个接一个地立即提交两个请求。

我的假设是某个地方请求被传递给另一个正在被不同线程连续轮询的执行队列,因此如果由于某种原因线程繁忙并且队列中同时存在两个请求,它们将按顺序执行但没有延迟。

任何想法如何解决这个问题?也许是一种不同的方法?

2 个答案:

答案 0 :(得分:1)

我会使用简单的Vertx事件总线和队列,每秒轮询一次:

 public static void main(String[] args) {


    Vertx vertx = Vertx.vertx();

    vertx.deployVerticle(new DebounceVerticle(), (r) -> {

        // Ok, verticle is ready!
        // Request to send 10 events in 1 second
        for (int i = 0; i < 10; i++) {
            vertx.eventBus().publish("call", UUID.randomUUID().toString());
        }
    });


}


private static class DebounceVerticle extends AbstractVerticle {

    HttpClient client;
    @Override
    public void start() {
        client = vertx.createHttpClient();

        BlockingQueue<String> queue = new LinkedBlockingQueue<>();

        vertx.eventBus().consumer("call", (payload) -> {
            String message = (String) payload.body();
            queue.add(message);
            System.out.println(String.format("I got %s but I don't know when it will be executed", message));
        });

        vertx.setPeriodic(1000, (l) -> {
            String message = queue.poll();

            if (message != null) {
                System.out.println(String.format("I'm finally sending %s", message));

                //Do your client magic
            }
        });
    }
}

答案 1 :(得分:0)

使用guava RateLimiter预先添加Web服务调用。这是RxJava中的一个示例,它显示每500毫秒的事件如何被限制为每秒一次。

    Function<Long, Long> throttlingFunction = new Function<Long, Long>() {
        private RateLimiter limiter = RateLimiter.create(1.0);

        public Long apply(Long t) throws Exception {
            limiter.acquire();
            return t;
        }
    };
    Observable.interval(500, TimeUnit.MILLISECONDS)
        .map(throttlingFunction)
        .subscribe(new Consumer<Long>() {
        public void accept(Long t) throws Exception {
            System.out.println(t);
        }
    });

同样在vert.x中,所有阻塞内容都应该在executeBlocking的帮助下运行。