概述
如下图,为sentinel的一个原理图,可以看到基本就是通过各个slot来进行资源统计收集限流等,因此为了更好的了解sentinel的底层实现细节,我们对sentinel的slot的组装和初始化进行一个分析,为后期其他功能分析打好基础。
从官方实例开始入手
sentinel的代码量还是比较多,所以无法快速定位slot的初始化的代码,因此最方便也是最直接的方式就是从入口分析开始,我们先看一下官方示例,如下代码我们可以看到SphU.entry才是限流的入口,我们就从这个方法开始分析
public static void main(String[] args) {
// 配置规则.
initFlowRules();
while (true) {
// 1.5.0 版本开始可以直接利用 try-with-resources 特性
try (Entry entry = SphU.entry("HelloWorld")) {
// 被保护的逻辑
System.out.println("hello world");
} catch (BlockException ex) {
// 处理被流控的逻辑
System.out.println("blocked!");
}
}
}
private static void initFlowRules(){
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("HelloWorld");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
// Set limit QPS to 20.
rule.setCount(20);
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
1. 限流入口
可以看到这里调用了Env.sph的entry的方法传递了,对应的参数,看一下Evn.sph的具体实现
2.Env.sph的具体实现
sph的具体实现为CtSph类,所以我们下一步分析entry的逻辑
3. entry逻辑分析
这里最终调用了entryWithPriority方法,所以重点分析entryWithPriority的逻辑
以上代码主要分为2部分,第一部分为获取ProcessorSlot链如下图
ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);
第二部分执行ProcessorSlot的entry,如下图代码
chain.entry(context, resourceWrapper, null, count, prioritized, args);
我们今天主要分析第一部分,ProcessorSlot的初始化
3.1 ProcessorSlot组装与初始化
以下代码的主要逻辑首先从chainMap中获取资源,如果没有在进行初始化,初始化会调用SlotChainProvider.newSlotChain();
public class CtSph implements Sph {
private static volatile Map<ResourceWrapper, ProcessorSlotChain> chainMap
= new HashMap<ResourceWrapper, ProcessorSlotChain>();
ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
ProcessorSlotChain chain = chainMap.get(resourceWrapper);
if (chain == null) {
synchronized (LOCK) {
chain = chainMap.get(resourceWrapper);
if (chain == null) {
// Entry size limit.
if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
return null;
}
chain = SlotChainProvider.newSlotChain();
Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
chainMap.size() + 1);
newMap.putAll(chainMap);
newMap.put(resourceWrapper, chain);
chainMap = newMap;
}
}
}
return chain;
}
}
3.1.1 SlotChainProvider.newSlotChain()分析
newSlotChain()方法主要逻辑分为2部分
- 第一部分 从META-INFA.services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder文件 获取SlotChainBuilder的spi实现
- 第二部分 执行SlotChainBuilder.build方法,组装ProcessorSlotChain
public final class SlotChainProvider {
private static volatile SlotChainBuilder slotChainBuilder = null;
public static ProcessorSlotChain newSlotChain() {
if (slotChainBuilder != null) {
return slotChainBuilder.build();
}
//第一步 3.1.2 获取SlotChainBuilder的spi实现
slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();
if (slotChainBuilder == null) {
//日志打印
slotChainBuilder = new DefaultSlotChainBuilder();
} else {
//日志打印
}
//第二步 3.1.3 执行build方法,组装ProcessorSlotChain
return slotChainBuilder.build();
}
private SlotChainProvider() {}
}
3.1.2 SlotChainBuilder 构建分析
以下代码就是获取SlotChainBuilder的spi的具体实现逻辑,先分析一下SpiLoader.of的逻辑
SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();
3.1.2.1 SpiLoader of的分析
of方法主要把传进去class,放入到SPI_LOADER_MAP的中,SPI_LOADER_MAP的key为class名称,value为SpiLoader实例,而在创建SpiLoader实例时传递class作为构造参数,并赋值给了service属性。这个service会在执行loadFirstInstanceOrDefault方法时候用到。
public final class SpiLoader<S> {
// Cache the SpiLoader instances, key: classname of Service, value: SpiLoader instance
private static final ConcurrentHashMap<String, SpiLoader> SPI_LOADER_MAP = new ConcurrentHashMap<>();
public static <T> SpiLoader<T> of(Class<T> service) {
...
//获取类名称
String className = service.getName();
SpiLoader<T> spiLoader = SPI_LOADER_MAP.get(className);
if (spiLoader == null) {
synchronized (SpiLoader.class) {
spiLoader = SPI_LOADER_MAP.get(className);
if (spiLoader == null) {
SPI_LOADER_MAP.putIfAbsent(className, new SpiLoader<>(service));
spiLoader = SPI_LOADER_MAP.get(className);
}
}
}
return spiLoader;
}
}
private Class<S> service;
private SpiLoader(Class<S> service) {
this.service = service;
}
3.1.2.2 SpiLoader loadFirstInstanceOrDefault 分析
loadFirstInstanceOrDefault 方法会根据of方法传递类获取对应实现并返回,这里分为3步,我们先看第一步load方法
public S loadFirstInstanceOrDefault() {
//第一步
load();
//第二步
for (Class<? extends S> clazz : classList) {
if (defaultClass == null || clazz != defaultClass) {
return createInstance(clazz);
}
}
//第三步
return loadDefaultInstance();
}
3.1.2.2.1 第一步: SpiLoader.load分析
这里service在3.1.2.1 步执行Of方法进行了设置为SlotChainBuilder类,load方法总结下就是从META-INFA.services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder文件中获取对应内容,找到SlotChainBuilder对应的实现类,并把对应的实现类保存在classList、sortedClassList、classMap对象中。但是这里的load的方法不止加载SlotChainBuilder文件,也用来加载其他资源。
public final class SpiLoader<S> {
//在of方法已经进行了设置这里为SlotChainBuilder类
private Class<S> service;
private static final String SPI_FILE_PREFIX = "META-INF/services/";
private final List<Class<? extends S>> classList = Collections.synchronizedList(new ArrayList<Class<? extends S>>());
// Cache the sorted classes of Provider
private final List<Class<? extends S>> sortedClassList = Collections.synchronizedList(new ArrayList<Class<? extends S>>());
private final ConcurrentHashMap<String, Class<? extends S>> classMap = new ConcurrentHashMap<>();
public void load() {
//1 如果已经执行则返回
if (!loaded.compareAndSet(false, true)) {
return;
}
//2 fullFileName= META-INFA.services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder
String fullFileName = SPI_FILE_PREFIX + service.getName();
//3 就是获取classloader
ClassLoader classLoader;
if (SentinelConfig.shouldUseContextClassloader()) {
classLoader = Thread.currentThread().getContextClassLoader();
} else {
classLoader = service.getClassLoader();
}
if (classLoader == null) {
classLoader = ClassLoader.getSystemClassLoader();
}
Enumeration<URL> urls = null;
try {
//4 根据全路径获取具体的资源
urls = classLoader.getResources(fullFileName);
} catch (IOException e) {
...
}
...
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
InputStream in = null;
BufferedReader br = null;
try {
in = url.openStream();
br = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8));
String line;
while ((line = br.readLine()) != null) {
...
line = line.trim();
Class<S> clazz = null;
try {
//5 根据文件内容转换为具体类
clazz = (Class<S>) Class.forName(line, false, classLoader);
} catch (ClassNotFoundException e) {
fail("class " + line + " not found", e);
}
//6 判断clazz是不是SlotChainBuilder的子类
if (!service.isAssignableFrom(clazz)) {
fail("class " + clazz.getName() + "is not subtype of " + service.getName() + ",SPI configuration file=" + fullFileName);
}
classList.add(clazz);
//7 获取子类的Spi的注解,并获取的spi的value值,如果为空返回类名称作为classMap的key
Spi spi = clazz.getAnnotation(Spi.class);
String aliasName = spi == null || "".equals(spi.value()) ? clazz.getName() : spi.value();
...
classMap.put(aliasName, clazz);
//8 如果spi注解标识为默认,那么defaultClass=当前子类
if (spi != null && spi.isDefault()) {
if (defaultClass != null) {
fail("Found more than one default Provider, SPI configuration file=" + fullFileName);
}
defaultClass = clazz;
}
}
} catch (IOException e) {
fail("error reading SPI configuration file", e);
} finally {
closeResources(in, br);
}
}
//9 把当前添加的classList的存入到sortedClassList中,并根据spi的注解的order值进行降序排序,这里因为SlotChainBuilder的子类只有一个所以这里,对他来说意义不大,但是后续ProcessorSlot加载会用到
sortedClassList.addAll(classList);
Collections.sort(sortedClassList, new Comparator<Class<? extends S>>() {
@Override
public int compare(Class<? extends S> o1, Class<? extends S> o2) {
Spi spi1 = o1.getAnnotation(Spi.class);
int order1 = spi1 == null ? 0 : spi1.order();
Spi spi2 = o2.getAnnotation(Spi.class);
int order2 = spi2 == null ? 0 : spi2.order();
return Integer.compare(order1, order2);
}
});
}
}
讲了那么多com.alibaba.csp.sentinel.slotchain.SlotChainBuilder文件,那么他的内容是什么
DefaultSlotChainBuilder的为SlotChainBuilder具体实现类,并且有Spi注解标识,并且是默认类
3.1.2.2.2 第二步:createInstance(clazz)分析
在3.1.2.2.1 分析中classList不为空,且值为DefaultSlotChainBuilder类,并且为defaultClass,所以这里逻辑不会走第二步,直接看第三步。
public final class SpiLoader<S> {
public S loadFirstInstanceOrDefault() {
//第一步
load();
//第二步
for (Class<? extends S> clazz : classList) {
if (defaultClass == null || clazz != defaultClass) {
return createInstance(clazz);
}
}
//第三步
return loadDefaultInstance();
}
}
3.1.2.2.3 第三步:SpiLoader loadFirstInstanceOrDefault分析
loadDefaultInstance()方法,主要逻辑就是创建DefaultSlotChainBuilder实例并返回。
public final class SpiLoader<S> {
public S loadFirstInstanceOrDefault() {
//第一步
load();
//第二步
for (Class<? extends S> clazz : classList) {
if (defaultClass == null || clazz != defaultClass) {
return createInstance(clazz);
}
}
//第三步
return loadDefaultInstance();
}
}
在**loadDefaultInstance()**逻辑中,又调用了load方法,这里load方法会直接返回,因为此方法已经在上面已经执行了,所以在执行到此代码快!loaded.compareAndSet(false, true)
会返回false,具体请查看load方法。
疑问:看了一下loadDefaultInstance的引用,只有loadFirstInstanceOrDefault方法调用,这里可以不用调用load方法,不止为啥又要调用?懂得人可以解答一下,继续分析。
由于loadDefaultInstance()方法中的defaultClass不为空,这里为DefaultSlotChainBuilder类,作为参数传递给createInstance方法,下面看看createInstance方法具体实现
public final class SpiLoader<S> {
public S loadDefaultInstance() {
//有执行调用load方法。
load();
if (defaultClass == null) {
return null;
}
return createInstance(defaultClass);
}
//这里会直接返回,因为loaded的值已经在第一次执行设置为true。
public void load() {
if (!loaded.compareAndSet(false, true)) {
return;
}
...
}
}
createInstance方法的参数class为DefaultSlotChainBuilder,DefaultSlotChainBuilder类上面是有@Spi(isDefault = true)
注解,其次spi属性singleton默认为true,所以singleton就等于true,这里又调用了createInstance重载方法。
public final class SpiLoader<S> {
private S createInstance(Class<? extends S> clazz) {
Spi spi = clazz.getAnnotation(Spi.class);
boolean singleton = true;
if (spi != null) {
singleton = spi.isSingleton();
}
return createInstance(clazz, singleton);
}
}
/**
* DefaultSlotChainBuilder类源码
**/
@Spi(isDefault = true)
public class DefaultSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
for (ProcessorSlot slot : sortedSlotList) {
if (!(slot instanceof AbstractLinkedProcessorSlot)) {
RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain");
continue;
}
chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
}
return chain;
}
}
createInstance(Class<? extends S> clazz, boolean singleton) 逻辑可以看到已class的名称作为key,创建class实例,存储在singletonMap中,并返回,比较简单。
public final class SpiLoader<S> {
private final ConcurrentHashMap<String, S> singletonMap = new ConcurrentHashMap<>();
private S createInstance(Class<? extends S> clazz, boolean singleton) {
S instance = null;
try {
if (singleton) {
instance = singletonMap.get(clazz.getName());
if (instance == null) {
synchronized (this) {
instance = singletonMap.get(clazz.getName());
if (instance == null) {
instance = service.cast(clazz.newInstance());
singletonMap.put(clazz.getName(), instance);
}
}
}
} else {
instance = service.cast(clazz.newInstance());
}
} catch (Throwable e) {
fail(clazz.getName() + " could not be instantiated");
}
return instance;
}
}
3.1.2.3 SlotChainBuilder构建总结
从模块中 META-INFA.services的文件夹下查找com.alibaba.csp.sentinel.slotchain.SlotChainBuilder文件,并读取对应的内容为com.alibaba.csp.sentinel.slots.DefaultSlotChainBuilder,并构建DefaultSlotChainBuilder实例返回。
SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();
3.1.3 组装ProcessorSlotChain
以下代码,在第一步已经构建了SlotChainBuilder实例为DefaultSlotChainBuilder,具体清查看build方法。
public final class SlotChainProvider {
private static volatile SlotChainBuilder slotChainBuilder = null;
public static ProcessorSlotChain newSlotChain() {
if (slotChainBuilder != null) {
return slotChainBuilder.build();
}
//第一步 3.1.2 获取SlotChainBuilder的spi实现
slotChainBuilder = SpiLoader.of(SlotChainBuilder.class).loadFirstInstanceOrDefault();
if (slotChainBuilder == null) {
//日志打印
slotChainBuilder = new DefaultSlotChainBuilder();
} else {
//日志打印
}
//第二步 3.1.3 执行build方法,组装ProcessorSlotChain
return slotChainBuilder.build();
}
private SlotChainProvider() {}
}
3.1.3.1ProcessorSlotChain build分析
以下代码,build方法中 SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted()
的of方法已经在上面分析过来,具体请看3.1.2.1节,这里具体分析第一步的loadInstanceListSorted方法逻辑
@Spi(isDefault = true)
public class DefaultSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
//第一步:获取ProcessorSlot集合
List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
for (ProcessorSlot slot : sortedSlotList) {
if (!(slot instanceof AbstractLinkedProcessorSlot)) {
//打印日志
continue;
}
//第二步: 组装ProcessorSlotChain链
chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
}
return chain;
}
}
3.1.3.1.1 ProcessorSlot实现类获取分析
以下代码,loadInstanceListSorted中第一步load方法,逻辑是解析META-INF/services/com.alibaba.csp.sentinel.slotchain.ProcessorSlot文件内容并进行升序排序后然后在创建对应的实例保存在sortedClassList集合中,具体请查看3.1.2.2.1节。
public final class SpiLoader<S> {
private final List<Class<? extends S>> sortedClassList = Collections.synchronizedList(new ArrayList<Class<? extends S>>());
public List<S> loadInstanceListSorted() {
//第一步
load();
//第二步
return createInstanceList(sortedClassList);
}
}
ProcessorSlot文件内容
3.1.3.1.1.1 创建ProcessorSlot实例
以下代码中1、2、3步,构建ProcessorSlot实例的具体逻辑,clazzList为ProcessorSlot实现类集合。
private List<S> createInstanceList(List<Class<? extends S>> clazzList) {
if (clazzList == null || clazzList.size() == 0) {
return Collections.emptyList();
}
List<S> instances = new ArrayList<>(clazzList.size());
for (Class<? extends S> clazz : clazzList) {
//第1步
S instance = createInstance(clazz);
instances.add(instance);
}
return instances;
}
//第2步
private S createInstance(Class<? extends S> clazz) {
Spi spi = clazz.getAnnotation(Spi.class);
boolean singleton = true;
if (spi != null) {
singleton = spi.isSingleton();
}
return createInstance(clazz, singleton);
}
//第3步
private S createInstance(Class<? extends S> clazz, boolean singleton) {
S instance = null;
try {
if (singleton) {
instance = singletonMap.get(clazz.getName());
if (instance == null) {
synchronized (this) {
instance = singletonMap.get(clazz.getName());
if (instance == null) {
instance = service.cast(clazz.newInstance());
singletonMap.put(clazz.getName(), instance);
}
}
}
} else {
instance = service.cast(clazz.newInstance());
}
} catch (Throwable e) {
fail(clazz.getName() + " could not be instantiated");
}
return instance;
}
}
3.1.3.1.2 组装ProcessorSlotChain总结
至此第一步已经分析完成,具体返回结果请看调试截图,与内容一致,根据order属性进行升序排序。
@Spi(isDefault = true)
public class DefaultSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
//第一步:获取ProcessorSlot集合
List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
for (ProcessorSlot slot : sortedSlotList) {
if (!(slot instanceof AbstractLinkedProcessorSlot)) {
//打印日志
continue;
}
//第二步: 组装ProcessorSlotChain链
chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
}
return chain;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vtdq6Xgc-1628423013811)(/Users/dongweizhao/Library/Application Support/typora-user-images/image-20210808183618701.png)]
3.1.3.2 构建 ProcessorSlot链
第一步已经执行完成,第二步就是构建ProcessorSlotChain链,ProcessorSlotChain链为责任链模式,先分析下ProcessorSlotChain逻辑
@Spi(isDefault = true)
public class DefaultSlotChainBuilder implements SlotChainBuilder {
@Override
public ProcessorSlotChain build() {
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
//第一步:获取ProcessorSlot集合
List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
for (ProcessorSlot slot : sortedSlotList) {
if (!(slot instanceof AbstractLinkedProcessorSlot)) {
//打印日志
continue;
}
//第二步: 组装ProcessorSlotChain链
chain.addLast((AbstractLinkedProcessorSlot<?>) slot);
}
return chain;
}
}
通过对以上代码分析,这里调用了addLast方法,可以看到根据每次设置进去的值,都作为上次end节点next节点。而end和first节点默认为内部类实现,这样就构成一个执行链,默认值顺序为
-> 内部类first
->com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot
->com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot
->com.alibaba.csp.sentinel.slots.logger.LogSlot
->com.alibaba.csp.sentinel.slots.statistic.StatisticSlot
->com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot
->com.alibaba.csp.sentinel.slots.system.SystemSlot
->com.alibaba.csp.sentinel.slots.block.flow.FlowSlot
->com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot
public class DefaultProcessorSlotChain extends ProcessorSlotChain {
AbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, Object t, int count, boolean prioritized, Object... args)
throws Throwable {
super.fireEntry(context, resourceWrapper, t, count, prioritized, args);
}
@Override
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
super.fireExit(context, resourceWrapper, count, args);
}
};
AbstractLinkedProcessorSlot<?> end = first;
//第一步
@Override
public void addLast(AbstractLinkedProcessorSlot<?> protocolProcessor) {
//第二步
end.setNext(protocolProcessor);
//第三步
end = protocolProcessor;
}
...
}
/**
* @author qinan.qn
* @author jialiang.linjl
*/
public abstract class AbstractLinkedProcessorSlot<T> implements ProcessorSlot<T> {
private AbstractLinkedProcessorSlot<?> next = null;
...
public AbstractLinkedProcessorSlot<?> getNext() {
return next;
}
//第二步
public void setNext(AbstractLinkedProcessorSlot<?> next) {
this.next = next;
}
}
3.2 总结
在看下下面的截图,当每次限流调用entryWithPriority方法时,首先会调用lookProcessChain方法获取slot执行链,而lookProcessChain的执行逻辑有分为2种场景。
1.如果是系统的第一次调用不是当前资源的第一次调用
lookProcessChain方法会调用**SlotChainProvider.newSlotChain()**方法进行slot的初始化。
2 .如果是资源的第一次调用非系统第一次调用
会从chainMap中已资源对象作为key,从中获取slot对象,如果没有,则调用**SlotChainProvider.newSlotChain()方法进行返回,而SlotChainProvider.newSlotChain()**方法会从缓存中返回 ProcessorSlotChain对象。
public class CtSph implements Sph {
private static volatile Map<ResourceWrapper, ProcessorSlotChain> chainMap
= new HashMap<ResourceWrapper, ProcessorSlotChain>();
ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
ProcessorSlotChain chain = chainMap.get(resourceWrapper);
if (chain == null) {
synchronized (LOCK) {
chain = chainMap.get(resourceWrapper);
if (chain == null) {
// Entry size limit.
if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
return null;
}
chain = SlotChainProvider.newSlotChain();
Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap<ResourceWrapper, ProcessorSlotChain>(
chainMap.size() + 1);
newMap.putAll(chainMap);
newMap.put(resourceWrapper, chain);
chainMap = newMap;
}
}
}
return chain;
}
}