事件过滤器框架

框架概述

  • 该事件处理框架通过一系列的事件过滤器(EventFilter)来处理事件。每个事件过滤器可以看作是管道(Pipeline)中的一个处理单元,事件(Event)会按顺序流经这些单元进行处理

  • 框架支持注解、缓存、配置管理以及线程池管理,以实现高效、灵活的事件处理

框架功能

该框架提供以下核心功能:

  1. 事件上下文管理: 每个事件都拥有自己的上下文(EventContext),在过滤器链中传递,用于共享和管理事件状态

  2. 多方式创建过滤器链:支持多种方式创建过滤器链,基于注解的、基于本地配置的、基于远程配置的

  3. 静态执行: 通过注解或者是本地配置的方式动态构建事件过滤器管道,按构建时的顺序执行每一个事件过滤器。

  4. 动态执行: 通过远程配置的方式动态构建事件过滤器链管道,可以不修改代码,不重启服务的情况下动态调整事件过滤器执行顺序。

  5. 缓存机制: 针对通过注解或本地配置构建出来过滤器链,可以被缓存起来避免反复创建过滤器对象,提高事件处理的效率。

  6. 异步机制: 支持异步处理,采用线程池来处理事件,可以并发处理多个事件,提高框架处理能力。

  7. 错误处理: 通过错误处理接口(ErrorHandler)统一处理过滤器链中发生的异常,在异常实现类中进行业务逻辑处理。

框架迭代

  • 从一个基本的简单的基于责任链模式实现的事件过滤器链,慢慢的进行完善,功能补充,形成目前的事件过滤器框架

初始版本

  • 最初版本是从B站上的一个老师发布的视频里面学习到的,下面介绍下最开始的这个版本的使用方式和核心代码
  • www.bilibili.com/video/BV1Uo...

基本使用

1、创建业务上下文,需要继承 AbstractEventContext

java 复制代码
public class ChargeContext extends AbstractEventContext {

  @Setter
  @Getter
  private ChargeRequest chargeRequest;

  @Setter
  @Getter
  private Car car;

  @Setter
  @Getter
  private ChargeModel chargeModel;

  public ChargeContext(BizEnum bizEnum,
      FilterSelector selector) {
    super(bizEnum, selector);
  }

  @Override
  public boolean continueChain() {
    return true;
  }
}

2、定义自己的业务处理器,需要继承 AbstractEventFilter,并将所有的业务过滤器添加到 Pipeline

java 复制代码
@Slf4j
@RequiredArgsConstructor
public class CarInfoQueryFilter extends AbstractEventFilter<ChargeContext> {

    //filter 里面 查询缓存,使用消息 ,调用其他服务
    private final IFacadeService facadeService;

    @Override
    protected void handle(ChargeContext context) {
        if (Objects.isNull(context.getCar())) {
            ChargeRequest chargeRequest = context.getChargeRequest();
            String carNo = chargeRequest.getCarNo();
            Car car = facadeService.getCarInfoByCarNO(carNo);
            context.setCar(car);
            ChargeModel chargeModel = new ChargeModel();
            context.setChargeModel(chargeModel);
        }
        log.info("查询车辆信息并且放入上下文中");

    }
}
java 复制代码
@Configuration
public class ChargePipelineConfig {

  @Autowired
  private  IFacadeService facadeService;

  @Bean
  public FilterChainPipeline chargePipeline(){
    FilterChainPipeline filterChainPipeline = new FilterChainPipeline();
    filterChainPipeline.addFirst("用户逻辑",userPayFilter());
    filterChainPipeline.addFirst("车辆信息判断",judgeCarFilter());
    filterChainPipeline.addFirst("车辆信息查询", carInfoQueryFilter());
    filterChainPipeline.addFirst("log存储",logSaveFilter());
    return filterChainPipeline;
  }

  @Bean
  public CarInfoQueryFilter carInfoQueryFilter(){
    return new CarInfoQueryFilter(facadeService);
  }

  @Bean
  public JudgeCarFilter judgeCarFilter(){
    return new JudgeCarFilter();
  }

  @Bean
  public LogSaveFilter logSaveFilter(){
    return new LogSaveFilter();
  }

  @Bean
  public UserPayFilter userPayFilter(){
    return new UserPayFilter();
  }


}

3、配置需要执行的业务处理器,并读取配置

注意这里配置的是业务过滤器的类名!!!

yml 复制代码
charge:
  configs:
    YW1:
      - CarInfoQueryFilter
      - LogSaveFilter
      - UserPayFilter
      - JudgeCarFilter
java 复制代码
@Slf4j
@RequiredArgsConstructor
@Service
public class EnvBasedFilterSelectorFactory implements ChargeFilterSelectorFactory{

  private final ChargeFilterConfigProperties properties;

  @Override
  public FilterSelector getFilterSelector(ChargeRequest request) {
    String bizCode = request.getBizCode();
    if(StringUtils.hasLength(bizCode)){
      List<String> filterNames = properties.getConfigs()
          .getOrDefault(bizCode, Collections.unmodifiableList(new ArrayList<>()));
      return new LocalListBasedFilterSelector(filterNames);
    }
    return null;
  }
}

4、在 Service 中调用

java 复制代码
@Service
@RequiredArgsConstructor
public class ChargeServiceImpl implements IChargeService {

    private final FilterChainPipeline chargePipeline;

    private final ChargeFilterSelectorFactory chargeFilterSelectorFactory;

    @Override
    public void handle(ChargeRequest chargeRequest) {
        // 根据入参获取业务编码,根据业务编码获取到配置好的需要执行的过滤器
        FilterSelector filterSelector = chargeFilterSelectorFactory.getFilterSelector(chargeRequest);
        ChargeContext chargeContext = new ChargeContext(BizEnum.METRIC_EVENT, filterSelector);
        chargeContext.setChargeRequest(chargeRequest);
        
        // 获取到过滤器链,并执行过滤器链
        chargePipeline.getFilterChain().handle(chargeContext);
    }
}

5、测试

  • YW1 需要执行全部的业务过滤器
  • YW2 只需要执行其中一部分业务过滤器

核心代码

  • 在我们使用过滤器链的时候,可以发现几个关键的点

    • 通过 getFilterSelector 获取要执行的业务过滤器
    • 通过 getFilterChain 获取构建好的过滤器链
    • 通过 handle 方法执行所有的业务逻辑
  • 提供获取要执行的过滤器的接口,其最终是读取配置,还是读取注解,亦或者是其他方式自己实现即可

java 复制代码
public interface ChargeFilterSelectorFactory {
  FilterSelector  getFilterSelector(ChargeRequest request);
}
  • 提供 addFirst 方法来构建 Pipeline 过滤器链,getFilterChain 获取构造好的过滤器链
java 复制代码
public class FilterChainPipeline<T extends EventFilter> {
  private DefaultFilterChain last;

  public FilterChainPipeline() {
  }

  public DefaultFilterChain getFilterChain() {
    return this.last;
  }

  public FilterChainPipeline addFirst(String desc, T filter) {
    DefaultFilterChain newChain = new DefaultFilterChain(this.last, filter);
    this.last = newChain;
    return this;
  }
}
  • 提供 handle 方法来执行整个业务逻辑
java 复制代码
public class DefaultFilterChain<T extends EventContext> implements EventFilterChain<T> {

  private EventFilterChain<T> next;
  private EventFilter<T> filter;

  public DefaultFilterChain(EventFilterChain chain, EventFilter filter) {
    this.next = chain;
    this.filter = filter;
  }

  @Override
  public void handle(T context) {
    filter.doFilter(context, this);
  }

  @Override
  public void fireNext(T ctx) {
    EventFilterChain nextChain = this.next;
    if (Objects.nonNull(nextChain)) {
      nextChain.handle(ctx);
    }
  }
}

遍历过滤器链根据是否匹配来决定是否执行,这个是关键

java 复制代码
public abstract class AbstractEventFilter<T extends EventContext> implements EventFilter<T> {

  @Override
  public void doFilter(T context, EventFilterChain chain) {
    if (context.getFilterSelector().matchFilter(this.getClass().getSimpleName())) {
      handle(context);
    }
    if (context.continueChain()) {
      chain.fireNext(context);
    }
  }

  protected abstract void handle(T context);
}

不足及可扩展点

  • 业务过滤器 Filter 无法指定执行顺序,一旦被添加到Pipeline中,无法进行动态调整

  • 缺少异常处理机制

  • 配置化的过滤器链:可以提供一种机制来允许从外部配置源(如文件、数据库)构建过滤器链,这样就可以在不修改代码的情况下更改过滤器的行为

  • 支持异步处理:当前的过滤器链是同步执行的。可以考虑添加异步处理的支持,使得过滤器可以异步地执行,这对于需要长时间运行的任务或者依赖于外部服务调用的过滤器来说非常有用

  • 注解支持:可以通过自定义注解来标记过滤器,增加更多样的方式来构建过滤器链

  • 缓存机制:可以考虑添加缓存机制,避免重复创建过滤器实例

优化方案

无法动态执行事件过滤器

  • 提供 priority 配置来指定过滤器执行顺序

  • FilterFactory 中提供基于注解、本地配置的方式来保证过滤器的顺序执行,提供给基于远程配置的方式来保证过滤器的动态执行

异常处理

  • 提供 ErrorHandler 接口,根据业务场景自行实现该接口

注解支持

  • 提供 EventFilterAnnotation 注解,以及 createFilterChainPipelineWithAnno 方法来实现通过注解构造过滤器链

缓存机制

  • 增加 FilterChainCache,使用 ConcurrentHashMap 来缓存已经创建好的事件过滤器链,避免重复创建创建

异步处理

  • 提供 handleAsync 方法,其内部通过 ExecutorServiceCompletableFuture 完成异步处理

调整过滤器执行顺序

实现思路

  • 放弃原先通过写死的代码的方式去创建 Pipeline

  • 修改原有的配置文件的内容,根据配置的全类名来动态生成 Bean 对象

  • 新增加一个 priority 属性,根据读取到的 priority 配置,按照大小顺序进行组装过滤器链

实现代码

  • 事件过滤器本地配置
yml 复制代码
pipeline:
  businessFilters:
    YW1:
      - name: "com.design.elegant.filters.CarInfoQueryFilter"
        priority: 7
      - name: "com.design.elegant.filters.LogSaveFilter"
        priority: 4
      - name: "com.design.elegant.filters.UserPayFilter"
        priority: 1
      - name: "com.design.elegant.filters.JudgeCarFilter"
        priority: 3
java 复制代码
/**
 * 事件过滤器本地配置
 *
 * @author: xudehui1
 * @date: 2023-12-02 15:49
 */
@Configuration
@ConfigurationProperties(prefix = "pipeline")
public class FilterConfiguration {

    private Map<String, List<FilterDefinition>> businessFilters;

    @Data
    public static class FilterDefinition {
        private String name;
        private int priority;
        // ... 可能还有其他配置项
    }

    public Map<String, List<FilterDefinition>> getBusinessFilters() {
        return businessFilters;
    }

    public void setBusinessFilters(Map<String, List<FilterDefinition>> businessFilters) {
        this.businessFilters = businessFilters;
    }
}
  • 提供 FilterFactory 工厂,根据业务编码获取到对应的过滤器链
java 复制代码
@Component
public class FilterFactory {

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private FilterConfiguration filterConfiguration;

    /**
     * 基于读取配置文件+业务编码动态组装过滤器链
     *
     * @param baseEnum
     * @return
     */
    public FilterChainPipeline createFilterChainPipeline(BaseEnum baseEnum) {
        List<FilterConfiguration.FilterDefinition> definitions = filterConfiguration.getBusinessFilters().get(baseEnum.getCode());
        if (definitions == null) {
            throw new IllegalStateException("No filter definitions found for business code: " + baseEnum.getCode());
        }
        return buildPipeline(definitions);
    }

    private FilterChainPipeline<EventFilter> buildPipeline(List<FilterConfiguration.FilterDefinition> definitions) {
        FilterChainPipeline<EventFilter> pipeline = new FilterChainPipeline<>();
        definitions.stream()
                .sorted(Comparator.comparingInt(FilterConfiguration.FilterDefinition::getPriority))
                .forEachOrdered(definition -> {
                    EventFilter filter = createFilter(definition);
                    pipeline.addFirst(filter);
                });
        return pipeline;
    }

    private EventFilter createFilter(FilterConfiguration.FilterDefinition definition) {
        try {
            // 通过 Spring ApplicationContext 获取 Bean
            EventFilter filter =  (EventFilter) applicationContext.getBean(Class.forName(definition.getName()));
            filter.setPriority(definition.getPriority());
            return filter;
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Failed to create filter instance for: " + definition.getName(), e);
        }
    }

}

如何使用

java 复制代码
@Service
public class ChargeServiceImpl implements IChargeService{

  @Resource
  private ChargeFilterSelectorFactory chargeFilterSelectorFactory;

  @Autowired
  private FilterFactory filterFactory;

  @Override
  public void handle(ChargeRequest chargeRequest) {
    FilterSelector filterSelector = chargeFilterSelectorFactory.getFilterSelector(chargeRequest);

    boolean present = BizEnum.of(chargeRequest.getBizCode()).isPresent();
    if (!present) {
      return;
    }
    BizEnum bizEnum = BizEnum.of(chargeRequest.getBizCode()).get();
    ChargeContext chargeContext = new ChargeContext(bizEnum,filterSelector);
    chargeContext.setChargeRequest(chargeRequest);
    FilterChainPipeline pipeline = filterFactory.createFilterChainPipeline(chargeContext.getBizCode());
    pipeline.getFilterChain().handle(chargeContext);
  }
}

动态调整过滤器执行顺序

实现思路

  • 要实现不重启服务就可以动态调整就意味着每一次请求进来都需要去读取到最新的配置

  • 因为 SpringBoot 不支持监听本地配置的变化(SpringCloud 支持),那么这个配置就不能放在本地配置中

  • 需要存放在远程的配置中心,则可以随时调整配置中的 priority 属性来实现动态执行过滤器顺序

  • 考虑到很多时候没有配置中心,则可以使用数据库来存储配置。那么这就意味着需要对配置文件进行再一次抽象,保证数据库和配置中心都能够也能存放

  • 那就只有是 JSON 字符串了,将 JSON 字符串反序列化成我们的配置对象

实现代码

java 复制代码
@Component
@Slf4j
public class FilterFactory {


    private final ApplicationContext applicationContext;
    private final FilterConfiguration filterConfiguration;

    public FilterFactory(ApplicationContext applicationContext, FilterConfiguration filterConfiguration) {
        this.applicationContext = applicationContext;
        this.filterConfiguration = filterConfiguration;
    }

    /**
     * 基于传入的过滤器配置对象(JSON 格式)+业务编码动态组装过滤器链
     *
     * @param jsonStrConfig
     * @param baseEnum
     * @return
     */
    public FilterChainPipeline createFilterChainPipelineWithRemoteConfig(String jsonStrConfig, BaseEnum baseEnum) {
        FilterConfiguration filterConfiguration = null;
        try {
            filterConfiguration = JSON.parseObject(jsonStrConfig, new TypeReference<FilterConfiguration>() {
            });
        } catch (Exception e) {
            log.error("Serialization exception occurred for remote config: {}", jsonStrConfig, e);
            throw new RuntimeException("Serialization exception occurred for remote config");
        }

        Map<String, List<FilterConfiguration.FilterDefinition>> businessFilters = filterConfiguration.getBusinessFilters();
        List<FilterConfiguration.FilterDefinition> definitions = businessFilters.get(baseEnum.getCode());
        if (definitions == null) {
            throw new IllegalStateException("No filter definitions found for business code: " + baseEnum.getCode());
        }
        return buildPipeline(definitions);
    }

    private FilterChainPipeline<EventFilter> buildPipeline(List<FilterConfiguration.FilterDefinition> definitions) {
        FilterChainPipeline<EventFilter> pipeline = new FilterChainPipeline<>();
        definitions.stream()
                .sorted(Comparator.comparingInt(FilterConfiguration.FilterDefinition::getPriority))
                .forEachOrdered(definition -> {
                    EventFilter filter = createFilter(definition);
                    pipeline.addFirst(filter);
                });
        return pipeline;
    }

    private EventFilter createFilter(FilterConfiguration.FilterDefinition definition) {
        try {
            // 通过 Spring ApplicationContext 获取 Bean
            EventFilter filter = (EventFilter) applicationContext.getBean(Class.forName(definition.getName()));
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Failed to create filter instance for: " + definition.getName(), e);
        }
    }

异常处理

实现思路

  • 提供给全局异常处理器接口 ErrorHandler

  • 使用方实现该接口,在实现 handleError 方法时进行自己的业务异常处理

  • 将实现 ErrorHandler 接口的异常处理类加入过滤器链中,保证出现异常的时候能够执行大异常处理类

实现代码

java 复制代码
/**
 * 异常处理器
 *
 * @author: xudehui1
 * @date: 2023-12-02 20:47
 */
public interface ErrorHandler {
    void handleError(Exception e, EventContext context) throws Exception;
}
java 复制代码
public class DefaultFilterChain<T extends EventContext> implements EventFilterChain<T> {

  private EventFilterChain<T> next;
  private EventFilter<T> filter;
  private ErrorHandler errorHandler;

  public DefaultFilterChain(EventFilterChain chain, EventFilter filter) {
    this.next = chain;
    this.filter = filter;
  }

  /**
   * 同步事件过滤器处理方法
   *
   * @param context
   */
  @Override
  public void handle(T context) {
    try {
      filter.doFilter(context, this);
    } catch (Exception e) {
      if (errorHandler != null) {
        try {
          errorHandler.handleError(e, context);
        } catch (Exception ex) {
          throw new RuntimeException(ex);
        }
      } else {
        throw e; // 如果没有设置错误处理器,则将异常继续抛出
      }
    }
  }

  // 提供设置异常处理类的方法
  @Override
  public void setErrorHandler(ErrorHandler errorHandler) {
    this.errorHandler = errorHandler;
  }
}

如何使用

  • 实现 ErrorHandler 接口
java 复制代码
@Slf4j
@Component
public class ExceptionHandler implements ErrorHandler {
    @Override
    public void handleError(Exception e, EventContext context) throws Exception {
        if (e instanceof BizException) {
            // 有些业务异常不能直接抛给调用方
            // 比如:发生了某一个业务异常,前端需要根据这个异常场景进行处理,那么就不能直接给前端返回 success = false,需要 success =  true,然后根据 context 中某些值进行业务处理
            log.error("使用 ExceptionHandler 进行异常处理-发生了业务异常-可以针对异常码进行业务逻辑-异常码:{}", ((BizException) e).getBizCode());
        } else {
            log.error("异常:", e);
            throw e;
        }
    }
}
  • 设置异常处理类到过滤器链中
java 复制代码
    @Autowired
    private ExceptionHandler exceptionHandler;

    @Override
    public void handle(ChargeRequest chargeRequest) {
        FilterSelector filterSelector = chargeFilterSelectorFactory.getFilterSelector(chargeRequest);

        boolean present = BizEnum.of(chargeRequest.getBizCode()).isPresent();
        if (!present) {
            return;
        }
        BizEnum bizEnum = BizEnum.of(chargeRequest.getBizCode()).get();
        ChargeContext chargeContext = new ChargeContext(bizEnum, filterSelector);
        chargeContext.setChargeRequest(chargeRequest);
        FilterChainPipeline pipeline = filterFactory.createPipelineFromLocalConfig(chargeContext.getBizCode());
        
        // 设置异常处理类到过滤器链中
        pipeline.setErrorHandler(exceptionHandler);
        pipeline.getFilterChain().handle(chargeContext);
    }
}

注解机制

实现思路

  • 读取配置文件创建配置对象后根据配置对象,然后根据业务编码过滤出需要的配置对象,再创建过滤器链其本质就是根据全类名取生成 Bean 对象,最后根据 priority 属性排序后创建
  • 那么注解的思路也是一样的
    • 提供 bizCode 属性,过滤出需要的业务过滤器对象
    • priority 属性,按照顺序创建过滤器链

实现代码

  • EventFilterAnnotation 注解
java 复制代码
/**
 * 事件过滤器注解
 *
 * @author: xudehui1
 * @date: 2023-12-03 22:06
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface EventFilterAnnotation {
    String bizCode();
    int priority();
}
  • FilterFactory 工厂提供基于注解生成过滤器链的方法
java 复制代码
@Component
public class FilterFactory {

    private final ApplicationContext applicationContext;
    private final FilterConfiguration filterConfiguration;

    public FilterFactory(ApplicationContext applicationContext, FilterConfiguration filterConfiguration) {
        this.applicationContext = applicationContext;
        this.filterConfiguration = filterConfiguration;
    }


    public FilterChainPipeline createFilterChainPipelineWithAnno(BaseEnum baseEnum) {
        FilterChainPipeline<EventFilter> pipeline = new FilterChainPipeline<>();

        List<Class<?>> filterClasses = Arrays.asList(applicationContext.getBeanNamesForType(EventFilter.class))
                .stream()
                .map(beanName -> applicationContext.getType(beanName))
                .collect(Collectors.toList());

        List<EventFilter> eventFilterList = filterClasses.stream()
                .filter(filterClass -> {
                    EventFilterAnnotation annotation = filterClass.getAnnotation(EventFilterAnnotation.class);
                    return annotation != null && baseEnum.getCode().equals(annotation.bizCode());
                })
                .map(this::createFilter)
                .sorted(Comparator.comparingInt(EventFilter::getPriority))
                .collect(Collectors.toList());

        eventFilterList.forEach(filter -> pipeline.addFirst(filter));

        return pipeline;
    }

    private EventFilter createFilter(Class<?> filterClass) {
        EventFilterAnnotation annotation = filterClass.getAnnotation(EventFilterAnnotation.class);
        int priority = annotation.priority();
        EventFilter filter;
        try {
            filter = (EventFilter) applicationContext.getBean(filterClass);
        } catch (BeansException e) {
            throw new RuntimeException("Failed to create filter instance for: " + filterClass, e);
        }
        filter.setPriority(priority);

        return filter;
    }

}

如何使用

java 复制代码
@Slf4j
@Component
@EventFilterAnnotation(bizCode = "YW3", priority = 2)
public class AddressInfoQueryFilter extends AbstractEventFilter<ChargeContext> {

  @Resource
  private IFacadeService facadeService;

  @Override
  protected void handle(ChargeContext context) {
    if(Objects.isNull(context.getAddress())){
      ChargeRequest chargeRequest = context.getChargeRequest();
      Long addressId = chargeRequest.getAddressId();
      Address address = facadeService.getAddressByAddressId(addressId);
      context.setAddress(address);
      ChargeModel chargeModel = new ChargeModel();
      context.setChargeModel(chargeModel);
    }
    log.info("查询地址信息并且放入上下文中");
  }
}
java 复制代码
@Slf4j
@Component
@EventFilterAnnotation(bizCode = "YW3", priority = 1)
public class JudgeAddressFilter extends AbstractEventFilter<ChargeContext> {

  @Override
  protected void handle(ChargeContext context) {
    Address address = context.getAddress();

    log.info("地址信息:{}", address.getId());
  }
}
ini 复制代码
@Override
public void handleWithAnno(ChargeRequest chargeRequest) {
    FilterSelector filterSelector = chargeFilterSelectorFactory.getFilterSelector(chargeRequest);

    boolean present = BizEnum.of(chargeRequest.getBizCode()).isPresent();
    if (!present) {
        return;
    }
    BizEnum bizEnum = BizEnum.of(chargeRequest.getBizCode()).get();
    ChargeContext chargeContext = new ChargeContext(bizEnum, filterSelector);
    chargeContext.setChargeRequest(chargeRequest);
    FilterChainPipeline pipeline = filterFactory.createFilterChainPipelineWithAnno(chargeContext.getBizCode());
    pipeline.setErrorHandler(exceptionHandler);
    pipeline.getFilterChain().handle(chargeContext);
}

缓存机制

实现思路

  • 哪些场景可以放在缓存中

    • 基于本地配置和基于注解的方式创建出来的每一个业务过滤器链是可以放到缓存中的,因为在服务运行的过程中他们的执行顺序是不会改变的

    • 基于远程配置创建出来的业务过滤器链不可以放到缓存中,因为本身就是要动态的调整执行顺序,就不能放在缓存中

  • 使用什么来缓存

    • 使用 ConcurrentHashMap

实现代码

java 复制代码
/**
 * 缓存过滤器链
 *
 * @author coderxdh
 * @date 2023/12/6 23:55
 */
@Slf4j
public class FilterChainCache {
    private static final Map<String, FilterChainPipeline> cache = new ConcurrentHashMap<>();

    public static boolean containsKey(String key) {
        boolean result = cache.containsKey(key);
        log.info("cache contain pipeline: {}", result);
        return result;
    }

    public static FilterChainPipeline get(String key) {
        log.info("Get the pipeline with key :{} from the cache", key);
        return cache.get(key);
    }

    public static void put(String key, FilterChainPipeline value) {
        log.info("Save the pipeline with key :{} to the cache", key);
        cache.put(key, value);
    }

    public static void remove(String key) {
        cache.remove(key);
    }

    public static void clear() {
        cache.clear();
    }
}
java 复制代码
@Component
@Slf4j
public class FilterFactory {

    public FilterChainPipeline createFilterChainPipelineWithLocalConfig(BaseEnum baseEnum) {
        String bizCode = baseEnum.getCode();
        if (filterChainCache.containsKey(bizCode)) {
            log.info("读取缓存的过滤器链");
            return filterChainCache.get(bizCode);
        }
        List<FilterConfiguration.FilterDefinition> definitions = filterConfiguration.getBusinessFilters().get(baseEnum.getCode());
        if (definitions == null) {
            throw new IllegalStateException("No filter definitions found for business code: " + baseEnum.getCode());
        }
        FilterChainPipeline<EventFilter> pipeline = buildPipeline(definitions);
        log.info("将过滤器链放入缓存");
        filterChainCache.put(bizCode, pipeline);
        return pipeline;
    }


    public FilterChainPipeline createFilterChainPipelineWithAnno(BaseEnum baseEnum) {
        String bizCode = baseEnum.getCode();
        if (filterChainCache.containsKey(bizCode)) {
            log.info("读取缓存的过滤器链");
            return filterChainCache.get(bizCode);
        }
        FilterChainPipeline<EventFilter> pipeline = new FilterChainPipeline<>();

        List<Class<?>> filterClasses = Arrays.asList(applicationContext.getBeanNamesForType(EventFilter.class))
                .stream()
                .map(beanName -> applicationContext.getType(beanName))
                .collect(Collectors.toList());

        List<EventFilter> eventFilterList = filterClasses.stream()
                .filter(filterClass -> {
                    EventFilterAnnotation annotation = filterClass.getAnnotation(EventFilterAnnotation.class);
                    return annotation != null && baseEnum.getCode().equals(annotation.bizCode());
                })
                .map(this::createFilter)
                .sorted(Comparator.comparingInt(EventFilter::getPriority))
                .collect(Collectors.toList());

        eventFilterList.forEach(filter -> pipeline.addFirst(filter));

        log.info("将过滤器链放入缓存");
        filterChainCache.put(bizCode, pipeline);
        return pipeline;
    }

如何使用

存在不足

  • 其实可以对每一个业务过滤器进行缓存,而不是缓存业务过滤器链,这就不管是基于本地配置、基于注解还是基于远程配置的方式创建的过滤器链都可以避免重复创建过滤器对象

异步处理

业务场景

  • 很多复杂业务场景下,我们进行最后的更新数据库操作前,需要进行很多查询(包括数据库查询,外部微服务查询)。有些查询时需要有前后依赖关系的,有些是不需要依赖的,有些是耗时的,有些是不耗时的

实现思路

  • 因此我们可以提供一个异步处理的方法 handleSync ,其实现的功能是:根据依赖关系进行分组,在同一组内的过滤器没有相互依赖关系可以异步进行,不同组的根据顺序进行执行,保证在执行"依赖方"时"提供方"已经执行完了

  • 那么如何实现分组 呢?这就需要借助到 priority 这个配置属性了,如果是在异步处理这个场景下,我们将同一个分组的过滤器的 priority 设置成一个值,就实现了分组的效果

  • 那么如何实现按顺序执行 呢?一方面需要根据 priority 的大小顺序来决定执行的先后顺序,一方面需要用到 CompletableFuture 来保证在执行"依赖方"时"提供方"已经执行完了

实现代码

  • 在 handleAsync 中将构造好的过滤器链进行拆分,拆分为一个过滤器列表
java 复制代码
/**
 * 事件过滤器处理对外提供的方法
 *
 * @param <T>
 */
public class DefaultFilterChain<T extends EventContext> implements EventFilterChain<T> {

  private EventFilterChain<T> next;
  private EventFilter<T> filter;
  private ErrorHandler errorHandler;

  public DefaultFilterChain(EventFilterChain chain, EventFilter filter) {
    this.next = chain;
    this.filter = filter;
  }

  /**
   * 异步事件过滤器处理方法
   *
   * @param context
   */
  @Override
  public void handleAsync(T context) {
    try {
      List<EventFilter> list = new ArrayList<>();
      DefaultFilterChain temp = (DefaultFilterChain)this;
      while(temp.next != null) {
        list.add(temp.filter);
        temp = (DefaultFilterChain)temp.next;
      }
      list.add(temp.filter);


      filter.doFilterAsync(context, list);
    } catch (Exception e) {
      if (errorHandler != null) {
        try {
          errorHandler.handleError(e, context);
        } catch (Exception ex) {
          throw new RuntimeException(ex);
        }
      } else {
        throw e; // 如果没有设置错误处理器,则将异常继续抛出
      }
    }
  }
}
  • 在 doFilterAsync 方法中实现异步执行
java 复制代码
@Slf4j
public abstract class AbstractEventFilter<T extends EventContext> implements EventFilter<T> {

    public int priority = 0;

    /**
     * 执行单个过滤器
     *
     * @param context
     */
    @Override
    public void doFilter(T context) {
        if (context.getFilterSelector().matchFilter(this.getClass().getSimpleName())) {
            handle(context);
        }
    }

    /**
     * 异步执行过滤器组
     *
     * @param context
     * @param filterList
     */
    @Override
    public void doFilterAsync(T context, List<EventFilter> filterList) {
        log.info("列表:{}", JSON.toJSONString(filterList));
        // 根据 priority 从大到小进行分组
        Map<Integer, List<EventFilter>> groupedFilters = filterList.stream()
            .collect(Collectors.groupingBy(
                    EventFilter::getPriority,
                    () -> new TreeMap<Integer, List<EventFilter>>(Comparator.reverseOrder()), // 自定义比较器,
                    Collectors.toList()));

        CompletableFuture<Void> previousFuture = CompletableFuture.completedFuture(null);
        for (List<EventFilter> filters : groupedFilters.values()) {
            // thenCompose() 方法可以将一个异步操作的结果作为输入传递给下一个异步操作,从而实现操作链的组合
            previousFuture = previousFuture.thenCompose(result -> executeFiltersInParallel(context, filters));
        }
        // 通过这样的组合,确保了每个分组的执行在前一个分组完成之后进行。最后,我们使用 join 方法等待最后一个分组的执行结果
        previousFuture.join();

        CompletableFuture.allOf(previousFuture);
    }

    private CompletableFuture<Void> executeFiltersInParallel(T context, List<EventFilter> filters) {
        List<CompletableFuture<Void>> filterFutures = new ArrayList<>();

        ExecutorService executorService = Executors.newFixedThreadPool(filters.size());
        log.info("分组优先级:{}", JSON.toJSONString(filters));
        for (EventFilter filter : filters) {
            CompletableFuture<Void> filterFuture = CompletableFuture.supplyAsync(() -> {
                filter.doFilter(context);
                return null;
            }, executorService);
            filterFutures.add(filterFuture);
        }

        // allOf 方法是 CompletableFuture 类提供的一个静态方法,用于等待多个 CompletableFuture 对象的完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(filterFutures.toArray(new CompletableFuture[0]));

        // 关闭线程池
        executorService.shutdown();

        return allFutures;
    }
}

如何使用

  • 配置优先级
yml 复制代码
pipeline:
  businessFilters:
    YW1:
      - name: "com.design.elegant.filters.CarInfoQueryFilter"
        priority: 4
      - name: "com.design.elegant.filters.LogSaveFilter"
        priority: 1
      - name: "com.design.elegant.filters.UserPayFilter"
        priority: 4
      - name: "com.design.elegant.filters.JudgeCarFilter"
        priority: 2
java 复制代码
@Service
public class ChargeServiceImpl implements IChargeService {

    @Resource
    private ChargeFilterSelectorFactory chargeFilterSelectorFactory;

    @Resource
    private PluginRegistry<IChargeModelHandler, ChargeModel> pluginRegistry;

    @Autowired
    private FilterFactory filterFactory;

    @Autowired
    private ExceptionHandler exceptionHandler;

    @Override
    public void handle(ChargeRequest chargeRequest) {
        FilterSelector filterSelector = chargeFilterSelectorFactory.getFilterSelector(chargeRequest);

        boolean present = BizEnum.of(chargeRequest.getBizCode()).isPresent();
        if (!present) {
            return;
        }
        BizEnum bizEnum = BizEnum.of(chargeRequest.getBizCode()).get();
        ChargeContext chargeContext = new ChargeContext(bizEnum, filterSelector);
        chargeContext.setChargeRequest(chargeRequest);
        FilterChainPipeline pipeline = filterFactory.createPipelineFromLocalConfig(chargeContext.getBizCode());
        pipeline.setErrorHandler(exceptionHandler);
        // 异步处理
        pipeline.getFilterChain().handleAsync(chargeContext);
    }

优化代码

线程池管理

  • 在执行 方法时,每一次都需要去 newFixedThreadPool,这个会造成一定的开销,可以提供一个全局的线程池管理类
java 复制代码
@Slf4j
@Component
public class ThreadPoolManager {
    // 创建一个根据需要创建新线程的线程池,该线程池根据工作负载自动调整线程数量。该线程池使用ForkJoinPool来实现任务的分配和执行
    private final ExecutorService pipelineExecutorService = Executors.newWorkStealingPool();

    public ExecutorService getPipelineExecutorService() {
        log.info("getPipelineExecutorService");
        return pipelineExecutorService;
    }

    @PreDestroy
    public void shutdown() {
        log.info("pipelineExecutorService shutdown");
        pipelineExecutorService.shutdown();
        try {
            if (!pipelineExecutorService.awaitTermination(60, TimeUnit.SECONDS)) {
                log.info("Forcing shutdownNow of pipelineExecutorService as it didn't respond to shutdown");
                pipelineExecutorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            pipelineExecutorService.shutdownNow();
        }
    }
}

单一职责

  • 工厂类 FilterFactory 功能混杂,有根据不同方式创建过滤器链的方法、有构建过滤器链的方法,有创建过滤器对象的方法。因此需要进行拆分保证单一职责
java 复制代码
/**
 * 事件过滤器链工厂,负责根据不同场景创建过滤器链
 *
 * @author: xudehui1
 * @date: 2023-12-02 15:51
 */

@Component
@Slf4j
public class FilterFactory {

    private final FilterChainBuilder filterChainBuilder;
    private final FilterConfigReader filterConfigReader;

    @Autowired
    public FilterFactory(FilterChainBuilder filterChainBuilder, FilterConfigReader filterConfigReader) {
        this.filterChainBuilder = filterChainBuilder;
        this.filterConfigReader = filterConfigReader;
    }

    /**
     * 基于读取配置文件+业务编码动态组装过滤器链
     *
     * @param baseEnum
     * @return
     */
    public FilterChainPipeline createPipelineFromLocalConfig(BaseEnum baseEnum) {
        String bizCode = baseEnum.getCode();
        if (FilterChainCache.containsKey(bizCode)) {
            return FilterChainCache.get(bizCode);
        }
        List<FilterConfiguration.FilterDefinition> definitions = filterConfigReader.getFilterDefinitionsFromLocal(baseEnum);
        if (definitions == null) {
            throw new IllegalStateException("No filter definitions found for business code: " + baseEnum.getCode());
        }
        FilterChainPipeline<EventFilter> pipeline = filterChainBuilder.buildPipelineFromDefinition(definitions);
        FilterChainCache.put(bizCode, pipeline);
        return pipeline;
    }

    /**
     * 基于传入的过滤器配置对象(JSON 格式)+业务编码动态组装过滤器链
     *
     * @param jsonStrConfig
     * @param baseEnum
     * @return
     */
    public FilterChainPipeline createPipelineFromRemoteConfig(String jsonStrConfig, BaseEnum baseEnum) {
        List<FilterConfiguration.FilterDefinition> definitions = filterConfigReader.getFilterDefinitionsFromRemote(jsonStrConfig, baseEnum);
        return filterChainBuilder.buildPipelineFromDefinition(definitions);
    }


    public FilterChainPipeline createFilterChainPipelineWithAnno(BaseEnum baseEnum) {
        String bizCode = baseEnum.getCode();
        if (FilterChainCache.containsKey(bizCode)) {
            return FilterChainCache.get(bizCode);
        }
        List<Class<?>> definitions = filterConfigReader.getFilterDefinitionsFromAnno(baseEnum);
        FilterChainPipeline<EventFilter> pipeline = filterChainBuilder.buildPipelineFromAnnoClass(definitions);
        FilterChainCache.put(bizCode, pipeline);
        return pipeline;
    }
}

待进行

  • 移除 FilterSelector

  • 缓存 EventFilter

最后

  • 感谢 coder1v5 老师分享的视频,能够让我学习到如此宝贵的思想

  • 希望各位看官能够提供宝贵的意见,最后附上仓库地址:gitee.com/xudehui1997...

参考

相关推荐
Yaml443 分钟前
智能化健身房管理:Spring Boot与Vue的创新解决方案
前端·spring boot·后端·mysql·vue·健身房管理
小码编匠2 小时前
一款 C# 编写的神经网络计算图框架
后端·神经网络·c#
WaaTong2 小时前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
AskHarries2 小时前
Java字节码增强库ByteBuddy
java·后端
霁月风2 小时前
设计模式——观察者模式
c++·观察者模式·设计模式
佳佳_2 小时前
Spring Boot 应用启动时打印配置类信息
spring boot·后端
许野平3 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
BiteCode_咬一口代码4 小时前
信息泄露!默认密码的危害,记一次网络安全研究
后端
暗黑起源喵4 小时前
设计模式-工厂设计模式
java·开发语言·设计模式