zoukankan      html  css  js  c++  java
  • 【编码样例】限流框架

    参考:极客时间 设计模式之美 项目实战章节

    重点考虑可扩展性:基于接口编程、提供抽象接口

    优秀的代码是重构出来的,复杂的代码是慢慢堆砌出来的。小步快跑、逐步迭代是我比较推崇的开发模式。追求完美主义会让我们迟迟无法下手。所以,为了克服这个问题,一方面,我们可以规划多个小版本来开发,不断迭代优化;另一方面,在编程实现的过程中,我们可以先实现MVP代码,以此来优化重构。

    我们站在Code Reviewer的角度,结合SOLID、DRY、KISS、LOD、基于接口而非实现编程、高内聚松耦合等经典的设计思想和原则,以及编码规范,从代码质量评判标准的角度,来剖析代码在可读性、扩展性、可维护性、灵活、简洁、复用性、可测试性等方面的表现,并且针对性地去优化不足。

    包和类结构设计

    限流规则解析parse:支持多种格式的规则文件解析
    规则文件数据源: 支持数据库和本地文件类型多种限流算法
    // 重构前:
    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 {
        ApiLimit apiLimit = rule.getLimit(appId, url); 
       if (apiLimit == null) { return true; }

    // 获取api对应在内存中的限流计数器(rateLimitCounter)
    String counterKey = appId + ":" + apiLimit.getApi();
    RateLimitAlg rateLimitCounter = counters.get(counterKey);
    if (rateLimitCounter == null) {
        RateLimitAlg newRateLimitCounter = new RateLimitAlg(apiLimit.getLimit());
        rateLimitCounter = counters.putIfAbsent(counterKey, newRateLimitCounter);
        if (rateLimitCounter == null) {
            rateLimitCounter = newRateLimitCounter; }
        }

        // 判断是否限流
        return rateLimitCounter.tryAcquire();
    } }

    各个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; } }

     规则定义

    configs:
    - appId: app-1
      limits:
      - api: /v1/user
        limit: 100
        unit:60
      - api: /v1/order
        limit: 50
    - appId: app-2
      limits:
      - api: /v1/user
        limit: 50
      - api: /v1/order
        limit: 50

    规则模型

    public class RuleConfig {
      private List<AppRuleConfig> configs;
    
      public List<AppRuleConfig> getConfigs() {
        return configs;
      }
    
      public void setConfigs(List<AppRuleConfig> configs) {
        this.configs = configs;
      }
    
      public static class AppRuleConfig {
        private String appId;
        private List<ApiLimit> limits;
    
        public AppRuleConfig() {}
    
        public AppRuleConfig(String appId, List<ApiLimit> limits) {
          this.appId = appId;
          this.limits = limits;
        }
        //...省略getter、setter方法...
      }
    }
    
    public class ApiLimit {
      private static final int DEFAULT_TIME_UNIT = 1; // 1 second
      private String api;
      private int limit;
      private int unit = DEFAULT_TIME_UNIT;
    
      public ApiLimit() {}
    
      public ApiLimit(String api, int limit) {
        this(api, limit, DEFAULT_TIME_UNIT);
      }
    
      public ApiLimit(String api, int limit, int unit) {
        this.api = api;
        this.limit = limit;
        this.unit = unit;
      }
      // ...省略getter、setter方法...
    }
  • 相关阅读:
    ARM IIC接口
    ARM硬件问题转载
    自动排版
    书签
    ARM硬件问题转载
    ATPCS规则1
    开发硬件所需的知识
    今天很崩溃呀
    回顾一下
    ARM汇编条件码
  • 原文地址:https://www.cnblogs.com/clarino/p/15738130.html
Copyright © 2011-2022 走看看