关于过滤链设计的实践记录

news/2024/5/19 15:19:20 标签: java, servlet, 责任链模式

关于过滤链设计的实践记录

一、背景

最近,在做一个API网关的小项目,提升Java基础及项目设计能力。对于一个网关来说,过滤链的设计是其核心的设计思想,简单做了一个该网关的过滤链的流程,如下。为了更好地去理解过滤链的设计,该篇文章从基础理论的责任链设计模式 及 应用实践Demo、开源框架相关实践分析 三方面 进行分析记录。

关于过滤链设计的实践记录

一、背景

最近,在做一个API网关的小项目,提升Java基础及项目设计能力。对于一个网关来说,过滤链的设计是其核心的设计思想,简单做了一个该网关的过滤链的流程,如下。为了更好地去理解过滤链的设计,该篇文章从基础理论的责任链设计模式 及 应用实践Demo、开源框架相关实践分析 三方面 进行分析记录。

在这里插入图片描述

二、责任链模式

问题背景

在日常生活,我们去做一件事,往往会去梳理各个步骤,并按照各个步骤去执行形成一条链路。如果这件事情是自己一个人的职责,那么恭喜你,你可以很自由地完成该件事情(可以理解为一个类中的方法,可以自己调用则很轻松),但是如果这个链路涉及到不同人,而且人还是不同领域,那么你将会感到复杂,此时你会说没事儿,我可以挨个去找对应的人,没错这样还好。那么进步思考,若每次找的人组合的步骤发生变化,且当你找到该人去负责,该人又让你去找其他人,问题复杂度是不是一下子上来了。

责任链模式则是解决以上问题,去抽离 **客户端(调用者)**和 具体执行者的职责,调用者无需关心整个链路怎么执行,只需提交一次对应的人即可,剩下的则交给具体执行者的组合(即责任链)去做。

举一个场景例子,工单审批。

对于工单审批是一件比较复杂的事情,而你关心的只是最终结果,例如请假我只关心能否请假成功。具体怎么批的过程无需关心。
在这里插入图片描述

代码举例

若想去实现简单的一个请求工单流程,简单示例代码如下所示,表明看这段代码还是十分简洁的没什么大碍。但细致想一想,定义审核流程的业务逻辑是不是全部放在一个方法中了,这样是不是导致该类(客户端或业务类)的职责过大。若我想突然改变这个执行过程,是不是得修改代码,而且也不便于灵活组合,同时需要业务类去组织整个链路的执行过程。

java">boolean audit(WorkerOrder workerOrder){
    Leader leader = new Leader();
    Manager manager = new Manager();
    Boss boss = new Boss();
    if(workerOrder.getDay()<3){
       return leader.audit(workerOrder);
    }else if (workerOrder.getDay()<7){
       return leader.audit(workerOrder) && manager.audit(workerOrder);
    }else {
       return leader.audit(workerOrder) && manager.audit(workerOrder) && boss.audit(workerOrder);
    }
}

对上面进行稍微改造一下:

java">@Data
public abstract class AbstractAuditFilter implements AuditHandler<WorkerOrder>{

    protected AuditHandler<WorkerOrder> next;

}
java">boolean audit(WorkerOrder workerOrder){
        Leader leader = new Leader();
        List<AbstractAuditFilter> filterList = new ArrayList<>();
        filterList.add(leader);
        FilterChain filterChain = FilterChainFactory.build(filterList);
        return filterChain.audit(workerOrder);
//        Manager manager = new Manager();
//        Boss boss = new Boss();
//        if(workerOrder.getDay()<3){
//           return leader.audit(workerOrder);
//        }else if (workerOrder.getDay()<7){
//           return leader.audit(workerOrder) && manager.audit(workerOrder);
//        }else {
//           return leader.audit(workerOrder) && manager.audit(workerOrder) && boss.audit(workerOrder);
//        }
    }

通过以上简单的改造,可以实现对执行链路的顺序编排,组合。而对于如何编排则可结合动态配置中心进行,定义规则,来加载选取需要执行哪些过滤链,另外也可以通过传参的方式进行。客户端或业务类执行关系选择何种执行链路或规则即可。无需再组织业务逻辑。

概括一下,对应原有的缺点则有以下:

1.业务类比较庞大,各个上级的审批方法都集中在该类中,违反了 “单一职责原则”,测试和维护难度大。
2.当需要修改该请假流程,譬如增加当天数大于30天时还需提交给董事长处理,必须修改该类源代码(并重新进行严格地测试),违反了 “开闭原则”
3.该流程缺乏灵活性,流程确定后不可再修改(除非修改源代码),客户端无法定制流程

但个人认为,若在执行链路并不长,执行的规则也不复杂(链路的业务逻辑相对比较固定),感觉没有必要去使用该设计模式,因为它会带来额外的问题,例如对链条的管理定义,对规则的定义等额外复杂性。[而对于网关这种过滤链、插件较多,同时需要灵活组合,则十分适合该方式]

关键类图

Handler(抽象处理者):它定义了一个处理请求的接口,一般设计为抽象类,由于不同的具体处理者处理请求的方式不同,因此在其中定义了抽象请求处理方法。因为每一个处理者的下家还是一个处理者,因此在抽象处理者中定义了一个抽象处理者类型的对象,作为其对下家的引用。通过该引用,处理者可以连成一条链。

ConcreteHandler(具体处理者):它是抽象处理者的子类,可以处理用户请求,在具体处理者类中实现了抽象处理者中定义的抽象请求处理方法,在处理请求之前需要进行判断,看是否有相应的处理权限,如果可以处理请求就处理它,否则将请求转发给后继者;在具体处理者中可以访问链中下一个对象,以便请求的转发。

在这里插入图片描述

关于链路

  1. 采用数组的方式进行管理整个链路【对于filter则无需去指定执行 next,遍历链表数组并执行即可】
  • tomcat的server的filter
  • nacos的configMangaer
  1. 采用next,引用链表的方式进行管理链路【每个filter需要去手动触发next的执行】

业务场景

  1. 工单审批
  2. 业务商品审核
  3. web请求过滤链(Tomat—的Servelet的filter)
  4. 做非功能性的切面工作

三、相关应用

  1. Netty 中的 Pipeline 和 ChannelHandler 通过责任链设计模式来组织代码逻辑

  2. Spring Security 使用责任链模式,可以动态地添加或删除责任(处理 request 请求)

  3. Spring AOP 通过责任链模式来管理 Advisor

  4. Dubbo Filter 过滤器链也是用了责任链模式(链表),可以对方法调用做一些过滤处理,譬如超时(TimeoutFilter),异常(ExceptionFilter),Token(TokenFilter)等

  5. Mybatis 中的 Plugin 机制使用了责任链模式,配置各种官方或者自定义的 Plugin,与 Filter 类似,可以在执行 Sql 语句的时候做一些操作

  6. nacos利用tomcat的内置filter做过滤:
    在这里插入图片描述

  7. nacos:Config Filter Chain Management ,com.alibaba.nacos.client.config.filter.impl``ConfigFilterChainManager [版本1.4.4],后续nacos: ConfigEncryptionFilter 实现该filter了

  8. zuul: 采用的数组进行管理
    在这里插入图片描述

四、源码分析

抓住核心概念:

  1. filter,hanlder,等定义过滤链的类,具体的执行者( 一般来说按照处理逻辑可以分为 前中后,错误)
  2. filterchain,责任链,负责编排责任链,filter的组合
  3. filterFactory,创建责任链(至于创建的时机,则需要因情况而定
  4. Config:如何去创建责任链,需要一定的配置,或者其他方式就进行创建。

关于服务器设计的概念:

  1. 找入口启动入口、请求入口
  2. 加载基础配置(静态固定的信息),动态配置则运行时更新处理
  3. 找Server的生命周期

Tomcat的过滤链设计

基本流程:

  1. 过滤器的加载具体是在 ContextConfig 类的 configureContext 方法中,分别加载 filter 和 filterMap 的相关信息,并保存在上下文环境中
  2. 过滤器的初始化在 StandardContext 类的 startInternal 方法中完成,保存在 filterConfigs 中并存到上下文环境中
  3. 请求流转到 StandardWrapperValve 时,在 invoke 方法中,会根据过滤器映射配置信息,为每个请求创建对应的 ApplicationFilterChain,其中包含了目标 Servlet 以及对应的过滤器链,并调用过滤器链的 doFilter 方法执行过滤器
  • StandardEngine处理请求:一旦请求进入Tomcat,它会被传递到**StandardEngine,这是Tomcat中的一个核心组件。StandardEngine负责根据请求的主机名(Host)将请求路由到适当的StandardHost**。
  • StandardHost选择合适的ContextStandardHost会根据请求的上下文路径(Context Path)来选择适当的StandardContext,这是一个Web应用程序的容器
  • 请求到达StandardContext:一旦**StandardContext选择了适当的Web应用程序,请求将进入该Web应用程序的上下文,然后由StandardContext**继续处理
  • Filter链的创建和执行:在**StandardContext中,存在一个名为ApplicationFilterChain的过滤器链。当请求进入Web应用程序的StandardContext**时,会首先进入这个过滤器链

在这里插入图片描述

五、思考总结

  1. 关于编排链路的方式:默认去编排定义[例如加载SPI插件,再通过插件的优先级排序],通过用户参数去编排
  • 加载所有的插件列表,执行时通过规则校验是否执行该插件
  • 无加载插件列表,通过参数指定id列表集合,动态编排链路
  1. 关于filter的职责问题
  • 执行部分(通过Request,Response去透传给下游即下一个Filter),OR执行全部
  1. 对比Aspect的切面操作,前后置的概念(实际上Spring AOP的执行即采用的责任链模式
  • 加载Aspect的配置,通过注解 加入容器
  • 通过动态代理实现函数的调用(基于反射机制),包装前后置操作
  • 通过责任链的方式进行执行

六、相关参考

  1. 图灵学院(强烈推荐):https://baijiahao.baidu.com/s?id=1660719391207352664&wfr=spider&for=pc

http://www.niftyadmin.cn/n/5128425.html

相关文章

使用docker部署lnmp多站点

1. 创建一个 Docker 网络 以便容器可以在同一网络上进行通信 docker network create lnmpnetwork2. 运行 MySQL 容器&#xff1a; 运行 MySQL 容器并将其连接到创建的网络。确保将 MySQL 的端口映射到宿主机上&#xff0c;以便您可以从宿主机访问数据库。 将mysql的配置和数…

工厂智能工具介绍——5W1H

5W1H分析法是一种常见的思考工具&#xff0c;它的名称来源于它的六个基本问题&#xff1a;1. What&#xff08;什么&#xff09;&#xff1b;2. Why&#xff08;为什么&#xff09;&#xff1b;3. Who&#xff08;谁&#xff09;&#xff1b;4. Where&#xff08;在哪里&#x…

Java将JPG/PNG图片转换为WEBP格式,以及WEBP转换成JPG/PNG格式

越来越多的网站采用了webp格式的图片&#xff0c;webp占用空间小&#xff0c;传送更快&#xff0c;画质不降低。经常会遇到图片格式转换的需求&#xff0c;我们可以借助第三方的包&#xff0c;通过Java实现来PG/PNG图片转换为WEBP格式&#xff0c;以及WEBP转换成JPG/PNG格式。 …

“第五十二天”

算术逻辑单元&#xff1a; 之前提过的运算器包括MQ,ACC,ALU,X,PSW&#xff1b;运算器可以实现运算以及一些辅助功能&#xff08;移位&#xff0c;求补等&#xff09;。 其中ALU负责运算&#xff0c;运算包括算术运算&#xff08;加减乘除等&#xff09;和逻辑运算&#xff08…

#力扣:2315. 统计星号@FDDLC

2315. 统计星号 - 力扣&#xff08;LeetCode&#xff09; 一、Java class Solution {public int countAsterisks(String s) {int cnt 0;boolean flag true;for(char c: s.toCharArray()) {if(c |) flag !flag;else if(c * && flag) cnt;}return cnt;} }

软考高项(十三)项目资源管理 ★重点集萃★

&#x1f451; 个人主页 &#x1f451; &#xff1a;&#x1f61c;&#x1f61c;&#x1f61c;Fish_Vast&#x1f61c;&#x1f61c;&#x1f61c; &#x1f41d; 个人格言 &#x1f41d; &#xff1a;&#x1f9d0;&#x1f9d0;&#x1f9d0;说到做到&#xff0c;言出必行&am…

CFD模拟仿真理论知识:流体仿真应用

CFD模拟仿真理论知识:流体仿真应用 本文将介绍CFD(Computational Fluid Dynamics,计算流体动力学)模拟仿真理论知识的原理、方法和应用。通过本文对CFD的深入理解,并了解如何运用这一理论解决实际问题。

杂牌行车记录仪特殊AVI结构恢复案例

最近遇到一个杂牌的行车记录仪需要恢复数据&#xff0c;其使用AVI格式&#xff0c;但是在扫描恢复的过程中却发现厂家对其AVI结构进行了“魔改”致程序无法正常识别 故障存储:16G SD卡 fat32文件系统 故障现象: 16G的SD卡&#xff0c;在发生事故后客户尝试自行接到手机上读…