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.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 * @since 3.5 051 */ 052public class ThresholdCircuitBreaker extends AbstractCircuitBreaker<Long> { 053 /** 054 * The initial value of the internal counter. 055 */ 056 private static final long INITIAL_COUNT = 0L; 057 058 /** 059 * The threshold. 060 */ 061 private final long threshold; 062 063 /** 064 * Controls the amount used. 065 */ 066 private final AtomicLong used; 067 068 /** 069 * Creates a new instance of {@link ThresholdCircuitBreaker} and initializes the threshold. 070 * 071 * @param threshold the threshold. 072 */ 073 public ThresholdCircuitBreaker(final long threshold) { 074 this.used = new AtomicLong(INITIAL_COUNT); 075 this.threshold = threshold; 076 } 077 078 /** 079 * {@inheritDoc} 080 */ 081 @Override 082 public boolean checkState() { 083 return !isOpen(); 084 } 085 086 /** 087 * {@inheritDoc} 088 * 089 * <p>Resets the internal counter back to its initial value (zero).</p> 090 */ 091 @Override 092 public void close() { 093 super.close(); 094 this.used.set(INITIAL_COUNT); 095 } 096 097 /** 098 * Gets the threshold. 099 * 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}