/** * */ package com.github.phantomthief.thrift.client.utils; import static com.google.common.cache.CacheBuilder.newBuilder; import static com.google.common.collect.EvictingQueue.create; import static java.lang.Boolean.TRUE; import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MINUTES; import static org.slf4j.LoggerFactory.getLogger; import java.util.Set; import java.util.concurrent.ExecutionException; import org.slf4j.Logger; import com.google.common.cache.Cache; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; import com.google.common.collect.EvictingQueue; /** * <p> * FailoverCheckingStrategy class. * </p> * * @author w.vela * @version $Id: $Id */ public class FailoverCheckingStrategy<T> { private static final int DEFAULT_FAIL_COUNT = 10; private static final long DEFAULT_FAIL_DURATION = MINUTES.toMillis(1); private static final long DEFAULT_RECOVERY_DURATION = MINUTES.toMillis(3); private final Logger logger = getLogger(getClass()); private final long failDuration; private final Cache<T, Boolean> failedList; private final LoadingCache<T, EvictingQueue<Long>> failCountMap; /** * <p> * Constructor for FailoverCheckingStrategy. * </p> */ public FailoverCheckingStrategy() { this(DEFAULT_FAIL_COUNT, DEFAULT_FAIL_DURATION, DEFAULT_RECOVERY_DURATION); } /** * <p> * Constructor for FailoverCheckingStrategy. * </p> * * @param failDuration a long. * @param recoveryDuration a long. */ public FailoverCheckingStrategy(int failCount, long failDuration, long recoveryDuration) { this.failDuration = failDuration; this.failedList = newBuilder().weakKeys().expireAfterWrite(recoveryDuration, MILLISECONDS) .build(); this.failCountMap = newBuilder().weakKeys().build( new CacheLoader<T, EvictingQueue<Long>>() { @Override public EvictingQueue<Long> load(T key) throws Exception { return create(failCount); } }); } /** * <p> * getFailed. * </p> * * @return a {@link java.util.Set} object. */ public Set<T> getFailed() { return failedList.asMap().keySet(); } /** * <p> * fail. * </p> * * @param object a T object. */ public void fail(T object) { logger.trace("server {} failed.", object); boolean addToFail = false; try { EvictingQueue<Long> evictingQueue = failCountMap.get(object); synchronized (evictingQueue) { evictingQueue.add(System.currentTimeMillis()); if (evictingQueue.remainingCapacity() == 0 && evictingQueue.element() >= System.currentTimeMillis() - failDuration) { addToFail = true; } } } catch (ExecutionException e) { logger.error("Ops.", e); } if (addToFail) { failedList.put(object, TRUE); logger.trace("server {} failed. add to fail list.", object); } } }