当前位置: 首页 > news >正文

开源框架中的责任链模式实践

作者:vivo 互联网服务器团队-Wang Zhi

责任链模式作为常用的设计模式而被大家熟知和使用。本文介绍责任链的常见实现方式,并结合开源框架如Dubbo、Sentinel等进行延伸探讨。

一、责任链介绍

在GoF 的《设计模式》一书中对责任链模定义的:将请求的发送和接收解耦,让多个接收对象都有机会处理这个请求。将这些接收对象串成一条链,并沿着这条链传递这个请求,直到链上的某个接收对象能够处理它为止或者所有接收对象处理一遍。

用通俗的话解释在责任链模式中,多个处理器(接收对象)依次处理同一个请求。一个请求先经过 A 处理器处理,然后再把请求传递给 B 处理器,B 处理器处理完后再传递给 C 处理器,以此类推,形成一个链条。链条上的每个处理器各自承担各自的处理职责,所以叫作责任链模式。

责任链模式有效地降低了发送和接收者之间的耦合度,增强了系统的可扩展性。在责任链的模式下不仅能够针对单个处理器对象进行定制升级(每个处理器对象关注各自的任务),而且能够对整个责任链的处理器对象的顺序的调整以及增删。

本文约定:责任链上的接收对象统一称为处理器;本文中介绍的责任链属于GOF定义中责任链的变种即责任链上的所有处理器都会参与任务的处理

二、责任链实现

责任链模式有多种实现方式,从驱动责任链上处理器方式的角度可以分类两类,即责任链驱动 和 责任链处理器自驱动

2.1 处理器自驱动

// 1、定义抽象类
public abstract class AbstractHandler {protected Handler next = null;// 绑定处理器public void setSuccessor(Handler next) {this.next = next;}// 处理器执行操作并驱动下一个处理器public abstract void handle();
}// 2、定义处理器A
public class HandlerA extends AbstractHandler {@Overridepublic void handle() {// do somethingif (next != null) {next.handle();}}
}// 3、定义处理器B
public class HandlerB extends AbstractHandler {@Overridepublic void handle() {// do somethingif (next != null) {next.handle();}}
}// 4、构建责任链并添加处理器
public class HandlerChain {// 通过链表的形式保存责任链private AbstractHandler head = null;private AbstractHandler tail = null;public void addHandler(AbstractHandler handler) {handler.setSuccessor(null);if (head == null) {head = handler;tail = handler;return;}tail.setSuccessor(handler);tail = handler;}public void handle() {if (head != null) {head.handle();}}
}// 5、整体构建责任链添加处理器并进行驱动
public class Application {public static void main(String[] args) {// 构建责任链并添加处理器HandlerChain chain = new HandlerChain();chain.addHandler(new HandlerA());chain.addHandler(new HandlerB());// 责任链负责触发chain.handle();}
}

说明:

  • 责任链上的每个处理器对象维护下一个处理器对象,整个责任链的驱动由每个处理器对象自行驱动。

  • 每个处理器对象Handler中包含下一个处理器对象next的变量,通过链表形式维护责任链的关系。

2.2 责任链驱动

// 1、定义抽象接口
public interface IHandler {void doSomething();
}// 2、定义处理器A
public class HandlerA implements IHandler {@Overridepublic void doSomething() {// do something}
}// 3、定义处理器B
public class HandlerB implements IHandler {@Overridepublic void doSomething() {// do something}
}// 4、构建责任链并添加处理器
public class HandlerChain {// 通过数组的形式保存处理器private List<IHandler> handlers = new ArrayList<>();public void addHandler(IHandler handler) {handlers.add(handler);}// 由责任链负责遍历所有的处理器并进行调用public void handle() {for (IHandler handler : handlers) {handler.handle();}}
}// 5、整体构建责任链添加处理器并进行驱动
public class Application {public static void main(String[] args) {HandlerChain chain = new HandlerChain();chain.addHandler(new HandlerA());chain.addHandler(new HandlerB());chain.handle();}
}

说明:

  • 责任链对象本身以数组的形式维护处理器对象,即上述代码中的handlers 。

  • 责任链的处理器的执行由责任链对象循环调用处理器对象驱动,即上述代码中的handle方法。

三、开源框架中责任链应用

责任链低耦合高扩展的特点让它在很多开源的框架中被采用,本文选取了开源框架中的Spring Interceptor、Servlet Filter、Dubbo、Sentinel进行责任链的实现介绍,通过对常用框架中责任链应用的了解能够更好掌握责任链落地并在日常的开发中积极的使用。

3.1 Spring Interceptor

3.1.1 Interceptor介绍

图片

  • Spring中的拦截器(Interceptor) 用于拦截控制器方法的执行,可以在方法执行前后添加自定义逻辑类似于AOP编程思想。

  • Inteceptor的作用时机是在请求(request)进入servlet后,在进入Controller之前进行预处理。

  • Inteceptor的实际应用包括:认证授权、日志记录、字符编码转换,敏感词过滤等等。

  • Inteceptor中责任链的实现会从处理器的介绍,责任链的构建以及责任链的执行三个角度进行阐述。

3.1.2 处理器介绍

public interface HandlerInterceptor {boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception;void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception;
}@Component
public class TimeInterceptor extends HandlerInterceptorAdapter {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 前置处理System.out.println("time interceptor preHandle");return true;}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {// 后置处理System.out.println("time interceptor postHandle");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("time interceptor afterCompletion");}
}

说明:

  • 处理器Interceptor的接口HandlerInterceptor定义了三个方法,可在控制器方法执行前后添加自定义逻辑。

  • 自定义处理器如上的TimeInterceptor需要自定义实现上述3个方法实现自我的逻辑。

  • 所有的自定义处理会串联在HandlerExecutionChain类实现的责任链上。

3.1.3 责任链构建

public class HandlerExecutionChain {private final Object handler;private HandlerInterceptor[] interceptors;private List<HandlerInterceptor> interceptorList;private int interceptorIndex = -1;public void addInterceptor(HandlerInterceptor interceptor) {// 添加拦截器initInterceptorList().add(interceptor);}public void addInterceptors(HandlerInterceptor... interceptors) {if (!ObjectUtils.isEmpty(interceptors)) {CollectionUtils.mergeArrayIntoCollection(interceptors, initInterceptorList());}}private List<HandlerInterceptor> initInterceptorList() {if (this.interceptorList == null) {this.interceptorList = new ArrayList<HandlerInterceptor>();if (this.interceptors != null) {// An interceptor array specified through the constructorCollectionUtils.mergeArrayIntoCollection(this.interceptors, this.interceptorList);}}this.interceptors = null;return this.interceptorList;}
}

说明:

  • HandlerExecutionChain类作为串联Interceptor处理器的责任链负责责任链的构建和执行。

  • HandlerExecutionChain类通过集合对象interceptorList保存所有相关的处理器对象。

3.1.4 责任链执行

public class DispatcherServlet extends FrameworkServlet {protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {try {try {// mappedHandler代表的是HandlerExecutionChain责任链                 mappedHandler = getHandler(processedRequest);HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());// 1、执行mappedHandler的applyPreHandle方法if (!mappedHandler.applyPreHandle(processedRequest, response)) {return;}// 2、执行controller的执行逻辑mv = ha.handle(processedRequest, response, mappedHandler.getHandler());if (asyncManager.isConcurrentHandlingStarted()) {return;}applyDefaultViewName(processedRequest, mv);// 执行mappedHandler的applyPostHandle方法mappedHandler.applyPostHandle(processedRequest, response, mv);}catch (Exception ex) {}processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);}catch (Exception ex) {}finally {}}
}public class HandlerExecutionChain {private final Object handler;private HandlerInterceptor[] interceptors;private List<HandlerInterceptor> interceptorList;private int interceptorIndex = -1;boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {// 责任链从前往后的顺序执行for (int i = 0; i < interceptors.length; i++) {HandlerInterceptor interceptor = interceptors[i];if (!interceptor.preHandle(request, response, this.handler)) {triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}}return true;}void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {HandlerInterceptor[] interceptors = getInterceptors();if (!ObjectUtils.isEmpty(interceptors)) {// 责任链从后往前的顺序执行for (int i = interceptors.length - 1; i >= 0; i--) {HandlerInterceptor interceptor = interceptors[i];interceptor.postHandle(request, response, this.handler, mv);}}}
}

说明:

  • 在servlet的doDispatch方法中依次触发责任链的applyPreHandle的前置处理方法、applyPostHandle的后置处理方法。

  • 前置处理方法applyPreHandle会遍历责任链上的处理器从前往后依次处理,后置处理方法applyPostHandle会遍历责任链上的处理器从后往前依次处理。

  • 处理器的驱动由责任链对象负责依次触发,非处理器对象自驱执行。

3.2 Servlet Filter

3.2.1 Filter介绍

图片

  • Servlet过滤器是在Java Servlet规范2.3中定义的,它能够对Servlet容器的请求和响应对象进行检查和修改,是个典型的责任链。

  • 在Servlet被调用之前检查Request对象并支持修改Request Header和Request内容。

  • 在Servlet被调用之后检查Response对象并支修改Response Header和Response内容。

3.2.2 处理器介绍

public interface Filter {public void init(FilterConfig filterConfig) throws ServletException;public void doFilter ( ServletRequest request, ServletResponse response, FilterChain chain ) throws IOException, ServletException;public void destroy();
}public class TimeFilter implements Filter {@Overridepublic void init(FilterConfig filterConfig) throws ServletException {System.out.println("time filter init");}@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {// 1、执行处理的逻辑System.out.println("time filter doFilter");// 2、执行责任链当中的下一个 Filter 对象,等价于执行 FilterChain 的internalDoFilter方法filterChain.doFilter(servletRequest, servletResponse);}
}

说明:

  • Servlet过滤器类要实现javax.servlet.Filter接口,该接口定义了通用的3个方法。

  • init方法:负责Servlet过滤器的初始化方法,Servlet容器创建Servlet过滤器实例过程中调用这个方法。

  • doFilter方法:当客户请求访问与过滤器关联的URL时,Servlet容器会调用该方法。

  • destroy方法:Servlet容器在销毁过滤器实例前调用该方法,可以释放过滤器占用的资源。

3.2.3 责任链构建

public final class ApplicationFilterChain implements FilterChain {// 责任链上 Filter 的维护对象private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];//责任链上待执行的 Filter 对象private int pos = 0;// 责任链上拥有的 Filter 数量private int n = 0;void addFilter(ApplicationFilterConfig filterConfig) {// 避免重复添加Filterfor(ApplicationFilterConfig filter:filters)if(filter==filterConfig)return;// 按需进行扩容if (n == filters.length) {ApplicationFilterConfig[] newFilters =new ApplicationFilterConfig[n + INCREMENT];System.arraycopy(filters, 0, newFilters, 0, n);filters = newFilters;}// 保存Filter 对象filters[n++] = filterConfig;}
}

说明:

  • ApplicationFilterChain作为Filter的责任链,负责责任链的构建和执行。

  • 责任链通过ApplicationFilterConfig类型的数组对象filters保存Filter处理器。

  • 责任链上处理器的添加通过保存到数组filters来实现。

3.2.4 责任链执行

public final class ApplicationFilterChain implements FilterChain {// 责任链上 Filter 的维护对象private ApplicationFilterConfig[] filters = new ApplicationFilterConfig[0];//责任链上待执行的 Filter 对象private int pos = 0;// 责任链上拥有的 Filter 数量private int n = 0;// 责任链的执行private void internalDoFilter(ServletRequest request,ServletResponse response)throws IOException, ServletException {// 在责任链未执行完的情况下执行责任链         if (pos < n) {// 获取当前待执行的 Filter,同时递增下一次待执行责任链的下标ApplicationFilterConfig filterConfig = filters[pos++];try {Filter filter = filterConfig.getFilter();if( Globals.IS_SECURITY_ENABLED ) {// 省略相关代码} else {filter.doFilter(request, response, this);}} catch (Throwable e) {}return;}try {if ((request instanceof HttpServletRequest) &&(response instanceof HttpServletResponse) &&Globals.IS_SECURITY_ENABLED ) {// 执行正常的业务逻辑} else {servlet.service(request, response);}} catch (Throwable e) {e = ExceptionUtils.unwrapInvocationTargetException(e);throw new ServletException(sm.getString("filterChain.servlet"), e);}}
}

说明:

  • 整个责任链上Filter处理器的执行通过处理器自驱进行实现,而非由责任链对象驱动。

  • Filter处理器的在处理过程中除了执行自我逻辑,会通过 filterChain.doFilter(servletRequest, servletResponse) 触发下一个处理器的执行。

3.3 Dubbo

3.3.1 Dubbo Filter介绍

图片

图片分享自《DUBBO官网》

  • Dubbo的Filter作用时机如上图所示,Filter实现是专门为服务提供方和服务消费方调用过程进行拦截,Dubbo本身的大多功能均基于此扩展点实现,每次远程方法执行该拦截都会被执行。

  • Dubbo官方针对Filter做了很多的原生支持,目前大致有20来个吧,包括我们熟知的RpcContext,accesslog功能都是通过filter来实现了。

  • 在实际业务开发中会对Filter接口进行扩展,在服务调用链路中嵌入我们自身的处理逻辑,如日志打印、调用耗时统计等。

3.3.2 处理器介绍

@Activate(group = PROVIDER, value = ACCESS_LOG_KEY)
public class AccessLogFilter implements Filter {@Overridepublic Result invoke(Invoker<?> invoker, Invocation inv) throws RpcException {try {if (ConfigUtils.isNotEmpty(accessLogKey)) {AccessLogData logData = buildAccessLogData(invoker, inv);log(accessLogKey, logData);}} catch (Throwable t) {}// 执行下一个invokerreturn invoker.invoke(inv);}
}

说明:

  • Dubbo中的自定义Filter需要实现org.apache.dubbo.rpc.Filter类,内部通过实现invoke方法来实现自定义逻辑。

  • 自定义Filter内部除了实现必要的自定义逻辑外,核心的需要通过invoker.invoke(inv)触发下一个过滤器的执行。

3.3.3 责任链构建

public class ProtocolFilterWrapper implements Protocol {private final Protocol protocol;public ProtocolFilterWrapper(Protocol protocol) {this.protocol = protocol;}private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {// 最后的 Invoker 对象Invoker<T> last = invoker;// 遍历所有 Filter 对象,构建责任链         List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);if (!filters.isEmpty()) {for (int i = filters.size() - 1; i >= 0; i--) {// 每个 Filter 封装成一个 Invoker 对象,通过 filter.invoke进行串联final Filter filter = filters.get(i);final Invoker<T> next = last;last = new Invoker<T>() {@Overridepublic Result invoke(Invocation invocation) throws RpcException {return filter.invoke(next, invocation);}};}}return last;}
}// 封装了Filter的invoker对象
static final class ProtocolFilterWrapper.1 implements Invoker < T > {final Invoker val$invoker;final Filter val$filter;// 指向下一个Invoker的变量final Invoker val$next;public Result invoke(Invocation invocation) throws RpcException {return this.val$filter.invoke(this.val$next, invocation);}ProtocolFilterWrapper.1(Invoker invoker, Filter filter, Invoker invoker2) {this.val$invoker = invoker;this.val$filter = filter;this.val$next = invoker2;}
}

说明:

  • ProtocolFilterWrapper 通过 buildInvokerChain 构建 Dubbo Filter 的责任链。

  • 责任链上的处理器对象是将 Filter 封装的 Invoker 对象,每个 Invoker 对象指向下一个处理器封装的Invoker对象。

3.3.4 责任链执行

public class FailfastClusterInvoker<T> extends AbstractClusterInvoker<T> {public FailfastClusterInvoker(Directory<T> directory) {super(directory);}@Overridepublic Result doInvoke(Invocation invocation, List<Invoker<T>> invokers, LoadBalance loadbalance) throws RpcException {checkInvokers(invokers, invocation);Invoker<T> invoker = select(loadbalance, invocation, invokers, null);try {// 执行封装了Filter的invoker对象,驱动处理器的执行return invoker.invoke(invocation);} catch (Throwable e) {}}
}static final class ProtocolFilterWrapper.1 implements Invoker < T > {final Invoker val$invoker;final Filter val$filter;final Invoker val$next;public Result invoke(Invocation invocation) throws RpcException {return this.val$filter.invoke(this.val$next, invocation);}ProtocolFilterWrapper.1(Invoker invoker, Filter filter, Invoker invoker2) {this.val$invoker = invoker;this.val$filter = filter;this.val$next = invoker2;}

说明:

  • 每个Invoker对象invoke方法会执行自定义逻辑,并触发下一个处理器的执行。

  • 整个责任链上处理器的执行通过Invoker对象的驱动,而非责任链对象的驱动。

3.4 Sentinel

3.4.1 Sentinel Slot介绍

图片

图片分享自《Sentinel官网》

  • Sentinel是面向分布式服务架构的流量治理组件,以流量为切入点提供熔断限流的功能保证系统的稳定性。

  • Sentinel 里面以Entry作为限流的资源对象,每个Entry创建的同时会关联一系列功能插槽(slot chain)。

  • Sentinel提供了通用的原生Slot处理不同的逻辑,同时支持自定义Slot来定制功能。

3.4.2 处理器介绍

public interface ProcessorSlot<T> {void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, boolean prioritized,Object... args) throws Throwable;void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized,Object... args) throws Throwable;void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);void fireExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
}public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {private AbstractLinkedProcessorSlot<?> next = null;@Overridepublic void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)throws Throwable {// 触发下一个处理器对象的处理if (next != null) {next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);}}void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)throws Throwable {T t = (T)o;// 执行具体处理器的逻辑,由具体的处理器自行实现entry(context, resourceWrapper, t, count, prioritized, args);}public void setNext(AbstractLinkedProcessorSlot<?> next) {// 绑定下一个处理器的逻辑this.next = next;}
}public class NodeSelectorSlot extends AbstractLinkedProcessorSlot<Object> {private volatile Map<String, DefaultNode> map = new HashMap<String, DefaultNode>(10);@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)throws Throwable {// 1、处理器处理本身的逻辑DefaultNode node = map.get(context.getName());context.setCurNode(node);// 2、处理器驱动触发下一个处理器fireEntry(context, resourceWrapper, node, count, prioritized, args);}
}

说明:

  • Sentinel 中的 Slot 需要实现 com.alibaba.csp.sentinel.slotchain.ProcessorSlot 的通用接口。

  • 自定义 Slot 一般继承抽象类 AbstractLinkedProcessorSlot 且只要改写 entry/exit 方法实现自定义逻辑。

  • Slot 通过 next 变量保存下一个处理器Slot对象。

  • 在自定义实现的 entry 方法中需要通过 fireEntry 触发下一个处理器的执行,在 exit 方法中通过fireExit 触发下一个处理器的执行。

3.4.3 责任链构建

public class DefaultSlotChainBuilder implements SlotChainBuilder {@Overridepublic ProcessorSlotChain build() {// 责任链的头部对象ProcessorSlotChainProcessorSlotChain chain = new DefaultProcessorSlotChain();// sortedSlotList获取所有的处理器对象List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();for (ProcessorSlot slot : sortedSlotList) {if (!(slot instanceof AbstractLinkedProcessorSlot)) {continue;}// 通过尾添法将职责slot添加到DefaultProcessorSlotChain当中chain.addLast((AbstractLinkedProcessorSlot<?>) slot);}return chain;}
}public class DefaultProcessorSlotChain extends ProcessorSlotChain {// 创建DefaultProcessorSlotChain的头尾节点first和endAbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)throws Throwable {super.fireEntry(context, resourceWrapper, t, count, prioritized, args);}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {super.fireExit(context, resourceWrapper, count, args);}};AbstractLinkedProcessorSlot<?> end = first;@Overridepublic void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {end.setNext(protocolProcessor);end = protocolProcessor;}
}

说明:

  • ProcessorSlotChain作为Slot的责任链,负责责任链的构建和执行。

  • 责任链上的处理器对象 AbstractLinkedProcessorSlot 通过保存指向下一个处理器的对象的进行关联,整体以链表的形式进行串联。

  • 责任链上的第一个处理器对象first本身不起任何作用,只是保存链表的头部。

3.4.4 责任链执行

public class CtSph implements Sph {private Entry entryWithPriority(ResourceWrapper resourceWrapper, int count, boolean prioritized, Object... args)throws BlockException {Context context = ContextUtil.getContext();// 省略相关代码ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);Entry e = new CtEntry(resourceWrapper, chain, context);// 驱动责任链上的第一个处理器,进而由处理器自驱动执行下一个处理器chain.entry(context, resourceWrapper, null, count, prioritized, args);return e;}
}public class DefaultProcessorSlotChain extends ProcessorSlotChain {// 创建DefaultProcessorSlotChain的头尾节点first和endAbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)throws Throwable {super.fireEntry(context, resourceWrapper, t, count, prioritized, args);}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {super.fireExit(context, resourceWrapper, count, args);}};AbstractLinkedProcessorSlot<?> end = first;@Overridepublic void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {end.setNext(protocolProcessor);end = protocolProcessor;}
}public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {private AbstractLinkedProcessorSlot<?> next = null;@Overridepublic void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args)throws Throwable {// 触发下一个处理器对象的处理if (next != null) {next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);}}void transformEntry(Context context, ResourceWrapper resourceWrapper, Object o, int count, boolean prioritized, Object... args)throws Throwable {T t = (T)o;// 执行具体处理器的逻辑,由具体的处理器自行实现entry(context, resourceWrapper, t, count, prioritized, args);}public void setNext(AbstractLinkedProcessorSlot<?> next) {// 绑定下一个处理器的逻辑this.next = next;}
}

说明:

  • 整个责任链上处理器的执行通过Invoker对象的驱动,而非责任链对象的驱动。

  • DefaultProcessorSlotChain的entry首先头部对象first,进而触发处理器的自驱实现处理器的执行。

  • 整体按照entry → fireEntry → transformEntry → entry 的循环顺序依次触发处理器的自驱。

四、实践总结

在日常项目实践中,责任链的设计模式会在很多业务场景中落地。

譬如对于支持用户生成内容(UGC)的应用来说,用户生成的内容可能包含一些敏感内容如敏感言论或者图片等。针对这种应用场景,可以通过责任链模式设置多个处理器来处理不同的任务,如文本过滤器处理敏感词,图片过滤器处理敏感图片等等。

譬如对于电商服务中的下单流程来说,一个下单流程包含订单拆合单,优惠计算,订单生成等多个步骤,我们可以通过责任链模式设置多个处理器来处理不同的任务等等。

责任链的应用场景非常广泛,在常见的开源框架中有丰富的落地场景,同样在业务开发中也可以根据场景灵活使用。

相关文章:

开源框架中的责任链模式实践

作者&#xff1a;vivo 互联网服务器团队-Wang Zhi 责任链模式作为常用的设计模式而被大家熟知和使用。本文介绍责任链的常见实现方式&#xff0c;并结合开源框架如Dubbo、Sentinel等进行延伸探讨。 一、责任链介绍 在GoF 的《设计模式》一书中对责任链模定义的&#xff1a;将…...

智能配电系统:保障电力运行安全、可控与高效

智能配电系统是一种先进的电力分配技术&#xff0c;它通过智能化、数字化和网络化等方式&#xff0c;有效地保障了电力运行的安全、可控和高效。 力安科技智能配电系统是在配电室&#xff08;含高压柜、变压器、低压柜&#xff09;、箱式变电站、配电箱及动力柜&#xff08…...

MySQL学习系列(11)-每天学习10个知识

目录 1. 数据库设计的关键因素2. 使用存储过程和函数来提高性能和可重用性3. MySQL性能优化4. 使用视图简化查询和提供数据安全性5. 数据库备份和恢复策略的重要性和实践经验6. 在分布式系统中保证数据一致性和可用性7. 理解MySQL的复制和其在实际应用中的作用8. 使用游标进行分…...

如何通过Gunicorn和Niginx部署Django

本文主要介绍如何配置Niginx加载Django的静态资源文件&#xff0c;也就是Static 1、首先需要将Django项目中的Settings.py 文件中的两个参数做以下设置&#xff1a; STATIC_URL /static/ STATIC_ROOT os.path.join(BASE_DIR, static) 然后在宝塔面板中执行python manage.…...

C语言 cortex-A7核UART总线实验

一、C 1&#xff09;uart4.h #ifndef __UART4_H__ #define __UART4_H__ #include "stm32mp1xx_rcc.h" #include "stm32mp1xx_gpio.h" #include "stm32mp1xx_uart.h&quo…...

asp.net C#免费反编译工具ILSpy

在维护一个没有源码的C#项目&#xff0c;只能反编译了。 项目主页 https://github.com/icsharpcode/ILSpy 使用方法 中文界面使用简单&#xff0c;把你要反编译的dll拖过去就可以了。好使&#xff01;&#xff01;&#xff01;...

演讲实录:DataFun 垂直开发者社区基于指标平台自主洞察北极星指标

在7月14日举办的 Kyligence 用户大会的数智新应用论坛上&#xff0c;DataFun COO 杜颖女士为大家带来了《垂直开发者社区基于指标平台自主洞察北极星指标》的主题演讲。接下来&#xff0c;我们一起看看 DataFun 如何在没有专门的 IT 团队的情况下&#xff0c;实现对北极星指标的…...

ffmpeg编译 Error: operand type mismatch for `shr‘

错误如下&#xff1a; D:\msys2\tmp\ccUxvBjQ.s: Assembler messages: D:\msys2\tmp\ccUxvBjQ.s:345: Error: operand type mismatch for shr D:\msys2\tmp\ccUxvBjQ.s:410: Error: operand type mismatch for shr D:\msys2\tmp\ccUxvBjQ.s:470: Error: operand type mismatch…...

【Windows Server 2012 R2搭建FTP站点】

打开服务器管理器——添加角色和功能 下一步 下一步 下一步 选择FTP服务器&#xff0c;勾上FTP服务和FTP扩展&#xff0c;点击下一步 安装 安装完成关闭 打开我们的IIS服务器 在WIN-XXX主页可以看到我们的FTP相关菜单 右键WIN-XXXX主页&#xff0c;添加FTP站点 输入站点名称-FT…...

python教程:使用gevent实现高并发并限制最大并发数

嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取 import time import gevent from gevent.pool import Pool from gevent import monkey # 一&#xff0c;定义最大并发数 p Pool(20) # 二&#xff0c;导入gevent…...

借助reCAPTCHA实现JavaScript验证码功能

前言 验证码&#xff08;CAPTCHA&#xff09;是一种常见的安全验证机制&#xff0c;常用于区分真实用户和机器人。使用验证码可以有效防止恶意登录、自动注册或者密码爆破等攻击。本文将借助reCAPTCHA第三方库来实现JavaScript验证码功能。 验证码的原理 验证码的核心思想是要…...

监控数据的采集方式及原理

采集方法使用频率从高到低依次是读取 /proc目录、执行命令行工具、远程黑盒探测、拉取特定协议的数据、连接到目标对象执行命令、代码埋点、日志解析。 读取 /proc目录 /proc是一个位于内存中的伪文件系统&#xff0c;而在该目录下保存的不是真正的文件和目录&#xff0c;而是…...

Vue路由与node.js环境搭建

目录 前言 一.Vue路由 1.什么是spa 1.1简介 1.2 spa的特点 1.3 spa的优势以及未来的挑战 2.路由的使用 2.1 导入JS依赖 2.2 定义两个组件 2.3 定义组件与路径对应关系 2.4 通过路由关系获取路由对象 2.5 将对象挂载到vue实例中 2.6 定义触发路由事件的按钮 2.7 定…...

腾讯云16核服务器性能测评_轻量和CVM配置大全

腾讯云16核服务器配置大全&#xff0c;CVM云服务器可选择标准型S6、标准型SA3、计算型C6或标准型S5等&#xff0c;目前标准型S5云服务器有优惠活动&#xff0c;性价比高&#xff0c;计算型C6云服务器16核性能更高&#xff0c;轻量16核32G28M带宽优惠价3468元15个月&#xff0c;…...

Postman应用——下载注册和登录

文章目录 下载安装注册登录注册账号登录账号 下载安装 Postman下载&#xff1a;https://www.postman.com/ 访问链接后&#xff0c;进入首页&#xff0c;根据自己的操作系统下载对应的版本。 找到下载到的目录直接双击.exe文件&#xff0c;会默认安装在C盘&#xff0c;安装完会…...

uni-app混合开发 navigateTo、reLaunch、redirectTo、switchTab区别

1.navigateTo 保留当前页面&#xff0c;跳转到应用内的某个页面&#xff0c;使用uni.navigateBack可以返回到原页面。 要注意的是navigateTo只能跳转的应用内非 tabBar 的页面的路径 , 路径后可以带参数&#xff1b;如果跳转url参数为tabBar的路径则无法进行跳转 2.redir…...

专业软件测评中心:关于软件性能测试的实用建议

软件性能测试是通过自动化的测试工具模拟多种正常、峰值以及异常负载条件来对系统的各项性能指标进行测试。性能测试在软件的质量保证中起着重要的作用&#xff0c;它包括的测试内容丰富多样。 一、软件性能测试的实用建议   1、制定清晰的测试目标&#xff1a;明确测试目标…...

vue项目通过json-bigint在前端处理java雪花id过长导致失去精度问题

这里 我简单模仿了一个接口 这里 我单纯 返回一个long类型的雪花id 然后 前端 用 axios 去请求 大家知道 axios 会对请求数据做一次处理 而我们 data才是拿到我们java这边实际返回的东西 简单说 就是输出一下我们后端返回 的内容 这里 我们网络中显示的是 35866101868095488…...

【全志V3s】SPI NAND Flash 驱动开发

文章目录 一、硬件介绍V3s的启动顺序 二、驱动支持U-Boot驱动主线 Linux 驱动已经支持 三、烧录工具 xfel四、构建U-Boot&#xff08;官方的Uboot&#xff09;先编译一下开始spi nand flash 代码层面的适配修改menuconfig配置ARM architecture配置Support for SPI Nand Flash o…...

【二叉树】二叉树展开为链表-力扣 114 题

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kuan 的首页,持续学…...

NLP文本生成全解析:从传统方法到预训练完整介绍

目录 1. 引言1.1 文本生成的定义和作用1.2 自然语言处理技术在文本生成领域的使用 2 传统方法 - 基于统计的方法2.1.1 N-gram模型2.1.2 平滑技术 3. 传统方法 - 基于模板的生成3.1 定义与特点3.2 动态模板 4. 神经网络方法 - 长短时记忆网络(LSTM)LSTM的核心概念PyTorch中的LST…...

OpenBA:开源模型家族再添一员!从头训练的15B中英非对称Encoder-Decoder结构双语模型...

苏州大学从头训练的双语非对称Encoder-Decoder模型OpenBA已正式开源&#xff01; 主要亮点包括&#xff1a; 亮点一&#xff1a;此模型为中文开源社区贡献了一个有代表性的编码器解码器大语言模型&#xff0c;其训练过程&#xff08;包括数据收集与清洗、模型构建与训练&#x…...

安防视频监控平台EasyNVR无法控制云台,该如何解决?

TSINGSEE青犀视频安防监控平台EasyNVR可支持设备通过RTSP/Onvif协议接入&#xff0c;并能对接入的视频流进行处理与多端分发&#xff0c;包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等多种格式。在智慧安防等视频监控场景中&#xff0c;EasyNVR可提供视频实时监控直播、云端…...

基座向量施密特正交化

最近再次细细的阅读了向量施密特正交化&#xff0c;重新系统梳理一下 一、正交基地与向量的正交分解 二、基化成标准正交基&#xff0c;是什么意思 将一个向量空间中的基向量通过某种方式转化为一组标准正交基&#xff0c;是指将原有的基向量进行调整&#xff0c;使得它们满足…...

OpenCV图像金字塔

什么是图像金字塔&#xff1f; 向上采样 &#xff1a;cv.pyrUp(img) 向下采样 : cv.pyrDown(img) 代码实现 import numpy as np import cv2 as cv import matplotlib.pyplot as plt#图像的读取 img cv.imread("lena.png")#进行图像采样 up_img cv.pyrUp(img) #上…...

Arduino驱动MMA7361/MMA7360三轴加速度传感器(惯性测量传感器篇)

目录 1、传感器特性 2、控制器和传感器连线图 3、驱动程序 MMA7361三轴加速度传感器是替代停产的MMA7260三轴加速度传感器,三轴加速度传感器是一种可以对物体运动过程中的加速度进行测量的电子设备,典型互动应用中的加速度传感器可以用来对物体的姿态或者运动方向进行检测…...

ceph分布式存储

目录 一、概述 1、特点 2、组件 3、架构图 二、分布式部署 1、环境拓扑 2、实验准备 3、ceph安装 安装 初始化monitor 同步管理信息 安装mgr&#xff08;管理守护进程&#xff09; 安装rgw 创建mds服务 OSD安装 4、dashboard安装 开启dashboard模块 生成签名 …...

DA1--用pandas查看网站用户数据

目录 1.题目描述 2.输入描述 3.输出描述 4.题目分析 5.通过代码 1.题目描述 现有一个Nowcoder.csv文件&#xff0c;它记录了牛客网的部分用户数据&#xff0c;包含如下字段&#xff08;字段与字段之间以逗号间隔&#xff09;&#xff1a; Nowcoder_ID&#xff1a;用户ID …...

JWT令牌

一、JWT&#xff08;Json Web Token&#xff09;能干什么 1、安全认证&#xff08;权限认证&#xff09; 比如登录系统的时候&#xff0c;服务器会检查前端请求数据中携带的token信息&#xff0c;符合标准则允许访问&#xff0c;不符合则拒绝你的访问请求。 2、信息传递 比…...

uni-app使用CSS实现无限旋转动画

本来想用uni.createAnimation创建一个旋转动画&#xff0c;发现转完一圈后就不动了&#xff0c;没法循环旋转&#xff0c; 后来又用setInterval每隔一个周期就把旋转角度加180度&#xff0c;发现运行一段时间后动画逐渐崩坏&#xff0c;应该是动画的周期和定时器的周期时间没有…...

做网站合肥/成人厨师短期培训班

requestAnimationFrame window.requestAnimationFrame() 方法跟 setTimeout 类似&#xff0c;都是推迟某个函数的执行。不同之处在于&#xff0c;setTimeout 必须指定推迟的时间; window.requestAnimationFrame() 则是推迟到浏览器下一次重流时执行&#xff0c;执行完才会进行…...

做直播网站需要手续/站长之家seo工具包

现象内存占用随着训练过程逐渐增大&#xff0c;最终Out of memory。即使将不再用到的变量及时删除并使用gc.collect()&#xff0c;也无法解决。解决方案方案1&#xff1a;Dataset 的成员变量避免使用Python原生数据格式以下面的Dataset代码为例&#xff0c;如果使用self.data …...

网站设计中国内优秀企业网站欣赏/it菜鸡网seo

我们寻常意义上的站长工具&#xff0c;是站长建站时用于对网站质量查询与制作的一些工具。 最为常见的特点&#xff0c;是查询SEO之于搜索引擎的数据变化&#xff0c;可以检测网站死链接、蜘蛛访问、HTML格式检测、网站速度测试、友情链接检查、网站域名IP查询等等。 但我们今…...

策划网站做营销推广/学校教育培训机构

缓存实现的过程以及淘汰旧页面的机制不同&#xff0c;所以会有不同缓存调度方法&#xff0c;就常见的就是FIFO&#xff0c;LRU&#xff0c;LFU缓存过期策略。 1.FIFO&#xff08;First In First out&#xff09;&#xff1a;先见先出&#xff0c;淘汰最先近来的页面&#xff0…...

做网站什么主题比较好/北京seo培训机构

浅谈 Linux 内核无线子系统 本文目录 1. 全局概览2. 模块间接口3. 数据路径与管理路径4. 数据包是如何被发送&#xff1f;5. 谈谈管理路径6. 数据包又是如何被接收&#xff1f;7. 总结一下Linux 内核是如何实现无线网络接口呢&#xff1f;数据包是通过怎样的方式被发送和接收呢…...

建设企业网站的目的/线上推广的渠道有哪些

点击下面链接&#xff0c;来测测您的javaScript水平吧。 JavaScript Puzzlers! 21. function f() {} var a f.prototype, b Object.getPrototypeOf(f); a b; //false f.prototype输出Object {constructor: function}&#xff0c;是f的实例对象的原型&#xff08;实例对象就是…...