区块链技术博客
www.b2bchain.cn

源码解读一 :ProcessorSlot责任链求职学习资料

本文介绍了源码解读一 :ProcessorSlot责任链求职学习资料,有助于帮助完成毕业设计以及求职,是一篇很好的资料。

对技术面试,学习经验等有一些体会,在此分享。

源码解读一 :ProcessorSlot责任链

  • 源码解读一 :ProcessorSlot责任链
    • 概述
    • 代码实现
      • 初始化 ProcessorSlotChain
        • 获取 SlotChainBuilder
        • DefaultSlotChainBuilder 构建 ProcessorSlotChain 实例
        • DefaultProcessorSlotChain 内部逻辑

概述

在总体设计章节中,我们知道 Sentinel 是通过不同的 ProcessorSlot 实现不同的功能,并且将不同的 ProcessorSlot 组装为一个责任链,也就是 ProcessorSlotChain,来对每一次资源访问使用不同 ProcessorSlot 进行处理从而实现对应的功能。那么在此之前,首先就需要进行 ProcessorSlotChain 的创建。

在 Sentinel 中,每一个资源都有与之对应的独立的 ProcessorSlotChain 。为此,Sentinel 中提供了一个方法 CtSph#lookProcessChain 来获取资源对应的 ProcessorSlotChain 。下面会针对整个获取和初始化的流程进行分析。

代码实现

初始化 ProcessorSlotChain

获取 SlotChainBuilder

Sentinel 所有的扩展点都是通过 SPI 的方式来实现。获取 ProcessorSlotChain 也不例外。对象 CtSph 内部有一个 Map 类型的属性 chainMap 用于存储资源和 ProcessorSlotChain 的映射关闭。方法 CtSph#lookProcessChain 首先是在这个 chainMap 中查找当前资源是否已经存在对应的 ProcessorSlotChain 。如果存在的话直接返回即可,如果不存在则新建对应的链条,并且汇总之前 chainMap 中的数据,创建一个新的 chainMap 对象来取代原先的 chainMap 对象。采用这种方式是因为一个系统一旦运行,其资源大致上是固定的,对应的链条也是固定的。

这是一个读多写少的场景,因此使用 HashMap 比 ConcurrentHashMap 更有性能上的优势。在 Sentinel 的其他配置信息上也都能看到这种实现思路。用非线程安全的容器承载数据,当容器内的数据发生变化时,则直接创建新的容器来替换旧的容器。通过这种方式实现多线程间正确的并发读可见性,并且因为是读多写少的场景,非线程安全的容器比线程安全的并发容器在性能表现上要更好。

如果需要新建 ProcessorSlotChain,则是调用静态方法 SlotChainProvider#newSlotChain 来实现。从类名 SlotChainProvider 就可以看出,该类的职责就是提供一个 ProcessorSlotChain 实例。但是具体的 ProcessorSlotChain 并不是该类来直接创建的,而是委托给了 SlotChainBuilder 这个接口。整体 newSlotChain 方法的流程如下

源码解读一 :ProcessorSlot责任链

这边唯一需要展开说明的就是这个 SPI 加载机制。Sentinel 对 SPI 机制的使用是自己封装了一个 SpiLoader 的类来实现的。其方法loadFirstInstanceOrDefault 从名字就可以看出含义:通过 SPI 机制加载第一个实例,如果不存在,则使用默认实例。方法的内部实现也仅仅是使用了 SPI 机制加载对应的接口的实现罢了,不展开说明。在默认使用情况下,也就是开发者都没有通过 SPI 进行这些接口的扩展情况下,Sentinel 都提供了默认的实现类,在这里的就是 DefaultSlotChainBuilder 。此外,该方法会使用一个类变量 SERVICE_LOADER_MAP ,用来存储类名和 ServiceLoader 的映射关系。这意味着多次调用该方法得到的实例(如果是从 SPI 方式中加载的)是相同的。这对于某些全局属性或者只需要初始化一次的类是有利的。而如果需要多实例的场合,该方法就不太适用了。

DefaultSlotChainBuilder 构建 ProcessorSlotChain 实例

DefaultSlotChainBuilder 的 build 方法很简单,其主要内容就是:

  • 创建 DefaultProcessorSlotChain 实例。
  • 通过 SpiLoader ,使用 SPI 机制加载 ProcessorSlot 实例,并且根据对应类上的 SpiOrder 注解信息将实例进行排序。
  • 将排序后的 ProcessorSlot 实例按照顺序添加到 DefaultProcessorSlotChain 中,返回该 DefaultProcessorSlotChain 实例。

这边先讲讲第二步,也就是加载 ProcessorSlot 的过程。这一步是通过方法 SpiLoader#loadPrototypeInstanceListSorted 来实现加载的,方法名也表明了该方法返回的是一个排序后的实例列表。此外,该方法多次调用,每次返回的列表中都是全新的实例。这是因为该方法用于通过 SPI 加载 ProcessorSlot 实例列表,前文提到过,在 Sentinel 中,对于每一个资源,其都拥有独立的 ProcessorSlot 链条。也就是说,每一个不同的资源,其对应的 ProcessorSlot 处理器也是独立的。

了解了这一点后,loadPrototypeInstanceListSorted 方法就没有太多的内容了。方法的实现步骤主要是:

  • 通过 SPI 机制加载 ProcessorSlot 实例。
  • 解析 ProcessorSlot 实现类上的 SpiOrder 注解,取得注解中的数字值作为排序依据。如果注解不存在的话,则排序数字值按照最小数字处理。
  • 依据每一个 ProcessorSlot 不同的排序数字值,按照升序排序。返回排序后的 ProcessorSlot 列表。

排序的这段代码,Sentinel 写的比较复杂且奇怪,自己通过 List 去实现排序,而没有使用 JDK 直接提供的 Collections#sort(List) 方法。显得代码比较臃肿。

DefaultProcessorSlotChain 内部逻辑

在了解了 SpiLoader#loadPrototypeInstanceListSorted 实现排序加载 ProcessorSlot 实例列表后,我们来看下 DefaultProcessorSlotChain 的内部构造和责任链的实现模式。仍然先看下类图,如下

源码解读一 :ProcessorSlot责任链

链条的基类 ProcessorSlotChain 也实现了处理插槽 ProcessorSlot 接口。这也是责任链模式常见的一种实现方式。就是链条类本身也实现了链条中节点的接口,这样对于外部调用者而言,实际上只需要关心一个处理器接口就好了。无需知道底层是一个链条还是说只是一个节点。

我们先看下 ProcessorSlot 的接口定义,如下

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); }

ProcessorSlot 接口是由对称的两组方法组成 entry 和 fireEntry 为一组,exit 和 fireExit 为一组。在 Sentinel 中的访问资源的模式是

  • 通过 Sph 的 entry 方法,获取对资源的访问许可。如果成功获得 Entry 对象,则说明资源访问请求被允许。

源码解读一 :ProcessorSlot责任链

  • 源码解读一 :ProcessorSlot责任链
    • 概述
    • 代码实现
      • 初始化 ProcessorSlotChain
        • 获取 SlotChainBuilder
        • DefaultSlotChainBuilder 构建 ProcessorSlotChain 实例
        • DefaultProcessorSlotChain 内部逻辑

概述

在总体设计章节中,我们知道 Sentinel 是通过不同的 ProcessorSlot 实现不同的功能,并且将不同的 ProcessorSlot 组装为一个责任链,也就是 ProcessorSlotChain,来对每一次资源访问使用不同 ProcessorSlot 进行处理从而实现对应的功能。那么在此之前,首先就需要进行 ProcessorSlotChain 的创建。

在 Sentinel 中,每一个资源都有与之对应的独立的 ProcessorSlotChain 。为此,Sentinel 中提供了一个方法 CtSph#lookProcessChain 来获取资源对应的 ProcessorSlotChain 。下面会针对整个获取和初始化的流程进行分析。

代码实现

初始化 ProcessorSlotChain

获取 SlotChainBuilder

Sentinel 所有的扩展点都是通过 SPI 的方式来实现。获取 ProcessorSlotChain 也不例外。对象 CtSph 内部有一个 Map 类型的属性 chainMap 用于存储资源和 ProcessorSlotChain 的映射关闭。方法 CtSph#lookProcessChain 首先是在这个 chainMap 中查找当前资源是否已经存在对应的 ProcessorSlotChain 。如果存在的话直接返回即可,如果不存在则新建对应的链条,并且汇总之前 chainMap 中的数据,创建一个新的 chainMap 对象来取代原先的 chainMap 对象。采用这种方式是因为一个系统一旦运行,其资源大致上是固定的,对应的链条也是固定的。

这是一个读多写少的场景,因此使用 HashMap 比 ConcurrentHashMap 更有性能上的优势。在 Sentinel 的其他配置信息上也都能看到这种实现思路。用非线程安全的容器承载数据,当容器内的数据发生变化时,则直接创建新的容器来替换旧的容器。通过这种方式实现多线程间正确的并发读可见性,并且因为是读多写少的场景,非线程安全的容器比线程安全的并发容器在性能表现上要更好。

如果需要新建 ProcessorSlotChain,则是调用静态方法 SlotChainProvider#newSlotChain 来实现。从类名 SlotChainProvider 就可以看出,该类的职责就是提供一个 ProcessorSlotChain 实例。但是具体的 ProcessorSlotChain 并不是该类来直接创建的,而是委托给了 SlotChainBuilder 这个接口。整体 newSlotChain 方法的流程如下

源码解读一 :ProcessorSlot责任链

这边唯一需要展开说明的就是这个 SPI 加载机制。Sentinel 对 SPI 机制的使用是自己封装了一个 SpiLoader 的类来实现的。其方法loadFirstInstanceOrDefault 从名字就可以看出含义:通过 SPI 机制加载第一个实例,如果不存在,则使用默认实例。方法的内部实现也仅仅是使用了 SPI 机制加载对应的接口的实现罢了,不展开说明。在默认使用情况下,也就是开发者都没有通过 SPI 进行这些接口的扩展情况下,Sentinel 都提供了默认的实现类,在这里的就是 DefaultSlotChainBuilder 。此外,该方法会使用一个类变量 SERVICE_LOADER_MAP ,用来存储类名和 ServiceLoader 的映射关系。这意味着多次调用该方法得到的实例(如果是从 SPI 方式中加载的)是相同的。这对于某些全局属性或者只需要初始化一次的类是有利的。而如果需要多实例的场合,该方法就不太适用了。

DefaultSlotChainBuilder 构建 ProcessorSlotChain 实例

DefaultSlotChainBuilder 的 build 方法很简单,其主要内容就是:

  • 创建 DefaultProcessorSlotChain 实例。
  • 通过 SpiLoader ,使用 SPI 机制加载 ProcessorSlot 实例,并且根据对应类上的 SpiOrder 注解信息将实例进行排序。
  • 将排序后的 ProcessorSlot 实例按照顺序添加到 DefaultProcessorSlotChain 中,返回该 DefaultProcessorSlotChain 实例。

这边先讲讲第二步,也就是加载 ProcessorSlot 的过程。这一步是通过方法 SpiLoader#loadPrototypeInstanceListSorted 来实现加载的,方法名也表明了该方法返回的是一个排序后的实例列表。此外,该方法多次调用,每次返回的列表中都是全新的实例。这是因为该方法用于通过 SPI 加载 ProcessorSlot 实例列表,前文提到过,在 Sentinel 中,对于每一个资源,其都拥有独立的 ProcessorSlot 链条。也就是说,每一个不同的资源,其对应的 ProcessorSlot 处理器也是独立的。

了解了这一点后,loadPrototypeInstanceListSorted 方法就没有太多的内容了。方法的实现步骤主要是:

  • 通过 SPI 机制加载 ProcessorSlot 实例。
  • 解析 ProcessorSlot 实现类上的 SpiOrder 注解,取得注解中的数字值作为排序依据。如果注解不存在的话,则排序数字值按照最小数字处理。
  • 依据每一个 ProcessorSlot 不同的排序数字值,按照升序排序。返回排序后的 ProcessorSlot 列表。

排序的这段代码,Sentinel 写的比较复杂且奇怪,自己通过 List 去实现排序,而没有使用 JDK 直接提供的 Collections#sort(List) 方法。显得代码比较臃肿。

DefaultProcessorSlotChain 内部逻辑

在了解了 SpiLoader#loadPrototypeInstanceListSorted 实现排序加载 ProcessorSlot 实例列表后,我们来看下 DefaultProcessorSlotChain 的内部构造和责任链的实现模式。仍然先看下类图,如下

源码解读一 :ProcessorSlot责任链

链条的基类 ProcessorSlotChain 也实现了处理插槽 ProcessorSlot 接口。这也是责任链模式常见的一种实现方式。就是链条类本身也实现了链条中节点的接口,这样对于外部调用者而言,实际上只需要关心一个处理器接口就好了。无需知道底层是一个链条还是说只是一个节点。

我们先看下 ProcessorSlot 的接口定义,如下

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); }

ProcessorSlot 接口是由对称的两组方法组成 entry 和 fireEntry 为一组,exit 和 fireExit 为一组。在 Sentinel 中的访问资源的模式是

  • 通过 Sph 的 entry 方法,获取对资源的访问许可。如果成功获得 Entry 对象,则说明资源访问请求被允许。

源码解读一 :ProcessorSlot责任链

  • 源码解读一 :ProcessorSlot责任链
    • 概述
    • 代码实现
      • 初始化 ProcessorSlotChain
        • 获取 SlotChainBuilder
        • DefaultSlotChainBuilder 构建 ProcessorSlotChain 实例
        • DefaultProcessorSlotChain 内部逻辑

概述

在总体设计章节中,我们知道 Sentinel 是通过不同的 ProcessorSlot 实现不同的功能,并且将不同的 ProcessorSlot 组装为一个责任链,也就是 ProcessorSlotChain,来对每一次资源访问使用不同 ProcessorSlot 进行处理从而实现对应的功能。那么在此之前,首先就需要进行 ProcessorSlotChain 的创建。

在 Sentinel 中,每一个资源都有与之对应的独立的 ProcessorSlotChain 。为此,Sentinel 中提供了一个方法 CtSph#lookProcessChain 来获取资源对应的 ProcessorSlotChain 。下面会针对整个获取和初始化的流程进行分析。

代码实现

初始化 ProcessorSlotChain

获取 SlotChainBuilder

Sentinel 所有的扩展点都是通过 SPI 的方式来实现。获取 ProcessorSlotChain 也不例外。对象 CtSph 内部有一个 Map 类型的属性 chainMap 用于存储资源和 ProcessorSlotChain 的映射关闭。方法 CtSph#lookProcessChain 首先是在这个 chainMap 中查找当前资源是否已经存在对应的 ProcessorSlotChain 。如果存在的话直接返回即可,如果不存在则新建对应的链条,并且汇总之前 chainMap 中的数据,创建一个新的 chainMap 对象来取代原先的 chainMap 对象。采用这种方式是因为一个系统一旦运行,其资源大致上是固定的,对应的链条也是固定的。

这是一个读多写少的场景,因此使用 HashMap 比 ConcurrentHashMap 更有性能上的优势。在 Sentinel 的其他配置信息上也都能看到这种实现思路。用非线程安全的容器承载数据,当容器内的数据发生变化时,则直接创建新的容器来替换旧的容器。通过这种方式实现多线程间正确的并发读可见性,并且因为是读多写少的场景,非线程安全的容器比线程安全的并发容器在性能表现上要更好。

如果需要新建 ProcessorSlotChain,则是调用静态方法 SlotChainProvider#newSlotChain 来实现。从类名 SlotChainProvider 就可以看出,该类的职责就是提供一个 ProcessorSlotChain 实例。但是具体的 ProcessorSlotChain 并不是该类来直接创建的,而是委托给了 SlotChainBuilder 这个接口。整体 newSlotChain 方法的流程如下

源码解读一 :ProcessorSlot责任链

这边唯一需要展开说明的就是这个 SPI 加载机制。Sentinel 对 SPI 机制的使用是自己封装了一个 SpiLoader 的类来实现的。其方法loadFirstInstanceOrDefault 从名字就可以看出含义:通过 SPI 机制加载第一个实例,如果不存在,则使用默认实例。方法的内部实现也仅仅是使用了 SPI 机制加载对应的接口的实现罢了,不展开说明。在默认使用情况下,也就是开发者都没有通过 SPI 进行这些接口的扩展情况下,Sentinel 都提供了默认的实现类,在这里的就是 DefaultSlotChainBuilder 。此外,该方法会使用一个类变量 SERVICE_LOADER_MAP ,用来存储类名和 ServiceLoader 的映射关系。这意味着多次调用该方法得到的实例(如果是从 SPI 方式中加载的)是相同的。这对于某些全局属性或者只需要初始化一次的类是有利的。而如果需要多实例的场合,该方法就不太适用了。

DefaultSlotChainBuilder 构建 ProcessorSlotChain 实例

DefaultSlotChainBuilder 的 build 方法很简单,其主要内容就是:

  • 创建 DefaultProcessorSlotChain 实例。
  • 通过 SpiLoader ,使用 SPI 机制加载 ProcessorSlot 实例,并且根据对应类上的 SpiOrder 注解信息将实例进行排序。
  • 将排序后的 ProcessorSlot 实例按照顺序添加到 DefaultProcessorSlotChain 中,返回该 DefaultProcessorSlotChain 实例。

这边先讲讲第二步,也就是加载 ProcessorSlot 的过程。这一步是通过方法 SpiLoader#loadPrototypeInstanceListSorted 来实现加载的,方法名也表明了该方法返回的是一个排序后的实例列表。此外,该方法多次调用,每次返回的列表中都是全新的实例。这是因为该方法用于通过 SPI 加载 ProcessorSlot 实例列表,前文提到过,在 Sentinel 中,对于每一个资源,其都拥有独立的 ProcessorSlot 链条。也就是说,每一个不同的资源,其对应的 ProcessorSlot 处理器也是独立的。

了解了这一点后,loadPrototypeInstanceListSorted 方法就没有太多的内容了。方法的实现步骤主要是:

  • 通过 SPI 机制加载 ProcessorSlot 实例。
  • 解析 ProcessorSlot 实现类上的 SpiOrder 注解,取得注解中的数字值作为排序依据。如果注解不存在的话,则排序数字值按照最小数字处理。
  • 依据每一个 ProcessorSlot 不同的排序数字值,按照升序排序。返回排序后的 ProcessorSlot 列表。

排序的这段代码,Sentinel 写的比较复杂且奇怪,自己通过 List 去实现排序,而没有使用 JDK 直接提供的 Collections#sort(List) 方法。显得代码比较臃肿。

DefaultProcessorSlotChain 内部逻辑

在了解了 SpiLoader#loadPrototypeInstanceListSorted 实现排序加载 ProcessorSlot 实例列表后,我们来看下 DefaultProcessorSlotChain 的内部构造和责任链的实现模式。仍然先看下类图,如下

源码解读一 :ProcessorSlot责任链

链条的基类 ProcessorSlotChain 也实现了处理插槽 ProcessorSlot 接口。这也是责任链模式常见的一种实现方式。就是链条类本身也实现了链条中节点的接口,这样对于外部调用者而言,实际上只需要关心一个处理器接口就好了。无需知道底层是一个链条还是说只是一个节点。

我们先看下 ProcessorSlot 的接口定义,如下

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); }

ProcessorSlot 接口是由对称的两组方法组成 entry 和 fireEntry 为一组,exit 和 fireExit 为一组。在 Sentinel 中的访问资源的模式是

  • 通过 Sph 的 entry 方法,获取对资源的访问许可。如果成功获得 Entry 对象,则说明资源访问请求被允许。

部分转自互联网,侵权删除联系

赞(0) 打赏
部分文章转自网络,侵权联系删除b2bchain区块链学习技术社区 » 源码解读一 :ProcessorSlot责任链求职学习资料
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

b2b链

联系我们联系我们