package com.taobao.tddl.atom.utils; import java.io.ByteArrayInputStream; import java.util.Map; import java.util.Properties; import java.util.concurrent.atomic.AtomicInteger; import com.taobao.tddl.common.utils.logger.Logger; import com.taobao.tddl.common.utils.logger.LoggerFactory; /** * 计数惩罚器 在最近一段时间窗口内计数超过限制则惩罚 * * <pre> * * CountPunisher timeOutPunisher = new CountPunisher(new SmoothValve(x), 3000, 10); 3秒钟之内超时10次则惩罚 * if(timeOutPunisher.punish()){ * // 获取连接时,判断为超时惩罚期,抛出该异常 * } * * * timeOutPunisher.count(); // 判断为超时,计数一下 * </pre> * * @author linxuan */ public class CountPunisher { private static final Logger logger = LoggerFactory.getLogger(CountPunisher.class); private final SmoothValve smoothValve; private final long timeWindow; // ms; // 值为0表示关闭这个功能,无任何限制 private final long limit; private final long resetTime; private volatile long timeWindowBegin = 0; // 0表示没有超时,没有计时 private volatile long punishBeginTime = Long.MAX_VALUE; private final AtomicInteger count = new AtomicInteger(); /** * timeWindow之内,超时(或其他意义)次数超过limit,则标识为punish, 这个punish标志resetTime过后自动复位 * * @param smoothValve * @param timeWindow * @param limit */ public CountPunisher(SmoothValve smoothValve, long timeWindow, long limit, long resetTime){ this.smoothValve = smoothValve; this.timeWindow = timeWindow; this.limit = limit; this.resetTime = resetTime; } public CountPunisher(SmoothValve smoothValve, long timeWindow, long limit){ this(smoothValve, timeWindow, limit, 5 * 60 * 100); // 默认5分钟复位 } public void count() { if (timeWindow == 0) { return; } long now = System.currentTimeMillis(); if (now - timeWindowBegin > timeWindow) { resetTimeWindow(); } if (timeWindowBegin == 0) { timeWindowBegin = now; } if (count.incrementAndGet() >= limit) { punishBeginTime = now; smoothValve.setNotAvailable(); } } private void resetTimeWindow() { timeWindowBegin = 0; count.set(0); } /** * @return true表示惩罚;false表示通过 */ public boolean punish() { if (punishBeginTime == Long.MAX_VALUE || timeWindow == 0) { // 这里不需要复位 return false; } if (System.currentTimeMillis() - punishBeginTime > resetTime) { // 超过复位时间,不再惩罚,状态复位 punishBeginTime = Long.MAX_VALUE; resetTimeWindow(); smoothValve.setAvailable(); return false; } return !smoothValve.smoothThroughOnInitial(); } private static enum CreateProperties { timeWindow, limit, resetTime; } private static final String LINE_SEP = System.getProperty("line.separator"); public static CountPunisher parse(SmoothValve smoothValve, String str) { str = str.replaceAll("@@@", LINE_SEP); str = str.replaceAll("###", LINE_SEP); str = str.replaceAll("$$$", LINE_SEP); str = str.replaceAll(";;;", LINE_SEP); try { Properties p = new Properties(); p.load(new ByteArrayInputStream((str).getBytes())); long timeWindow = -1; long limit = -1; long resetTime = -1; for (Map.Entry<Object, Object> entry : p.entrySet()) { String key = ((String) entry.getKey()).trim(); String value = ((String) entry.getValue()).trim(); switch (CreateProperties.valueOf(key)) { case timeWindow: timeWindow = Long.parseLong(value); break; case limit: limit = Long.parseLong(value); break; case resetTime: resetTime = Long.parseLong(value); break; default: break; } } if (timeWindow == -1 || limit == -1) { logger.error("CountPunisher Properties incomplete"); return null; } if (resetTime == -1) { return new CountPunisher(smoothValve, timeWindow, limit); } else { return new CountPunisher(smoothValve, timeWindow, limit, resetTime); } } catch (Exception e) { logger.error("parse CountPunisher Properties failed", e); return null; } } public String toString() { return new StringBuilder("exceed ").append(limit) .append(" in ") .append(timeWindow) .append("ms, and will reset in ") .append(resetTime) .append("ms") .toString(); } }