1. 程式人生 > 其它 >【編碼樣例】限流框架

【編碼樣例】限流框架

參考:極客時間 設計模式之美 專案實戰章節

重點考慮可擴充套件性:基於介面程式設計、提供抽象介面

包和類結構設計

// 重構前:
com.xzg.ratelimiter
  --RateLimiter
com.xzg.ratelimiter.rule
  --ApiLimit
  --RuleConfig
  --RateLimitRule
com.xzg.ratelimiter.alg
  --RateLimitAlg
  
// 重構後:
com.xzg.ratelimiter
  --RateLimiter(有所修改)
com.xzg.ratelimiter.rule  // 限流規則內部模型
  
--ApiLimit(不變) --RuleConfig(不變) --RateLimitRule(抽象介面) --TrieRateLimitRule(實現類,就是重構前的RateLimitRule) com.xzg.ratelimiter.rule.parser // 限流規則解析 --RuleConfigParser(抽象介面) --YamlRuleConfigParser(Yaml格式配置檔案解析類) --JsonRuleConfigParser(Json格式配置檔案解析類) com.xzg.ratelimiter.rule.datasource --RuleConfigSource(抽象介面)
--FileRuleConfigSource(基於本地檔案的配置類) com.xzg.ratelimiter.alg // 實際限流演算法,時間視窗,令牌桶(支援以後擴充套件),策略模式優化 --RateLimitAlg(抽象介面) --FixedTimeWinRateLimitAlg(實現類,就是重構前的RateLimitAlg)

RateLimiter類:通過RuleConfigSource 實現配置檔案載入

public class RateLimiter {
  private static final Logger log = LoggerFactory.getLogger(RateLimiter.class
); // 為每個api在記憶體中儲存限流計數器 private ConcurrentHashMap<String, RateLimitAlg> counters = new ConcurrentHashMap<>(); private RateLimitRule rule; public RateLimiter() { //改動主要在這裡:呼叫RuleConfigSource類來實現配置載入 RuleConfigSource configSource = new FileRuleConfigSource(); RuleConfig ruleConfig = configSource.load(); this.rule = new TrieRateLimitRule(ruleConfig); } public boolean limit(String appId, String url) throws InternalErrorException, InvalidUrlException { //...程式碼不變... } }

各個Parser和RuleConfigSource類的設計有點類似策略模式,如果要新增新的格式的解析,只需要實現對應的Parser類,並且新增到FileRuleConfig類的PARSER_MAP中就可以了。

com.xzg.ratelimiter.rule.parser
  --RuleConfigParser(抽象介面)
  --YamlRuleConfigParser(Yaml格式配置檔案解析類)
  --JsonRuleConfigParser(Json格式配置檔案解析類)
com.xzg.ratelimiter.rule.datasource
  --RuleConfigSource(抽象介面)
  --FileRuleConfigSource(基於本地檔案的配置類)
  
public interface RuleConfigParser {
  RuleConfig parse(String configText);
  RuleConfig parse(InputStream in);
}

public interface RuleConfigSource {
  RuleConfig load();
}

public class FileRuleConfigSource implements RuleConfigSource {
  private static final Logger log = LoggerFactory.getLogger(FileRuleConfigSource.class);

  public static final String API_LIMIT_CONFIG_NAME = "ratelimiter-rule";
  public static final String YAML_EXTENSION = "yaml";
  public static final String YML_EXTENSION = "yml";
  public static final String JSON_EXTENSION = "json";

  private static final String[] SUPPORT_EXTENSIONS =
      new String[] {YAML_EXTENSION, YML_EXTENSION, JSON_EXTENSION};
  private static final Map<String, RuleConfigParser> PARSER_MAP = new HashMap<>();

  static {
    PARSER_MAP.put(YAML_EXTENSION, new YamlRuleConfigParser());
    PARSER_MAP.put(YML_EXTENSION, new YamlRuleConfigParser());
    PARSER_MAP.put(JSON_EXTENSION, new JsonRuleConfigParser());
  }

  @Override
  public RuleConfig load() {
    for (String extension : SUPPORT_EXTENSIONS) {
      InputStream in = null;
      try {
        in = this.getClass().getResourceAsStream("/" + getFileNameByExt(extension));
        if (in != null) {
          RuleConfigParser parser = PARSER_MAP.get(extension);
          return parser.parse(in);
        }
      } finally {
        if (in != null) {
          try {
            in.close();
          } catch (IOException e) {
            log.error("close file error:{}", e);
          }
        }
      }
    }
    return null;
  }

  private String getFileNameByExt(String extension) {
    return API_LIMIT_CONFIG_NAME + "." + extension;
  }
}