「什么是责任链」
责任链模式(Chain of Responsibility Pattern):责任链模式是⼀种对象的⾏为模式。
我们可以简单地理解为:当一个请求从执行开始到结束,需要经过很多层的处理,此时我们可以将这些不同层的处理给抽象出来,其中的每一层就是一个过滤链路,将它们窜起来之后,就会形成链状结构。如下图所示:
「为什么要这么设计,这样设计的好处在哪?」
「解耦:」 可以将每一层的逻辑都单独封装起来,互相不干预,达到解耦的效果。另外调用方也不必关心链路内部的那些handler都做了什么,链路内部的数据传递关系统一封装好。
「开闭原则:」 其实统一封装成多层之后,也满足了设计原则中的开闭原则,只需要新增或移开一项handler,就可以不影响其他层代码,直接改变原先责任链的逻辑。
「责任链模式有什么不足点?」
这里我归纳了下几个在实践中发现的不足点:
「链路长:」 请求必须要从链头出发,执行到链路末端,所以在链路的每一层中对于代码的性能需要谨慎考虑。
「排查困难:」 如果一旦任意一环出现了问题,要记得打好日志。
「长链路的业务代码就一定适合使用责任链模式吗?」
这里我先给出我的思考答案:不。我们来看看下边这个代码案例:
这是一段看起来非常长而且业务层次也很清晰的一段代码,如果我们将这段代码改成用责任链模式来实现,大概就是这样的。
这样看来代码变得简单了许多,调用方也确实不知道这里面处理了什么东西。但是这样的做法合适吗?
其实这里我们漏了一个步骤,那就是没有结合业务场景进行分析,过度设计地使用了责任链模式。
责任链模式,其实是比较适用于执行逻辑有先后顺序的场景,然后这里我所贴出来的代码其实有些方法是可以并行执行的,如果使用了责任链模式反而处理时间更长了些。
所以这里的代码 更好的做法应该是通过一个事件抛出 或者 是mq发送消息,去通知到各个业务服务进行相关操作。例如下边的代码所示:
/** * 会员升级处理 * * @param currentLevel * @param role * @param userId * @param price * @param currentPrice */ public void upgradeV3(Integer currentLevel, int role, long userId, int price, int currentPrice) { //包装一些参数... mqTemplate.send(upgradeMsg); }
❝
「责任链的扩展思考」
❞
「一链到底」
其实责任链的玩法有许多种,第一种 也是最经典的一种,从链路的开始执行到末尾。
这种链路我们的代码实现可以如下所示:
首先我们统一定义一个链路层的接口:
public interface BizFilterNode { //这个BizFilterContext是参数传递时候使用的上下文对象 void handle(BizFilterContext bizFilterContext); }
然后就是如何遍历这批BizFilterNode对象
public void filterHandle(BizFilterContext bizFilterContext) { for (BizFilterNode bizFilterNode : bizFilterNodes) { bizFilterNode.handle(bizFilterContext); } }
直接一个for循环,然后对各层的链路节点遍历,执行相关的invoke方法。
「中途暂停」
中途暂停的意思是,链路执行到了一半,某一环节有问题,则整个脸路暂停了。例如下图所示:
这个时候,我们的代码可以这么来设计:
public interface BizFilterNode { void handle(BizFilterContext bizFilterContext); //增加一个是否终止执行后边逻辑 boolean terminateChain(BizFilterContext bizFilterContext); }
然后还是在遍历的时候,进行判断处理
public void filterHandle(BizFilterContext bizFilterContext) { for (BizFilterNode bizFilterNode : bizFilterNodes) { //全链路终止 if (bizFilterNode.terminateChain(bizFilterContext)) { return; } bizFilterNode.handle(bizFilterContext); } }
「跳过当前环节」
所谓的跳过当前环节,其实意思是指当前的handler不想执行,直接跳到下一层的handler里面执行。
这个时候,我们只需要再在原有的代码里做写扩展就可以了:
public interface BizFilterNode { void handle(BizFilterContext bizFilterContext); boolean terminateChain(BizFilterContext bizFilterContext); //是否跳过当前节点 boolean skipNode(BizFilterContext bizFilterContext); }
遍历节点的时候,进行逻辑判断处理
public void filterHandle(BizFilterContext bizFilterContext) { for (BizFilterNode bizFilterNode : bizFilterNodes) { if (bizFilterNode.terminateChain(bizFilterContext)) { return; } //判断是否要跳过当前环节 if (bizFilterNode.skipNode(bizFilterContext)) { continue; } bizFilterNode.handle(bizFilterContext); } }
「责任链模式在开源组件中的使用」
责任链在各个组件中应⽤很多,基本都⼤同⼩异。但是 Netty 的责任链相对来说有趣⼀些,我们⽤它来举
例简单介绍在netty中是如何使⽤责任链。 ChannelHandler 是 Netty 提供的默认处理者。核⼼的两个实现是 ChannelInboundHandler 和 ChannelOutboundHandler 。
ChannelInboundHandler
:⼊站处理器,处理读数据的逻辑。ChannelOutBoundHandler
:出站处理器,处理写数据的逻辑。
当数据传递到netty的时候,会有一个读事件触发,接着会通过ChannelHandler
里面的一个责任链。只不过Netty的责任链采用的是双向链路。这里的双向是指业务设计,并不是数据结构采用了双向链表。
ChannelInboundHandler
里面有多个inbound节点,处理了不同的业务逻辑,而ChannelOutBoundHandler
里面则有许多不同的outbound节点,也处理了不同的业务逻辑。
「小结」
责任链模式只是众多设计模式中的一种,合理的使用设计模式不一定会提升代码的性能,但是对于代码的维护性会有一定的帮助。在深入了解了它们的设计思想之后,希望可以对你平时的工作有所帮助。