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 * https://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.beans.PropertyChangeListener; 020import java.util.EnumMap; 021import java.util.Map; 022import java.util.concurrent.TimeUnit; 023import java.util.concurrent.atomic.AtomicReference; 024 025/** 026 * A simple implementation of the <a 027 * href="https://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern 028 * that counts specific events. 029 * 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 {@link 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 {@link 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 * {@link 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 {@link 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 {@link 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 * 137 * @since 3.5 138 */ 139public class EventCountCircuitBreaker extends AbstractCircuitBreaker<Integer> { 140 141 /** 142 * Internally used class for executing check logic based on the current state of the 143 * circuit breaker. Having this logic extracted into special classes avoids complex 144 * if-then-else cascades. 145 */ 146 private abstract static class AbstractStateStrategy { 147 148 /** 149 * Obtains the check interval to applied for the represented state from the given 150 * {@link CircuitBreaker}. 151 * 152 * @param breaker The {@link CircuitBreaker} 153 * @return The check interval to be applied 154 */ 155 protected abstract long fetchCheckInterval(EventCountCircuitBreaker breaker); 156 157 /** 158 * Tests whether the end of the current check interval is reached. 159 * 160 * @param breaker The {@link CircuitBreaker} 161 * @param currentData The current state object 162 * @param now The current time 163 * @return A flag whether the end of the current check interval is reached 164 */ 165 public boolean isCheckIntervalFinished(final EventCountCircuitBreaker breaker, 166 final CheckIntervalData currentData, final long now) { 167 return now - currentData.getCheckIntervalStart() > fetchCheckInterval(breaker); 168 } 169 170 /** 171 * Tests whether the specified {@link CheckIntervalData} objects indicate that a 172 * state transition should occur. Here the logic which checks for thresholds 173 * depending on the current state is implemented. 174 * 175 * @param breaker The {@link CircuitBreaker} 176 * @param currentData The current {@link CheckIntervalData} object 177 * @param nextData The updated {@link CheckIntervalData} object 178 * @return A flag whether a state transition should be performed 179 */ 180 public abstract boolean isStateTransition(EventCountCircuitBreaker breaker, 181 CheckIntervalData currentData, CheckIntervalData nextData); 182 } 183 184 /** 185 * An internally used data class holding information about the checks performed by 186 * this class. Basically, the number of received events and the start time of the 187 * current check interval are stored. 188 */ 189 private static final class CheckIntervalData { 190 191 /** The counter for events. */ 192 private final long eventCount; 193 194 /** The start time of the current check interval. */ 195 private final long checkIntervalStart; 196 197 /** 198 * Creates a new instance of {@link CheckIntervalData}. 199 * 200 * @param count The current count value 201 * @param intervalStart The start time of the check interval 202 */ 203 CheckIntervalData(final long count, final long intervalStart) { 204 eventCount = count; 205 checkIntervalStart = intervalStart; 206 } 207 208 /** 209 * Gets the start time of the current check interval. 210 * 211 * @return The check interval start time 212 */ 213 public long getCheckIntervalStart() { 214 return checkIntervalStart; 215 } 216 217 /** 218 * Gets the event counter. 219 * 220 * @return The number of received events 221 */ 222 public long getEventCount() { 223 return eventCount; 224 } 225 226 /** 227 * Returns a new instance of {@link CheckIntervalData} with the event counter incremented by the given delta. If the delta is 0, this object is 228 * returned. 229 * <p> 230 * If the increment would overflow, the event counter saturates at {@link Long#MAX_VALUE}. 231 * </p> 232 * 233 * @param delta The delta 234 * @return The updated instance 235 */ 236 public CheckIntervalData increment(final int delta) { 237 if (delta == 0) { 238 return this; 239 } 240 final long nextCount = getEventCount() + delta; 241 return new CheckIntervalData(nextCount < getEventCount() ? Long.MAX_VALUE : nextCount, getCheckIntervalStart()); 242 } 243 } 244 245 /** 246 * A specialized {@link AbstractStateStrategy} implementation for the state closed. 247 */ 248 private static final class StateStrategyClosed extends AbstractStateStrategy { 249 250 /** 251 * {@inheritDoc} 252 */ 253 @Override 254 protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) { 255 return breaker.getOpeningInterval(); 256 } 257 258 /** 259 * {@inheritDoc} 260 */ 261 @Override 262 public boolean isStateTransition(final EventCountCircuitBreaker breaker, 263 final CheckIntervalData currentData, final CheckIntervalData nextData) { 264 return nextData.getEventCount() > breaker.getOpeningThreshold(); 265 } 266 } 267 268 /** 269 * A specialized {@link AbstractStateStrategy} implementation for the state open. 270 */ 271 private static final class StateStrategyOpen extends AbstractStateStrategy { 272 273 /** 274 * {@inheritDoc} 275 */ 276 @Override 277 protected long fetchCheckInterval(final EventCountCircuitBreaker breaker) { 278 return breaker.getClosingInterval(); 279 } 280 281 /** 282 * {@inheritDoc} 283 */ 284 @Override 285 public boolean isStateTransition(final EventCountCircuitBreaker breaker, 286 final CheckIntervalData currentData, final CheckIntervalData nextData) { 287 return nextData.getCheckIntervalStart() != currentData 288 .getCheckIntervalStart() 289 && currentData.getEventCount() < breaker.getClosingThreshold(); 290 } 291 } 292 293 /** A map for accessing the strategy objects for the different states. */ 294 private static final Map<State, AbstractStateStrategy> STRATEGY_MAP = createStrategyMap(); 295 296 /** 297 * Creates the map with strategy objects. It allows access for a strategy for a given 298 * state. 299 * 300 * @return The strategy map 301 */ 302 private static Map<State, AbstractStateStrategy> createStrategyMap() { 303 final Map<State, AbstractStateStrategy> map = new EnumMap<>(State.class); 304 map.put(State.CLOSED, new StateStrategyClosed()); 305 map.put(State.OPEN, new StateStrategyOpen()); 306 return map; 307 } 308 309 /** 310 * Returns the {@link AbstractStateStrategy} object responsible for the given state. 311 * 312 * @param state The state 313 * @return The corresponding {@link AbstractStateStrategy} 314 * @throws CircuitBreakingException Thrown if the strategy cannot be resolved. 315 */ 316 private static AbstractStateStrategy stateStrategy(final State state) { 317 return STRATEGY_MAP.get(state); 318 } 319 320 /** Stores information about the current check interval. */ 321 private final AtomicReference<CheckIntervalData> checkIntervalData; 322 323 /** The threshold for opening the circuit breaker. */ 324 private final int openingThreshold; 325 326 /** The time interval for opening the circuit breaker. */ 327 private final long openingInterval; 328 329 /** The threshold for closing the circuit breaker. */ 330 private final int closingThreshold; 331 332 /** The time interval for closing the circuit breaker. */ 333 private final long closingInterval; 334 335 /** 336 * Creates a new instance of {@link EventCountCircuitBreaker} which uses the same parameters for 337 * opening and closing checks. 338 * 339 * @param threshold The threshold for changing the status of the circuit breaker; if 340 * the number of events received in a check interval is greater than this value, the 341 * circuit breaker is opened; if it is lower than this value, it is closed again 342 * @param checkInterval The check interval for opening or closing the circuit breaker 343 * @param checkUnit The {@link TimeUnit} defining the check interval 344 */ 345 public EventCountCircuitBreaker(final int threshold, final long checkInterval, final TimeUnit checkUnit) { 346 this(threshold, checkInterval, checkUnit, threshold); 347 } 348 349 /** 350 * Creates a new instance of {@link EventCountCircuitBreaker} with the same interval for opening 351 * and closing checks. 352 * 353 * @param openingThreshold The threshold for opening the circuit breaker; if this 354 * number of events is received in the time span determined by the check interval, the 355 * circuit breaker is opened 356 * @param checkInterval The check interval for opening or closing the circuit breaker 357 * @param checkUnit The {@link TimeUnit} defining the check interval 358 * @param closingThreshold The threshold for closing the circuit breaker; if the 359 * number of events received in the time span determined by the check interval goes 360 * below this threshold, the circuit breaker is closed again 361 */ 362 public EventCountCircuitBreaker(final int openingThreshold, final long checkInterval, final TimeUnit checkUnit, 363 final int closingThreshold) { 364 this(openingThreshold, checkInterval, checkUnit, closingThreshold, checkInterval, 365 checkUnit); 366 } 367 368 /** 369 * Creates a new instance of {@link EventCountCircuitBreaker} and initializes all properties for 370 * opening and closing it based on threshold values for events occurring in specific 371 * intervals. 372 * 373 * @param openingThreshold The threshold for opening the circuit breaker; if this 374 * number of events is received in the time span determined by the opening interval, 375 * the circuit breaker is opened 376 * @param openingInterval The interval for opening the circuit breaker 377 * @param openingUnit The {@link TimeUnit} defining the opening interval 378 * @param closingThreshold The threshold for closing the circuit breaker; if the 379 * number of events received in the time span determined by the closing interval goes 380 * below this threshold, the circuit breaker is closed again 381 * @param closingInterval The interval for closing the circuit breaker 382 * @param closingUnit The {@link TimeUnit} defining the closing interval 383 */ 384 public EventCountCircuitBreaker(final int openingThreshold, final long openingInterval, 385 final TimeUnit openingUnit, final int closingThreshold, final long closingInterval, 386 final TimeUnit closingUnit) { 387 checkIntervalData = new AtomicReference<>(new CheckIntervalData(0, 0)); 388 this.openingThreshold = openingThreshold; 389 this.openingInterval = openingUnit.toNanos(openingInterval); 390 this.closingThreshold = closingThreshold; 391 this.closingInterval = closingUnit.toNanos(closingInterval); 392 } 393 394 /** 395 * Changes the state of this circuit breaker and also initializes a new 396 * {@link CheckIntervalData} object. 397 * 398 * @param newState The new state to be set 399 */ 400 private void changeStateAndStartNewCheckInterval(final State newState) { 401 changeState(newState); 402 checkIntervalData.set(new CheckIntervalData(0, nanoTime())); 403 } 404 405 /** 406 * {@inheritDoc} 407 * <p> 408 * This implementation checks the internal event counter against the 409 * threshold values and the check intervals. This may cause a state change of this 410 * circuit breaker. 411 * </p> 412 */ 413 @Override 414 public boolean checkState() { 415 return performStateCheck(0); 416 } 417 418 /** 419 * {@inheritDoc} 420 * <p> 421 * A new check interval is started. If too many events are received in 422 * this interval, the circuit breaker changes again to state open. If this circuit 423 * breaker is already closed, this method has no effect, except that a new check 424 * interval is started. 425 * </p> 426 */ 427 @Override 428 public void close() { 429 super.close(); 430 checkIntervalData.set(new CheckIntervalData(0, nanoTime())); 431 } 432 433 /** 434 * Gets the interval (in nanoseconds) for checking for the closing threshold. 435 * 436 * @return The opening check interval 437 */ 438 public long getClosingInterval() { 439 return closingInterval; 440 } 441 442 /** 443 * Gets the threshold value for closing the circuit breaker. If the number of 444 * events received in the time span determined by the closing interval goes below this 445 * threshold, the circuit breaker is closed again. 446 * 447 * @return The closing threshold 448 */ 449 public int getClosingThreshold() { 450 return closingThreshold; 451 } 452 453 /** 454 * Gets the interval (in nanoseconds) for checking for the opening threshold. 455 * 456 * @return The opening check interval 457 */ 458 public long getOpeningInterval() { 459 return openingInterval; 460 } 461 462 /** 463 * Gets the threshold value for opening the circuit breaker. If this number of 464 * events is received in the time span determined by the opening interval, the circuit 465 * breaker is opened. 466 * 467 * @return The opening threshold 468 */ 469 public int getOpeningThreshold() { 470 return openingThreshold; 471 } 472 473 /** 474 * Increments the monitored value by <strong>1</strong> and performs a check of the current state of this 475 * circuit breaker. This method works like {@link #checkState()}, but the monitored 476 * value is incremented before the state check is performed. 477 * 478 * @return <strong>true</strong> if the circuit breaker is now closed; 479 * <strong>false</strong> otherwise 480 */ 481 public boolean incrementAndCheckState() { 482 return incrementAndCheckState(1); 483 } 484 485 /** 486 * {@inheritDoc} 487 * 488 * <p> 489 * The event count is a protective counter: negative increments are rejected, and an increment that would overflow 490 * {@link Long#MAX_VALUE} saturates the counter at {@link Long#MAX_VALUE} instead of wrapping negative. Saturation prevents 491 * an overloaded interval from causing an automatic close. The counter resets when a new check interval starts or the 492 * circuit breaker changes state. 493 * </p> 494 * 495 * @throws IllegalArgumentException Thrown if the increment is negative. 496 */ 497 @Override 498 public boolean incrementAndCheckState(final Integer increment) { 499 if (increment.intValue() < 0) { 500 throw new IllegalArgumentException("Increment must not be negative: " + increment); 501 } 502 return performStateCheck(increment); 503 } 504 505 /** 506 * Returns the current time in nanoseconds. This method is used to obtain the current 507 * time. This is needed to calculate the check intervals correctly. 508 * 509 * @return The current time in nanoseconds 510 */ 511 long nanoTime() { 512 return System.nanoTime(); 513 } 514 515 /** 516 * Calculates the next {@link CheckIntervalData} object based on the current data and 517 * the current state. The next data object takes the counter increment and the current 518 * time into account. 519 * 520 * @param increment The increment for the internal counter 521 * @param currentData The current check data object 522 * @param currentState The current state of the circuit breaker 523 * @param time The current time 524 * @return The updated {@link CheckIntervalData} object 525 */ 526 private CheckIntervalData nextCheckIntervalData(final int increment, 527 final CheckIntervalData currentData, final State currentState, final long time) { 528 final CheckIntervalData nextData; 529 if (stateStrategy(currentState).isCheckIntervalFinished(this, currentData, time)) { 530 nextData = new CheckIntervalData(increment, time); 531 } else { 532 nextData = currentData.increment(increment); 533 } 534 return nextData; 535 } 536 537 /** 538 * {@inheritDoc} 539 * <p> 540 * This circuit breaker may close itself again if the number of events 541 * received during a check interval goes below the closing threshold. If this circuit 542 * breaker is already open, this method has no effect, except that a new check 543 * interval is started. 544 * </p> 545 */ 546 @Override 547 public void open() { 548 super.open(); 549 checkIntervalData.set(new CheckIntervalData(0, nanoTime())); 550 } 551 552 /** 553 * Actually checks the state of this circuit breaker and executes a state transition 554 * if necessary. 555 * 556 * @param increment The increment for the internal counter 557 * @return A flag whether the circuit breaker is now closed 558 */ 559 private boolean performStateCheck(final int increment) { 560 CheckIntervalData currentData; 561 CheckIntervalData nextData; 562 State currentState; 563 564 do { 565 final long time = nanoTime(); 566 currentState = state.get(); 567 currentData = checkIntervalData.get(); 568 nextData = nextCheckIntervalData(increment, currentData, currentState, time); 569 } while (!updateCheckIntervalData(currentData, nextData)); 570 571 // This might cause a race condition if other changes happen in between! 572 // Refer to the header comment! 573 if (stateStrategy(currentState).isStateTransition(this, currentData, nextData)) { 574 currentState = currentState.oppositeState(); 575 changeStateAndStartNewCheckInterval(currentState); 576 } 577 return !isOpen(currentState); 578 } 579 580 /** 581 * Updates the {@link CheckIntervalData} object. The current data object is replaced 582 * by the one modified by the last check. The return value indicates whether this was 583 * successful. If it is <strong>false</strong>, another thread interfered, and the 584 * whole operation has to be redone. 585 * 586 * @param currentData The current check data object 587 * @param nextData The replacing check data object 588 * @return A flag whether the update was successful 589 */ 590 private boolean updateCheckIntervalData(final CheckIntervalData currentData, 591 final CheckIntervalData nextData) { 592 return currentData == nextData 593 || checkIntervalData.compareAndSet(currentData, nextData); 594 } 595 596}