001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.lang3.concurrent; 018 019import java.util.EnumMap; 020import java.util.Map; 021import java.util.concurrent.TimeUnit; 022import java.util.concurrent.atomic.AtomicReference; 023 024/** 025 * <p> 026 * A simple implementation of the <a 027 * href="http://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern 028 * that counts specific events. 029 * </p> 030 * <p> 031 * A <em>circuit breaker</em> can be used to protect an application against unreliable 032 * services or unexpected load. A newly created {@code EventCountCircuitBreaker} object is 033 * initially in state <em>closed</em> meaning that no problem has been detected. When the 034 * application encounters specific events (like errors or service timeouts), it tells the 035 * circuit breaker to increment an internal counter. If the number of events reported in a 036 * specific time interval exceeds a configurable threshold, the circuit breaker changes 037 * into state <em>open</em>. This means that there is a problem with the associated sub 038 * system; the application should no longer call it, but give it some time to settle down. 039 * The circuit breaker can be configured to switch back to <em>closed</em> state after a 040 * certain time frame if the number of events received goes below a threshold. 041 * </p> 042 * <p> 043 * When a {@code EventCountCircuitBreaker} object is constructed the following parameters 044 * can be provided: 045 * </p> 046 * <ul> 047 * <li>A threshold for the number of events that causes a state transition to 048 * <em>open</em> state. If more events are received in the configured check interval, the 049 * circuit breaker switches to <em>open</em> state.</li> 050 * <li>The interval for checks whether the circuit breaker should open. So it is possible 051 * to specify something like "The circuit breaker should open if more than 10 errors are 052 * encountered in a minute."</li> 053 * <li>The same parameters can be specified for automatically closing the circuit breaker 054 * again, as in "If the number of requests goes down to 100 per minute, the circuit 055 * breaker should close itself again". Depending on the use case, it may make sense to use 056 * a slightly lower threshold for closing the circuit breaker than for opening it to avoid 057 * continuously flipping when the number of events received is close to the threshold.</li> 058 * </ul> 059 * <p> 060 * This class supports the following typical use cases: 061 * </p> 062 * <p> 063 * <strong>Protecting against load peaks</strong> 064 * </p> 065 * <p> 066 * Imagine you have a server which can handle a certain number of requests per minute. 067 * Suddenly, the number of requests increases significantly - maybe because a connected 068 * partner system is going mad or due to a denial of service attack. A 069 * {@code EventCountCircuitBreaker} can be configured to stop the application from 070 * processing requests when a sudden peak load is detected and to start request processing 071 * again when things calm down. The following code fragment shows a typical example of 072 * such a scenario. Here the {@code EventCountCircuitBreaker} allows up to 1000 requests 073 * per minute before it interferes. When the load goes down again to 800 requests per 074 * second it switches back to state <em>closed</em>: 075 * </p> 076 * 077 * <pre> 078 * EventCountCircuitBreaker breaker = new EventCountCircuitBreaker(1000, 1, TimeUnit.MINUTE, 800); 079 * ... 080 * public void handleRequest(Request request) { 081 * if (breaker.incrementAndCheckState()) { 082 * // actually handle this request 083 * } else { 084 * // do something else, e.g. send an error code 085 * } 086 * } 087 * </pre> 088 * <p> 089 * <strong>Deal with an unreliable service</strong> 090 * </p> 091 * <p> 092 * In this scenario, an application uses an external service which may fail from time to 093 * time. If there are too many errors, the service is considered down and should not be 094 * called for a while. This can be achieved using the following pattern - in this concrete 095 * example we accept up to 5 errors in 2 minutes; if this limit is reached, the service is 096 * given a rest time of 10 minutes: 097 * </p> 098 * 099 * <pre> 100 * EventCountCircuitBreaker breaker = new EventCountCircuitBreaker(5, 2, TimeUnit.MINUTE, 5, 10, TimeUnit.MINUTE); 101 * ... 102 * public void handleRequest(Request request) { 103 * if (breaker.checkState()) { 104 * try { 105 * service.doSomething(); 106 * } catch (ServiceException ex) { 107 * breaker.incrementAndCheckState(); 108 * } 109 * } else { 110 * // return an error code, use an alternative service, etc. 111 * } 112 * } 113 * </pre> 114 * <p> 115 * In addition to automatic state transitions, the state of a circuit breaker can be 116 * changed manually using the methods {@link #open()} and {@link #close()}. It is also 117 * possible to register {@code PropertyChangeListener} objects that get notified whenever 118 * a state transition occurs. This is useful, for instance to directly react on a freshly 119 * detected error condition. 120 * </p> 121 * <p> 122 * <em>Implementation notes:</em> 123 * </p> 124 * <ul> 125 * <li>This implementation uses non-blocking algorithms to update the internal counter and 126 * state. This should be pretty efficient if there is not too much contention.</li> 127 * <li>This implementation is not intended to operate as a high-precision timer in very 128 * short check intervals. It is deliberately kept simple to avoid complex and 129 * time-consuming state checks. It should work well in time intervals from a few seconds 130 * up to minutes and longer. If the intervals become too short, there might be race 131 * conditions causing spurious state transitions.</li> 132 * <li>The handling of check intervals is a bit simplistic. Therefore, there is no 133 * guarantee that the circuit breaker is triggered at a specific point in time; there may 134 * be some delay (less than a check interval).</li> 135 * </ul> 136 * @since 3.5 137 */ 138public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> { 139 140 /** A map for accessing the strategy objects for the different states. */ 141 private static final Map<State, StateStrategy> STRATEGY_MAP = createStrategyMap(); 142 143 /** Stores information about the current check interval. */ 144 private final AtomicReference<CheckIntervalData> checkIntervalData; 145 146 /** The threshold for opening the circuit breaker. */ 147 private final int openingThreshold; 148 149 /** The time interval for opening the circuit breaker. */ 150 private final long openingInterval; 151 152 /** The threshold for closing the circuit breaker. */ 153 private final int closingThreshold; 154 155 /** The time interval for closing the circuit breaker. */ 156 private final long closingInterval; 157 158 /** 159 * Creates a new instance of {@code EventCountCircuitBreaker} and initializes all properties for 160 * opening and closing it based on threshold values for events occurring in specific 161 * intervals. 162 * 163 * @param openingThreshold the threshold for opening the circuit breaker; if this 164 * number of events is received in the time span determined by the opening interval, 165 * the circuit breaker is opened 166 * @param openingInterval the interval for opening the circuit breaker 167 * @param openingUnit the {@code TimeUnit} defining the opening interval 168 * @param closingThreshold the threshold for closing the circuit breaker; if the 169 * number of events received in the time span determined by the closing interval goes 170 * below this threshold, the circuit breaker is closed again 171 * @param closingInterval the interval for closing the circuit breaker 172 * @param closingUnit the {@code TimeUnit} defining the closing interval 173 */ 174 public EventCountCircuitBreaker(final int openingThreshold, final long openingInterval, 175 final TimeUnit openingUnit, final int closingThreshold, final long closingInterval, 176 final TimeUnit closingUnit) { 177 super(); 178 checkIntervalData = new AtomicReference<>(new CheckIntervalData(0, 0)); 179 this.openingThreshold = openingThreshold; 180 this.openingInterval = openingUnit.toNanos(openingInterval); 181 this.closingThreshold = closingThreshold; 182 this.closingInterval = closingUnit.toNanos(closingInterval); 183 } 184 185 /** 186 * Creates a new instance of {@code EventCountCircuitBreaker} with the same interval for opening 187 * and closing checks. 188 * 189 * @param openingThreshold the threshold for opening the circuit breaker; if this 190 * number of events is received in the time span determined by the check interval, the 191 * circuit breaker is opened 192 * @param checkInterval the check interval for opening or closing the circuit breaker 193 * @param checkUnit the {@code TimeUnit} defining the check interval 194 * @param closingThreshold the threshold for closing the circuit breaker; if the 195 * number of events received in the time span determined by the check interval goes 196 * below this threshold, the circuit breaker is closed again 197 */ 198 public EventCountCircuitBreaker(final int openingThreshold, final long checkInterval, final TimeUnit checkUnit, 199 final int closingThreshold) { 200 this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval, 201 checkUnit); 202 } 203 204 /** 205 * Creates a new instance of {@code EventCountCircuitBreaker} which uses the same parameters for 206 * opening and closing checks. 207 * 208 * @param threshold the threshold for changing the status of the circuit breaker; if 209 * the number of events received in a check interval is greater than this value, the 210 * circuit breaker is opened; if it is lower than this value, it is closed again 211 * @param checkInterval the check interval for opening or closing the circuit breaker 212 * @param checkUnit the {@code TimeUnit} defining the check interval 213 */ 214 public EventCountCircuitBreaker(final int threshold, final long checkInterval, final TimeUnit checkUnit) { 215 this(threshold, checkInterval, checkUnit, threshold); 216 } 217 218 /** 219 * Returns the threshold value for opening the circuit breaker. If this number of 220 * events is received in the time span determined by the opening interval, the circuit 221 * breaker is opened. 222 * 223 * @return the opening threshold 224 */ 225 public int getOpeningThreshold() { 226 return openingThreshold; 227 } 228 229 /** 230 * Returns the interval (in nanoseconds) for checking for the opening threshold. 231 * 232 * @return the opening check interval 233 */ 234 public long getOpeningInterval() { 235 return openingInterval; 236 } 237 238 /** 239 * Returns the threshold value for closing the circuit breaker. If the number of 240 * events received in the time span determined by the closing interval goes below this 241 * threshold, the circuit breaker is closed again. 242 * 243 * @return the closing threshold 244 */ 245 public int getClosingThreshold() { 246 return closingThreshold; 247 } 248 249 /** 250 * Returns the interval (in nanoseconds) for checking for the closing threshold. 251 * 252 * @return the opening check interval 253 */ 254 public long getClosingInterval() { 255 return closingInterval; 256 } 257 258 /** 259 * {@inheritDoc} This implementation checks the internal event counter against the 260 * threshold values and the check intervals. This may cause a state change of this 261 * circuit breaker. 262 */ 263 @Override 264 public boolean checkState() { 265 return performStateCheck(0); 266 } 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override 272 public boolean incrementAndCheckState(final Integer increment) 273 throws CircuitBreakingException { 274 return performStateCheck(1); 275 } 276 277 /** 278 * Increments the monitored value by <strong>1</strong> and performs a check of the current state of this 279 * circuit breaker. This method works like {@link #checkState()}, but the monitored 280 * value is incremented before the state check is performed. 281 * 282 * @return <strong>true</strong> if the circuit breaker is now closed; 283 * <strong>false</strong> otherwise 284 */ 285 public boolean incrementAndCheckState() { 286 return incrementAndCheckState(1); 287 } 288 289 /** 290 * {@inheritDoc} This circuit breaker may close itself again if the number of events 291 * received during a check interval goes below the closing threshold. If this circuit 292 * breaker is already open, this method has no effect, except that a new check 293 * interval is started. 294 */ 295 @Override 296 public void open() { 297 super.open(); 298 checkIntervalData.set(new CheckIntervalData(0, now())); 299 } 300 301 /** 302 * {@inheritDoc} A new check interval is started. If too many events are received in 303 * this interval, the circuit breaker changes again to state open. If this circuit 304 * breaker is already closed, this method has no effect, except that a new check 305 * interval is started. 306 */ 307 @Override 308 public void close() { 309 super.close(); 310 checkIntervalData.set(new CheckIntervalData(0, now())); 311 } 312 313 /** 314 * Actually checks the state of this circuit breaker and executes a state transition 315 * if necessary. 316 * 317 * @param increment the increment for the internal counter 318 * @return a flag whether the circuit breaker is now closed 319 */ 320 private boolean performStateCheck(final int increment) { 321 CheckIntervalData currentData; 322 CheckIntervalData nextData; 323 State currentState; 324 325 do { 326 final long time = now(); 327 currentState = state.get(); 328 currentData = checkIntervalData.get(); 329 nextData = nextCheckIntervalData(increment, currentData, currentState, time); 330 } while (!updateCheckIntervalData(currentData, nextData)); 331 332 // This might cause a race condition if other changes happen in between! 333 // Refer to the header comment! 334 if (stateStrategy(currentState).isStateTransition(this, currentData, nextData)) { 335 currentState = currentState.oppositeState(); 336 changeStateAndStartNewCheckInterval(currentState); 337 } 338 return !isOpen(currentState); 339 } 340 341 /** 342 * Updates the {@code CheckIntervalData} object. The current data object is replaced 343 * by the one modified by the last check. The return value indicates whether this was 344 * successful. If it is <strong>false</strong>, another thread interfered, and the 345 * whole operation has to be redone. 346 * 347 * @param currentData the current check data object 348 * @param nextData the replacing check data object 349 * @return a flag whether the update was successful 350 */ 351 private boolean updateCheckIntervalData(final CheckIntervalData currentData, 352 final CheckIntervalData nextData) { 353 return currentData == nextData 354 || checkIntervalData.compareAndSet(currentData, nextData); 355 } 356 357 /** 358 * Changes the state of this circuit breaker and also initializes a new 359 * {@code CheckIntervalData} object. 360 * 361 * @param newState the new state to be set 362 */ 363 private void changeStateAndStartNewCheckInterval(final State newState) { 364 changeState(newState); 365 checkIntervalData.set(new CheckIntervalData(0, now())); 366 } 367 368 /** 369 * Calculates the next {@code CheckIntervalData} object based on the current data and 370 * the current state. The next data object takes the counter increment and the current 371 * time into account. 372 * 373 * @param increment the increment for the internal counter 374 * @param currentData the current check data object 375 * @param currentState the current state of the circuit breaker 376 * @param time the current time 377 * @return the updated {@code CheckIntervalData} object 378 */ 379 private CheckIntervalData nextCheckIntervalData(final int increment, 380 final CheckIntervalData currentData, final State currentState, final long time) { 381 CheckIntervalData nextData; 382 if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) { 383 nextData = new CheckIntervalData(increment, time); 384 } else { 385 nextData = currentData.increment(increment); 386 } 387 return nextData; 388 } 389 390 /** 391 * Returns the current time in nanoseconds. This method is used to obtain the current 392 * time. This is needed to calculate the check intervals correctly. 393 * 394 * @return the current time in nanoseconds 395 */ 396 long now() { 397 return System.nanoTime(); 398 } 399 400 /** 401 * Returns the {@code StateStrategy} object responsible for the given state. 402 * 403 * @param state the state 404 * @return the corresponding {@code StateStrategy} 405 * @throws CircuitBreakingException if the strategy cannot be resolved 406 */ 407 private static StateStrategy stateStrategy(final State state) { 408 return STRATEGY_MAP.get(state); 409 } 410 411 /** 412 * Creates the map with strategy objects. It allows access for a strategy for a given 413 * state. 414 * 415 * @return the strategy map 416 */ 417 private static Map<State, StateStrategy> createStrategyMap() { 418 final Map<State, StateStrategy> map = new EnumMap<>(State.class); 419 map.put(State.CLOSED, new StateStrategyClosed()); 420 map.put(State.OPEN, new StateStrategyOpen()); 421 return map; 422 } 423 424 /** 425 * An internally used data class holding information about the checks performed by 426 * this class. Basically, the number of received events and the start time of the 427 * current check interval are stored. 428 */ 429 private static class CheckIntervalData { 430 /** The counter for events. */ 431 private final int eventCount; 432 433 /** The start time of the current check interval. */ 434 private final long checkIntervalStart; 435 436 /** 437 * Creates a new instance of {@code CheckIntervalData}. 438 * 439 * @param count the current count value 440 * @param intervalStart the start time of the check interval 441 */ 442 CheckIntervalData(final int count, final long intervalStart) { 443 eventCount = count; 444 checkIntervalStart = intervalStart; 445 } 446 447 /** 448 * Returns the event counter. 449 * 450 * @return the number of received events 451 */ 452 public int getEventCount() { 453 return eventCount; 454 } 455 456 /** 457 * Returns the start time of the current check interval. 458 * 459 * @return the check interval start time 460 */ 461 public long getCheckIntervalStart() { 462 return checkIntervalStart; 463 } 464 465 /** 466 * Returns a new instance of {@code CheckIntervalData} with the event counter 467 * incremented by the given delta. If the delta is 0, this object is returned. 468 * 469 * @param delta the delta 470 * @return the updated instance 471 */ 472 public CheckIntervalData increment(final int delta) { 473 return (delta != 0) ? new CheckIntervalData(getEventCount() + delta, 474 getCheckIntervalStart()) : this; 475 } 476 } 477 478 /** 479 * Internally used class for executing check logic based on the current state of the 480 * circuit breaker. Having this logic extracted into special classes avoids complex 481 * if-then-else cascades. 482 */ 483 private abstract static class StateStrategy { 484 /** 485 * Returns a flag whether the end of the current check interval is reached. 486 * 487 * @param breaker the {@code CircuitBreaker} 488 * @param currentData the current state object 489 * @param now the current time 490 * @return a flag whether the end of the current check interval is reached 491 */ 492 public boolean isCheckIntervalFinished(final EventCountCircuitBreaker breaker, 493 final CheckIntervalData currentData, final long now) { 494 return now - currentData.getCheckIntervalStart() > fetchCheckInterval(breaker); 495 } 496 497 /** 498 * Checks whether the specified {@code CheckIntervalData} objects indicate that a 499 * state transition should occur. Here the logic which checks for thresholds 500 * depending on the current state is implemented. 501 * 502 * @param breaker the {@code CircuitBreaker} 503 * @param currentData the current {@code CheckIntervalData} object 504 * @param nextData the updated {@code CheckIntervalData} object 505 * @return a flag whether a state transition should be performed 506 */ 507 public abstract boolean isStateTransition(EventCountCircuitBreaker breaker, 508 CheckIntervalData currentData, CheckIntervalData nextData); 509 510 /** 511 * Obtains the check interval to applied for the represented state from the given 512 * {@code CircuitBreaker}. 513 * 514 * @param breaker the {@code CircuitBreaker} 515 * @return the check interval to be applied 516 */ 517 protected abstract long fetchCheckInterval(EventCountCircuitBreaker breaker); 518 } 519 520 /** 521 * A specialized {@code StateStrategy} implementation for the state closed. 522 */ 523 private static class StateStrategyClosed extends StateStrategy { 524 525 /** 526 * {@inheritDoc} 527 */ 528 @Override 529 public boolean isStateTransition(final EventCountCircuitBreaker breaker, 530 final CheckIntervalData currentData, final CheckIntervalData nextData) { 531 return nextData.getEventCount() > breaker.getOpeningThreshold(); 532 } 533 534 /** 535 * {@inheritDoc} 536 */ 537 @Override 538 protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) { 539 return breaker.getOpeningInterval(); 540 } 541 } 542 543 /** 544 * A specialized {@code StateStrategy} implementation for the state open. 545 */ 546 private static class StateStrategyOpen extends StateStrategy { 547 /** 548 * {@inheritDoc} 549 */ 550 @Override 551 public boolean isStateTransition(final EventCountCircuitBreaker breaker, 552 final CheckIntervalData currentData, final CheckIntervalData nextData) { 553 return nextData.getCheckIntervalStart() != currentData 554 .getCheckIntervalStart() 555 && currentData.getEventCount() < breaker.getClosingThreshold(); 556 } 557 558 /** 559 * {@inheritDoc} 560 */ 561 @Override 562 protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) { 563 return breaker.getClosingInterval(); 564 } 565 } 566 567}