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.util.concurrent.atomic.AtomicLong; 020 021/** 022 * A simple implementation of the <a 023 * href="https://martinfowler.com/bliki/CircuitBreaker.html">Circuit Breaker</a> pattern 024 * that opens if the requested increment amount is greater than a given threshold. 025 * 026 * <p> 027 * It contains an internal counter that starts in zero, and each call increments the counter by a given amount. 028 * If the threshold is zero, the circuit breaker will be in a permanent <em>open</em> state. 029 * </p> 030 * 031 * <p> 032 * An example of use case could be a memory circuit breaker. 033 * </p> 034 * 035 * <pre> 036 * long threshold = 10L; 037 * ThresholdCircuitBreaker breaker = new ThresholdCircuitBreaker(10L); 038 * ... 039 * public void handleRequest(Request request) { 040 * long memoryUsed = estimateMemoryUsage(request); 041 * if (breaker.incrementAndCheckState(memoryUsed)) { 042 * // actually handle this request 043 * } else { 044 * // do something else, e.g. send an error code 045 * } 046 * } 047 * </pre> 048 * 049 * <p>#Thread safe#</p> 050 * 051 * @since 3.5 052 */ 053public class ThresholdCircuitBreaker extends AbstractCircuitBreaker<Long> { 054 055 /** 056 * The initial value of the internal counter. 057 */ 058 private static final long INITIAL_COUNT = 0L; 059 060 /** 061 * The threshold. 062 */ 063 private final long threshold; 064 065 /** 066 * Controls the amount used. 067 */ 068 private final AtomicLong used; 069 070 /** 071 * Creates a new instance of {@link ThresholdCircuitBreaker} and initializes the threshold. 072 * 073 * @param threshold the threshold. 074 */ 075 public ThresholdCircuitBreaker(final long threshold) { 076 this.used = new AtomicLong(INITIAL_COUNT); 077 this.threshold = threshold; 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 @Override 084 public boolean checkState() { 085 return !isOpen(); 086 } 087 088 /** 089 * {@inheritDoc} 090 * 091 * <p>Resets the internal counter back to its initial value (zero).</p> 092 */ 093 @Override 094 public void close() { 095 super.close(); 096 this.used.set(INITIAL_COUNT); 097 } 098 099 /** 100 * Gets the threshold. 101 * 102 * @return the threshold 103 */ 104 public long getThreshold() { 105 return threshold; 106 } 107 108 /** 109 * {@inheritDoc} 110 * 111 * <p>If the threshold is zero, the circuit breaker will be in a permanent <em>open</em> state.</p> 112 */ 113 @Override 114 public boolean incrementAndCheckState(final Long increment) { 115 if (threshold == 0) { 116 open(); 117 } 118 119 final long used = this.used.addAndGet(increment); 120 if (used > threshold) { 121 open(); 122 } 123 124 return checkState(); 125 } 126 127}