CircuitBreaker breaker = new CircuitBreaker() .withFailureThreshold(5) .withSuccessThreshold(3) .withDelay(1, TimeUnit.MINUTES); Failsafe.with(breaker).run(() -> connect());
/** * Reports a successful service call to the {@link CircuitBreaker}, * putting the <code>CircuitBreaker</code> back into the CLOSED * state serving requests. */ protected void close() { state = BreakerState.CLOSED; isAttemptLive = false; notifyBreakerStateChange(getStatus()); }
protected void handleFailure(Throwable cause) throws Exception { if (failureInterpreter == null || failureInterpreter.shouldTrip(cause)) { this.tripException = cause; trip(); } else if (isAttemptLive) { close(); } if (cause instanceof Exception) { throw (Exception)cause; } else if (cause instanceof Error) { throw (Error)cause; } else { throw (RuntimeException)cause; } }
/** Wrap the given service call with the {@link CircuitBreaker} * protection logic. * @param c the {@link Callable} to attempt * @return whatever c would return on success * @throws CircuitBreakerException if the * breaker was OPEN or HALF_CLOSED and this attempt wasn't the * reset attempt * @throws Exception if <code>c</code> throws one during * execution */ public <V> V invoke(Callable<V> c) throws Exception { if (!byPass) { if (!allowRequest()) { throw mapException(new CircuitBreakerException()); } try { isAttemptLive = true; V result = c.call(); close(); return result; } catch (Throwable cause) { handleFailure(cause); } throw new IllegalStateException("not possible"); } else { return c.call(); } }
/** * Specifies the failure tolerance limit for the {@link * DefaultFailureInterpreter} that comes with a {@link * CircuitBreaker} by default. * @see DefaultFailureInterpreter * @param limit the number of tolerated failures in a window */ public void setLimit(int limit) { FailureInterpreter fi = getFailureInterpreter(); if (!(fi instanceof DefaultFailureInterpreter)) { throw new IllegalStateException("setLimit() not supported: this CircuitBreaker's FailureInterpreter isn't a DefaultFailureInterpreter."); } ((DefaultFailureInterpreter)fi).setLimit(limit); }
protected void configureCircuitBreaker(String name, CircuitBreaker circuit, CircuitBreakerConfig config) { long resetMillis = config.getResetMillis(); Long resetMillisOverride = getLongPropertyOverrideValue(name, RESETMILLIS_KEY); if (resetMillisOverride != null) { resetMillis = resetMillisOverride; } FailureInterpreter fi = config.getFailureInterpreter(); circuit.setFailureInterpreter(fi); if (resetMillis > 0) { circuit.setResetMillis(resetMillis); } if (fi instanceof DefaultFailureInterpreter) { configureDefaultFailureInterpreter(name, resetMillis, circuit); } else { logger.info( "Created CircuitBreaker '{}', resetMillis={}", new Object[] { name, resetMillis }); } }
/** * Manually trips the CircuitBreaker until {@link #reset()} is invoked. */ public void tripHard() { this.trip(); isHardTrip = true; }
/** Returns a {@link String} representation of the breaker's * status; potentially useful for exposing to monitoring software. * @return <code>String</code> which is <code>"GREEN"</code> if * the breaker is CLOSED; <code>"YELLOW"</code> if the breaker * is HALF_CLOSED; and <code>"RED"</code> if the breaker is * OPEN (tripped). */ public String getHealthCheck() { return getStatus().getSignal(); }
/** Sets the reset period to the given number of milliseconds. The * default is 15,000 (make one retry attempt every 15 seconds). * * @param l number of milliseconds to "cool down" after tripping * before allowing a "test request" through again */ @ManagedAttribute @Override public void setResetMillis(long l) { super.setResetMillis(l); }
/** * Returns the last exception that caused the breaker to trip, empty <code>String </code> * if never tripped. * * @return Throwable */ public String getTripExceptionAsString() { if (tripException == null) { return ""; } else { return getFullStackTrace(tripException); } }
/** * @return boolean whether the breaker will allow a request * through or not. */ protected boolean allowRequest() { if (this.isHardTrip) { return false; } else if (BreakerState.CLOSED == state) { return true; } if (BreakerState.OPEN == state && System.currentTimeMillis() - lastFailure.get() >= resetMillis.get()) { state = BreakerState.HALF_CLOSED; } return canAttempt(); }
/** * Get the current state of the {@link CircuitBreaker} byPass * * @return boolean the byPass flag's current value */ @ManagedAttribute @Override public boolean getByPassState() { return super.getByPassState(); }
/** * Get the current {@link ServiceStatus} of the * {@link CircuitBreaker}, including the name, * {@link org.fishwife.jrugged.Status}, and reason. * @return the {@link ServiceStatus}. */ public ServiceStatus getServiceStatus() { boolean canSendProbeRequest = !isHardTrip && lastFailure.get() > 0 && allowRequest(); if (byPass) { return new ServiceStatus(name, Status.DEGRADED, "Bypassed"); } switch(state) { case OPEN: return (canSendProbeRequest ? new ServiceStatus(name, Status.DEGRADED, "Send Probe Request") : new ServiceStatus(name, Status.DOWN, "Open")); case HALF_CLOSED: return new ServiceStatus(name, Status.DEGRADED, "Half Closed"); case CLOSED: default: return new ServiceStatus(name, Status.UP); } }
/** Wrap the given service call with the {@link CircuitBreaker} * protection logic. * @param r the {@link Runnable} to attempt * @throws CircuitBreakerException if the * breaker was OPEN or HALF_CLOSED and this attempt wasn't the * reset attempt * @throws Exception if <code>c</code> throws one during * execution */ public void invoke(Runnable r) throws Exception { if (!byPass) { if (!allowRequest()) { throw mapException(new CircuitBreakerException()); } try { isAttemptLive = true; r.run(); close(); return; } catch (Throwable cause) { handleFailure(cause); } throw new IllegalStateException("not possible"); } else { r.run(); } }
/** * Specifies the tolerance window in milliseconds for the {@link * DefaultFailureInterpreter} that comes with a {@link * CircuitBreaker} by default. * @see DefaultFailureInterpreter * @param windowMillis length of the window in milliseconds */ public void setWindowMillis(long windowMillis) { FailureInterpreter fi = getFailureInterpreter(); if (!(fi instanceof DefaultFailureInterpreter)) { throw new IllegalStateException("setWindowMillis() not supported: this CircuitBreaker's FailureInterpreter isn't a DefaultFailureInterpreter."); } ((DefaultFailureInterpreter)fi).setWindowMillis(windowMillis); }
/** * Manually trips the CircuitBreaker until {@link #reset()} is invoked. */ @ManagedOperation @Override public void trip() { super.trip(); }
if (!allowRequest()) { throw mapException(new CircuitBreakerException()); isAttemptLive = true; r.run(); close(); return result; } catch (Throwable cause) { handleFailure(cause);
/** * When called with true - causes the {@link CircuitBreaker} to byPass * its functionality allowing requests to be executed unmolested * until the <code>CircuitBreaker</code> is reset or the byPass * is manually set to false. * * @param b Set this breaker into bypass mode */ public void setByPassState(boolean b) { byPass = b; notifyBreakerStateChange(getStatus()); }
/** * Specifies a set of {@link Throwable} classes that should not * be considered failures by the {@link CircuitBreaker}. * @see DefaultFailureInterpreter * @param ignore a {@link java.util.Collection} of {@link Throwable} * classes */ public void setIgnore(Collection<Class<? extends Throwable>> ignore) { FailureInterpreter fi = getFailureInterpreter(); if (!(fi instanceof DefaultFailureInterpreter)) { throw new IllegalStateException("setIgnore() not supported: this CircuitBreaker's FailureInterpreter isn't a DefaultFailureInterpreter."); } @SuppressWarnings("unchecked") Class<? extends Throwable>[] classes = new Class[ignore.size()]; int i = 0; for(Class<? extends Throwable> c : ignore) { classes[i] = c; i++; } ((DefaultFailureInterpreter)fi).setIgnore(classes); }