【編碼樣例】限流框架
阿新 • • 發佈:2021-12-30
參考:極客時間 設計模式之美 專案實戰章節
重點考慮可擴充套件性:基於介面程式設計、提供抽象介面
包和類結構設計
// 重構前: 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; } }