Java braking mechanism

Update . I am on Java 1.6.34 with no chance of migrating to Java 7.

I have a scenario where I am allowed to call a method 80 times per minute. This is actually a service API written by a third party, and it "turns off" (ignores calls) its API if you call it too many times:

public class WidgetService {
    // Can only call this method 80x/min, otherwise it
    // it just doesn't do anything
    public void doSomething(Fizz fizz);
}

      

I would like to write a class ApiThrottler

that has a method boolean canRun()

that will tell my Java client if the method can be called doSomething(Fizz)

or not. (Of course, it can always be named, but it makes no sense to name it if we have exceeded our bid.)

So, something that would allow me to write code like this:

// 80x/min
ApiThrottler throttler = new ApiThrottler(80);

WidgetService widgetService = new DefaultWidgetService();

// Massive list of Fizzes
List<Fizz> fizzes = getFizzes();

for(Fizz fizz : fizzes)
    if(throttler.canRun())
        widgetService.doSomething(fizz);

      

It doesn't have to be API ( ApiThrottler#canRun

), but I still need a reliable / reliable mechanism that will suspend / hibernate until WidgetService#doSomething(Fizz)

called.

This makes me feel like we are headed into the area of ​​using multiple threads, which makes me feel like we can use some kind of locking mechanism and Java ( wait()

/ notify()

) notification model . But with no experience in this world, I don't seem to be leaning towards the most elegant solution. Thanks in advance.

+3


source to share


4 answers


Probably one of the best options would be to use the Semaphore class http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html and give it 80 permissions every minute. That could be done for example using the timer class http://docs.oracle.com/javase/7/docs/api/java/util/Timer.html The caller thread will consume permissions every time it makes a service call by calling the method get () on the semaphore, which will block if all permissions have already been removed.

Surely one could code this with wait / notify and an integer counter with a timer or a separate thread as you mentioned, but that would be more complicated compared to using the more modern java.util.concurrent API I outlined above.

It might look something like this:

class Throttler implements TimerTask {
  final Semaphore s = new Semaphore(80);  
  final Timer timer = new Timer(true);

  Throttler() {
    timer.schedule(this, 0, 60*1000);   //schedule this for 1 min execution  
  }

  run() {  //called by timer 
    s.release(80 - s.availablePermits());
  }

  makeCall() {
    s.acquire();
    doMakeCall();
  }

}

      



This should work as of Java 5.

Also it would be better to use com.google.common.util.concurrent.RateLimiter

from Guava. It might look like this:

class Throttler {
  final RateLimiter rateLimiter = RateLimiter.create(80.0/60.0);

  makeCall() {
    rateLimiter.acquire();
    doMakeCall();
  }
}

      

The semantics are slightly different from Semaphore's solution, with RateLimiter likely being a better fit for your situation.

+5


source


I recently wrote something similar. The only change is that my code expects a callback when the function is executed. So if it doesn't work, I call the callback.

Another change is that since this call is probably asynchronous, it is possible that the call is made when you call it a second time. In such cases, I just ignored the challenge.

And my choke has a helper function called call

that takes a callable function and a callback. It was in C ++, so it would be in the form of Action and Listener interfaces for you.

This has an advantage over Semaphore based solutions that serialize requests , so you don't call it too often .



interface Callback{
    public void OnFunctionCalled();
}

class APIThrottler
   //ctor etc
   boolean CanCall();

   public boolean IsInProgress();

   public void SetInProgress(boolean inProgress = true);

   public void Mark(){/*increment counter*/; SetInProgress(false);} // couldnt think of a better name..

   public void Call(Callable event, Callback callback){
        If(IsInProgress())
            return;
        else if(CanCall())
        {
            SetInProgress();
            event.Call();
        }
        else
            callback.OnFunctionCalled();

    }
}

      

Once the function is callback (or within the function itself if it is synchronous), you will need Mark()

after it completes.

This is mostly my implementation, the only difference is that I dealt with once every x seconds (or minutes).

+1


source


This can be accomplished with a simple counter. Let's call this the "permissive" counter, initialized in block 80. Before you call the "function", first try to get the permission. When you're done, release it. Then set up a repeating timer that reset the counter 80 times per second.

class Permit extends TimerTask
{
    private Integer permit = 80;

    private synchronized void resetPermit() { this.permit = 80; }

    public synchronized boolean acquire() {
        if(this.permit == 0) return false;
        this.permit--;
        return true;
    }

    public synchronized void release() { this.permit++; }

    @Override public void run() { resetPermit(); }
}

      

Set the timer to reset to allow every second like this. The first parameter of the schedule method is a TimerTask instance (pass the Permit object above). The run () method will be called for every period specified in the second argument (here 1000 miliseconds / 1 sec). True indicates that this is a daemon timer that will keep repeating.

Timer timer = new Timer(true);
timer.schedule(permit, 1000);

      

Then every time you need to call your function, first check if you can get any permission. Don't forget to release it when you're done.

void myFunction() {
  if(!permit.acquire()) {
      System.out.println("Nah.. been calling this 80x in the past 1 sec");
      return;
  }

  // do stuff here

  permit.release();
}

      

Note the use of the synchronized keyword in the methods of the Permit class above - this should avoid more than one thread executing one of the methods of the same object instance at the same time

0


source


You can keep track of the time and make sure there are no more than 80 entries at the last minute.

// first=newest, last=oldest
final LinkedList<Long> records = new LinkedList<>();

synchronized public void canRun() throws InterruptedException
{
    while(true)
    {
        long now = System.currentTimeMillis();
        long oneMinuteAgo = now - 60000;

        // remove records older than one minute
        while(records.getLast() < oneMinuteAgo)
            records.removeLast();

        if(records.size()<80) // less than 80 records in the last minute
        {
            records.addFirst(now);
            return;   // can run
        }

        // wait for the oldest record to expire, then check again
        wait( 1 + records.getLast() - oneMinuteAgo);
    }
}

      

It is possible that at the 0th second we give 80 calls, wait a minute, and then at the 60th second we give out 80 more calls. The other end can measure 160 calls in a minute due to inaccurate clocks at both ends or random network delays. To be safe, lengthen the time, say throttle 80 calls in 70 seconds.

0


source







All Articles