速率限制可观察量

时间:2014-11-14 15:01:05

标签: rx-java

我有一个observable,可以从数据库游标的快速流中生成数据。我希望以每秒x项的速率限制输出。到目前为止,我一直在使用文档中描述的Callstack阻止:

observable.map(f -> {
ratelimiter.acquire(); // configured limiter to only allow
});

这项工作正常,但出于好奇,有没有更好的方法来处理背压?

韩国社交协会

3 个答案:

答案 0 :(得分:2)

使用sample(throttleLast)运算符:

Observable<T> throttled = 
    observable.sample(1 / rate, TimeUnit.MILLISECONDS);

http://reactivex.io/documentation/operators/sample.html

https://github.com/ReactiveX/RxJava/wiki/Backpressure

答案 1 :(得分:2)

您可以尝试将rx.Observable#onBackpressureBuffer()与定制订阅者结合使用,定制订阅者每秒会定期请求n个项目。但是,你必须 hard 一秒钟的采样。

注意 .subscribeOn().toBlocking()只是为了让主要方法不会立即退出。

public class BackpressureTest {

  public static void main(final String[] args) {
    Observable.range(1, 1000)
      .compose(Observable::onBackpressureBuffer) // consume source immediately, but buffer it
      .lift(allowPerSecond(3)) // via operator using custom subscriber request n items per second
      .subscribeOn(Schedulers.computation())
      .toBlocking()
      .subscribe(System.out::println);
  }

  private static <T> Observable.Operator<T, T> allowPerSecond(final int n) {
    return upstream -> periodicallyRequestingSubscriber(upstream, n);
  }

  private static <T> Subscriber<T> periodicallyRequestingSubscriber(final Subscriber<T> upstream, final int n) {
    return new Subscriber<T>() {

      @Override
      public void onStart() {
        request(0); // request 0 so that source stops emitting
        Observable.interval(1, SECONDS).subscribe(x -> request(n)); // every second request n items
      }

      @Override
      public void onCompleted() {
        upstream.onCompleted();
      }

      @Override
      public void onError(final Throwable e) {
        upstream.onError(e);
      }

      @Override
      public void onNext(final T integer) {
        upstream.onNext(integer);
      }
    };
  }
}

答案 2 :(得分:0)

@michalsamek的回答似乎是正确的,尽管背压仅适用于Flowables。我已经更正了他的订阅者,因此它可以执行所请求的内容。

在不同时间连续使用时也存在轻微问题。

private static <T> FlowableOperator<T, T> allowPerMillis(int millis) {
    return observer -> new PeriodicallyRequestingSubscriber<>(observer, millis);
}


Observable.range(1, 100)
    .observeOn(Schedulers.io())
    .toFlowable(BackpressureStrategy.BUFFER)
    .compose(Flowable::onBackpressureBuffer)
    .lift(allowPerMillis(200))
    .subscribe(value -> System.out.println(System.currentTimeMillis() % 10_000 + " : " + value));



public class PeriodicallyRequestingSubscriber<T> implements Subscriber<T> {

    private final Subscriber<T> upstream;

    private final int millis;

    // If there hasn't been a request for a long time, do not flood
    private final AtomicBoolean shouldRequest = new AtomicBoolean(true);

    public PeriodicallyRequestingSubscriber(Subscriber<T> upstream, int millis) {
        this.upstream = upstream;
        this.millis = millis;
    }

    @Override
    public void onSubscribe(Subscription subscription) {
        Observable
                .interval(millis, TimeUnit.MILLISECONDS)
                .subscribe(x -> {
                   if (shouldRequest.getAndSet(false))
                       subscription.request(1);
                });
}

@Override
public void onNext(T t) {
    shouldRequest.set(true);
    upstream.onNext(t);
}

@Override
public void onError(Throwable throwable) {
    upstream.onError(throwable);
}

@Override
public void onComplete() {
    upstream.onComplete();
}
}