Sentinel资源限流原理

一 资源定义
资源在sentinel中,可以是任何东西,服务,服务里的方法,甚至是一段代码。均可以使用Sentinel 来进行资源保护
1 埋点方式

 try {
            // entry可以理解成入口登记
            entry = SphU.entry(KEY);
            // 被保护的逻辑, 这里为订单查询接口
            System.out.println("处理业务中------------");
        } catch (BlockException blockException) {
            // 接口被限流的时候, 会进入到这里
            return "接口限流, 请稍后重试";
        } finally {
            // SphU.entry(xxx) 需要与 entry.exit() 成对出现,否则会导致调用链记录异常
            if (entry != null) {
                entry.exit();
            }
        }
        

  

2 注解方式

@SentinelResource(value = "hello", entryType = EntryType.OUT, blockHandlerClass = SentinelExceptionHandler.class,
            blockHandler = "helloExceptionHandle")

@SentinelResource注解源码解析

try {
    entry = SphU.entry(resourceName, resourceType, entryType, pjp.getArgs());
    Object result = pjp.proceed();
    return result;
} catch (BlockException ex) {
    return handleBlockException(pjp, annotation, ex);
} catch (Throwable ex) {
    Class<? extends Throwable>[] exceptionsToIgnore = annotation.exceptionsToIgnore();
    // The ignore list will be checked first.
    if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) {
        throw ex;
    }
    if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) {
        traceException(ex);
        return handleFallback(pjp, annotation, ex);
    }

    // No fallback function can handle the exception, so throw it out.
    throw ex;
} finally {
    if (entry != null) {
        entry.exit(1, pjp.getArgs());
    }
}

通过源码可以知道注解其实也是通过埋点的方式来进行资源限制的
规则限制关键代码
entry = SphU.entry(Resources);
entry.exit();
二 限流规则的流程分析
在这里插入图片描述

从流程途中我们可以提取出关键几点
1 资源包装
new StringResourceWrapper(name, type)给资源增加节点属性,资源类型
2 获取当前线程上下文内容
Context context = ContextUtil.getContext();

那么线程上下文中都存放了那些内容呢?
关键属性

/**
 * 存储调用链路所在线程的Context
 * Store the context in ThreadLocal for easy access.
 */
private static ThreadLocal<Context> contextHolder = new ThreadLocal<>();

/**
 * 
 * Holds all {@link EntranceNode}. Each {@link EntranceNode} is associated with a distinct context name.
 */
private static volatile Map<String, DefaultNode> contextNameNodeMap = new HashMap<>();
context = new Context(node, name);
context.setOrigin(origin);
contextHolder.set(context);

上下文中存储内容 name指的是资源名称,默认是sentinel_default_context

public final static String CONTEXT_DEFAULT_NAME = "sentinel_default_context";

node指的是由当前资源包装类创建的入口节点
```java
node = new EntranceNode(new StringResourceWrapper(name, EntryType.IN), null);

origin指的是当前应用的application 名称。默认为空

context = InternalContextUtil.internalEnter(Constants.CONTEXT_DEFAULT_NAME);
private final static class InternalContextUtil extends ContextUtil {
    static Context internalEnter(String name) {
        return trueEnter(name, "");
    }

    static Context internalEnter(String name, String origin) {
        return trueEnter(name, origin);
    }
}
context = new Context(node, name);
context.setOrigin(origin);
contextHolder.set(context);

contextNameNodeMap存储的是默认资源名为KEY,入口节点为VALUE的值

contextNameNodeMap.put(defaultContextName, node);

ContextUtil类加载时调用initDefaultContext方法初始化创建EntranceNode入口节点,默认上下文名称为CONTEXT_DEFAULT_NAME=“sentinel_default_context”
然后将EntranceNode加到 Constants.ROOT(ClusterNode)后边,然后以CONTEXT_DEFAULT_NAME为key,EntranceNode为值放入到 Map<String, DefaultNode> contextNameNodeMap = new HashMap<>()

ContextUtil初始化方法

private static void initDefaultContext() {
        String defaultContextName = Constants.CONTEXT_DEFAULT_NAME;
        //初始化一个sentinel_default_context,type为in的队形
        EntranceNode node = new EntranceNode(new StringResourceWrapper(defaultContextName, EntryType.IN), null);
        //Constants.ROOT会初始化一个name是machine-root,type=IN的对象
        Constants.ROOT.addChild(node);
        //所以现在map里面有一个key=CONTEXT_DEFAULT_NAME的对象
        contextNameNodeMap.put(defaultContextName, node);
    }

如果context为NullContext或者全局限制没有开启(Constants.ON=false)则对资源不进行规则限制
如果context=null则调用trueEnter方法(作用与initDefaultContext方法一致)当前线程context数量>2000对资源不进行规则限制
三 获取/创建一系列功能插槽
对资源进行一系列判断之后通过lookProcessChain(resourceWrapper)获取/创建功能插槽(规则调用链)
如果全局规则调用链结点数大于6000不再创建功能插槽不对资源进行限制
1 根据资源获取已存在调用链

ProcessorSlot<Object> chain = lookProcessChain(resourceWrapper);

ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) {
    //根据resourceWrapper初始化插槽(相对应的规则slot)
    ProcessorSlotChain chain = chainMap.get(resourceWrapper);
    if (chain == null) {
        synchronized (LOCK) {
            chain = chainMap.get(resourceWrapper);
            // 双重检查机制
            if (chain == null) {
                // Entry size limit.6000
                if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) {
                    return null;
                }
                // 构造SlotChain
                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;
}

2 构建规则调用链

DefaultSlotChainBuilder.build()

@Override
public ProcessorSlotChain build() {
    ProcessorSlotChain chain = new DefaultProcessorSlotChain();
    //收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级
    chain.addLast(new NodeSelectorSlot());
    //用于存储资源的统计信息以及调用者信息
    chain.addLast(new ClusterBuilderSlot());
    chain.addLast(new LogSlot());
    //用于记录、统计不同纬度的 runtime 指标监控信息
    chain.addLast(new StatisticSlot());
    //根据配置的黑白名单和调用来源信息,来做黑白名单控制
    chain.addLast(new AuthoritySlot());
    //通过系统的状态,例如 load1 等,来控制总的入口流量
    chain.addLast(new SystemSlot());
    //用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制
    chain.addLast(new FlowSlot());
    //通过统计信息以及预设的规则,来做熔断降级
    chain.addLast(new DegradeSlot());

    return chain;
}

四 sentinel限流流程总结
1 资源被entry节点包围,资源包装StringResourceWrapper
2 初始化当前线程上下文root节点以及入口节点(获取当前线程上下文内容)
3 查找/构建当前资源存在的资源调用链
4 链式调用节点的entry方法,统计数据/判断规则是否生效
调用顺序为:
NodeSelectorSlot-ClusterBuilderSlot-StatisticSlot-SystemSlot-AuthoritySlot-FlowSlot-DegradeSlot

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值