1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.commons.lang3.concurrent; 18 19 import java.util.concurrent.atomic.AtomicLong; 20 21 /** 22 * A simple implementation of the <a 23 * href="https://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern 24 * that opens if the requested increment amount is greater than a given threshold. 25 * 26 * <p> 27 * It contains an internal counter that starts in zero, and each call increments the counter by a given amount. 28 * If the threshold is zero, the circuit breaker will be in a permanent <em>open</em> state. 29 * </p> 30 * 31 * <p> 32 * An example of use case could be a memory circuit breaker. 33 * </p> 34 * 35 * <pre> 36 * long threshold = 10L; 37 * ThresholdCircuitBreaker breaker = new ThresholdCircuitBreaker(10L); 38 * ... 39 * public void handleRequest(Request request) { 40 * long memoryUsed = estimateMemoryUsage(request); 41 * if (breaker.incrementAndCheckState(memoryUsed)) { 42 * // actually handle this request 43 * } else { 44 * // do something else, e.g. send an error code 45 * } 46 * } 47 * </pre> 48 * 49 * <p>#Thread safe#</p> 50 * @since 3.5 51 */ 52 public class ThresholdCircuitBreaker extends AbstractCircuitBreaker<Long> { 53 /** 54 * The initial value of the internal counter. 55 */ 56 private static final long INITIAL_COUNT = 0L; 57 58 /** 59 * The threshold. 60 */ 61 private final long threshold; 62 63 /** 64 * Controls the amount used. 65 */ 66 private final AtomicLong used; 67 68 /** 69 * Creates a new instance of {@link ThresholdCircuitBreaker} and initializes the threshold. 70 * 71 * @param threshold the threshold. 72 */ 73 public ThresholdCircuitBreaker(final long threshold) { 74 this.used = new AtomicLong(INITIAL_COUNT); 75 this.threshold = threshold; 76 } 77 78 /** 79 * {@inheritDoc} 80 */ 81 @Override 82 public boolean checkState() { 83 return !isOpen(); 84 } 85 86 /** 87 * {@inheritDoc} 88 * 89 * <p>Resets the internal counter back to its initial value (zero).</p> 90 */ 91 @Override 92 public void close() { 93 super.close(); 94 this.used.set(INITIAL_COUNT); 95 } 96 97 /** 98 * Gets the threshold. 99 * 100 * @return the threshold 101 */ 102 public long getThreshold() { 103 return threshold; 104 } 105 106 /** 107 * {@inheritDoc} 108 * 109 * <p>If the threshold is zero, the circuit breaker will be in a permanent <em>open</em> state.</p> 110 */ 111 @Override 112 public boolean incrementAndCheckState(final Long increment) { 113 if (threshold == 0) { 114 open(); 115 } 116 117 final long used = this.used.addAndGet(increment); 118 if (used > threshold) { 119 open(); 120 } 121 122 return checkState(); 123 } 124 125 }