rxjs rate limit (requests per second) and concurrency

这一生的挚爱 提交于 2019-12-07 10:32:30

If you just want to drop the events that occur in between you can use windowWithTimeOrCount + throttleFirst:

var subscription = source

 //Splits the events into 15 minute windows
 .windowWithTimeOrCount(900000 /*15 minutes*/, 15) 

 //Stops us from receiving more than one window in 15 minutes
 .throttleFirst(900000 /*15 minutes*/)

 //Flatten the observable
 .concatAll()
 .subscribe(
  function (x) { console.log('onNext: %s', x); },
  function (e) { console.log('onError: %s', e); },
  function () { console.log('onCompleted'); });

Working example (output in console):

var source = Rx.Observable.generateWithRelativeTime(
    0,
    function(x) { return x < 1000; },
    function(x) { return x + 1; },
    function(x) { return x; },
    function(x) { return Math.floor(Math.random() * 100); });


source
 .windowWithTimeOrCount(1000, 3)
 .throttleFirst(1000)
 .concatAll()
 .subscribe(console.log.bind(console));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.3/rx.all.js"></script>

Alternative 1

If you don't want to drop any values you can also use controlled on your pipeline along with a specially rolled regulate method:

var subscription = source
  .windowWithTimeOrCount(900000, 15)
  //This will stop the loss of any events from the hot window
  .map(function(x) {
    var c = x.replay(),
        d = c.connect();
    //Shut down the connected observable when you are done.
    return Rx.Observable.using(function() {return d; },
                               function() {return c; });
  })
  //This will prevent more than one window from going through per interval
  //[See snippet]
  .regulate(900000)
  .concatAll()
  .subscribe(
    function (x) { console.log('onNext: %s', x); },
    function (e) { console.log('onError: %s', e); },
    function () { console.log('onCompleted'); });

Working example (output in console):

Rx.Observable.prototype.regulate = function(interval, scheduler) {
  var source = this,
    scheduler = scheduler || (scheduler = Rx.Scheduler.timeout);

  return Rx.Observable.create(function(observer) {
    var controller = source.controlled(scheduler),
      d = new Rx.SerialDisposable();

    function nextSample(x) {

      //This will request a new value after our minimum interval expires
      d.setDisposable(scheduler.scheduleWithRelative(interval, function(s) {
        return controller.request(1);
      }));

      observer.onNext(x);
    }

    return new Rx.CompositeDisposable(
      d,
      controller.subscribe(nextSample,
        observer.onError.bind(observer),
        observer.onCompleted.bind(observer)),
      controller.request(1));

  }, source);

};


var subscription = Rx.Observable.generateWithRelativeTime(
    0,
    function(x) {
      return x < 100;
    },
    function(x) {
      return x + 1;
    },
    function(x) {
      return x;
    },
    function(x) {
      return Math.floor(Math.random() * 200);
    })
  //Divides up windows by count and our interval time
  .windowWithTimeOrCount(2000, 15)
  //Necessary since the windows we receive are hot
  .map(function(x) {
    var c = x.replay();
    var d = c.connect();
    return Rx.Observable.using(function() {
      return d;
    }, function() {
      return c;
    });
  })
  //Only allows one window through every 2 seconds
  .regulate(2000)
  //Flatten everything out
  .concatAll()
  .subscribe(console.log.bind(console), console.error.bind(console), console.log.bind(console, "Finished!"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/2.5.3/rx.all.js"></script>

I've faced a very similar issue in my personal project and decided to publish a reusable solution as a npm package https://www.npmjs.com/package/rx-op-lossless-throttle

Unlike http://www.g9labs.com/2016/03/21/lossless-rate-limiting-with-rxjs/ it doesn't force the delay on every single event.

If you don't want to lose any notification you can use buffer or one of it's variations (with time/count/time or count). It basically combines the notifications in an array and forwards the array when:

  • an observable notifies
  • a timer expires
  • the count of notifications has been reached
  • a combination of the above.

So you could buffer your notifications in an array and receive it only once per minute, or when 100 notifications have arrived.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!