A web application queries an external server. If more than 80% of the requests to the server send within the past 'n' minutes fails, then the web applicatoin should back out 开发者_开发技巧from querying the server and perform other logic. I could think of an atomic integer being incremented by the failed requests. But i dont think atomic integer supports any action to be executed if the value of the atomic integer reaches a particular value. Is there any smarter way to do this in java?
Well, after updating your atomic integer, you could check its value and if the 80% is met, then you could take action (like flagging that server as 'weak responding or so'). If you are working in a multi-threaded environment, there is nothing wrong with your solution.
Another solution is to have the threads call a synchronized method to increase a non-atomic integer and perform the check. That integer would have to be an attribute of the class to which this method belongs.
If you want to monitor events in the last 'N' minutes you need more than just an integer. You need to know what was happening 'N' minutes ago so you can keep your success level estimate correct.
Here is one way to do it:
import java.util.LinkedList;
/**
* Class that monitors outcomes for until the proportion of successes in a
* specified time window falls below a trigger level, at which point an action
* is invoked.
*
* @author Simon
*/
public class SuccessMonitor {
/** An outcome */
static class Outcome {
/** Time of outcome */
final long eventTime = System.currentTimeMillis();
/** True for success, false for failure */
boolean outcome;
}
/** The action to invoke when there are two few successes */
private final Runnable action_;
/** The history of outcomes in the time window */
private final LinkedList<Outcome> events_ = new LinkedList<Outcome>();
/** Number of successes in the time window */
private int goodCount_ = 0;
/** Synchronization lock */
private final Object lock_ = new Object();
/** Length of the time window in milliseconds */
private final long trackTime_;
/** The success proportion at which to invoke the action */
private final double triggerLevel_;
/**
* New monitor
*
* @param trackTime
* number of milliseconds to retain history for
* @param triggerLevel
* the level at which to invoke the action
* @param action
* the action
*/
public SuccessMonitor(long trackTime, double triggerLevel, Runnable action) {
trackTime_ = trackTime;
triggerLevel_ = triggerLevel;
action_ = action;
}
private void check(boolean result) {
// create a new outcome
Outcome out = new Outcome();
out.outcome = result;
double level;
synchronized (lock_) {
// add the new outcome
goodCount_ += (result) ? 1 : 0;
events_.addLast(out);
// remove expired outcomes
long expire = System.currentTimeMillis() - trackTime_;
while( (!events_.isEmpty())
&& (events_.getFirst().eventTime < expire) ) {
out = events_.removeFirst();
goodCount_ -= (out.outcome) ? 1 : 0;
}
// Calculate the success level.
if (events_.isEmpty()) {
// if empty assume ok
level = 1.0;
} else {
// calculate success level
level = (double) goodCount_ / events_.size();
}
}
// if level has fallen too low, invoke action
if (level < triggerLevel_) action_.run();
}
/**
* Notify this monitor of a failure.
*/
public void fail() {
check(false);
}
/**
* Reset this monitor, causing it to discard all currently stored history.
*/
public void reset() {
synchronized (lock_) {
events_.clear();
goodCount_ = 0;
}
}
/**
* Notify this monitor of a success.
*/
public void success() {
check(true);
}
}
精彩评论