问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

Sentinel限流和熔断机制深度解析

创作时间:
作者:
@小白创作中心

Sentinel限流和熔断机制深度解析

引用
CSDN
1.
https://blog.csdn.net/Deligent_lvan/article/details/146267666

Sentinel是阿里巴巴开源的流量控制组件,主要用于微服务架构中的系统稳定性保障。本文将深入剖析Sentinel的核心功能——限流和熔断的实现原理,帮助读者理解其工作机制。

前言

在微服务架构中,一个请求往往需要经过多个服务的调用链路。为了保护每个服务不被过载,需要采取限流和熔断等措施。本文将通过源码分析,详细介绍Sentinel是如何实现这些功能的。

雪崩问题

雪崩问题通常发生在服务调用链路中,当某个下游服务发生故障时,会导致整个链路的性能下降甚至崩溃。例如:

a1服务——》b服务——》c服务
a2服务——》b服务——》c服务
a3服务——》b服务
d服务——》a3服务

如果服务c发生故障,会导致b服务的连接被占用至超时。随后a2服务的请求也会堆积在b服务上,最终可能导致b服务也发生故障。这种故障扩散现象就是雪崩问题。

市面上的解决方案

常见的解决方案包括:

  • 流量控制(限流):提前对服务的QPS进行限制,防止过载。
  • 熔断降级:当检测到下游服务故障时,暂时停止对该服务的调用,避免故障扩散。
  • 舱壁模式:为每个服务调用分配独立的线程池,隔离故障影响。

限流与熔断的概念

  • 限流:通过限制服务的访问频率,防止服务过载。
  • 熔断:当检测到服务故障时,暂时停止对该服务的调用,避免故障扩散。

源码分析

在Sentinel中,每个资源都对应一个资源名称和一个Entry。Entry可以通过注解或API显式创建。每个Entry创建时,会同时创建一系列功能插槽(slot chain)。

例如,服务调用链路如下:

服务a——》服务b——》服务c

每个服务都可以作为一个保护资源,默认情况下,一个Controller对应一个保护资源。在服务内部,可以通过@SentinelResource注解标识需要保护的service层方法。

Sentinel的调用链路主要包括以下8个slot:

每个slot负责不同的功能,例如统计访问QPS、判断是否达到限流或熔断阈值等。如果某个slot抛出BlockException,则后续的slot将不再执行。

SentinelResource的切面

SentinelResource的切面主要通过SphU.entry方法实现:

// 查找slot链
com.alibaba.csp.sentinel.CtSph#entryWithPriority
ProcessorSlot<Object> chain = this.lookProcessChain(resourceWrapper);

// 第一次访问资源时,使用copyOnWrite的方式添加到map中,后面就从map获取即可
ProcessorSlotChain chain = (ProcessorSlotChain)chainMap.get(resourceWrapper);
if (chain == null) {
    synchronized(LOCK) {
        chain = (ProcessorSlotChain)chainMap.get(resourceWrapper);
        if (chain == null) {
            if (chainMap.size() >= 6000) {
                return null;
            }
            chain = SlotChainProvider.newSlotChain();
            Map<ResourceWrapper, ProcessorSlotChain> newMap = new HashMap(chainMap.size() + 1);
            newMap.putAll(chainMap);
            newMap.put(resourceWrapper, chain);
            chainMap = newMap;
        }
    }
}

// 创建的slotChain是通过spi的方式从METAINFO目录获取所有的slot
public ProcessorSlotChain build() {
    ProcessorSlotChain chain = new DefaultProcessorSlotChain();
    List<ProcessorSlot> sortedSlotList = SpiLoader.of(ProcessorSlot.class).loadInstanceListSorted();
    Iterator var3 = sortedSlotList.iterator();
    while(var3.hasNext()) {
        ProcessorSlot slot = (ProcessorSlot)var3.next();
        if (!(slot instanceof AbstractLinkedProcessorSlot)) {
            RecordLog.warn("The ProcessorSlot(" + slot.getClass().getCanonicalName() + ") is not an instance of AbstractLinkedProcessorSlot, can't be added into ProcessorSlotChain", new Object[0]);
        } else {
            chain.addLast((AbstractLinkedProcessorSlot)slot);
        }
    }
    return chain;
}

// 创建完slotChain后,就开始执行每个slot
chain.entry(context, resourceWrapper, (Object)null, count, prioritized, args);
public class DefaultProcessorSlotChain extends ProcessorSlotChain {
    AbstractLinkedProcessorSlot<?> first = new AbstractLinkedProcessorSlot<Object>() {
        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);
        }
        public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
            super.fireExit(context, resourceWrapper, count, args);
        }
    };
}

// 调用下一个slot是通过当前slot的next属性
public void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, boolean prioritized, Object... args) throws Throwable {
    if (this.next != null) {
        this.next.transformEntry(context, resourceWrapper, obj, count, prioritized, args);
    }
}

滑动窗口源码

滑动窗口用于统计访问资源的线程数和QPS。其原理是将一个时间窗口划分为多个时间样本,以解决固定时间窗口的缺陷。

// 统计线程数和QPS
com.alibaba.csp.sentinel.slots.statistic.StatisticSlot#entry
this.fireEntry(context, resourceWrapper, node, count, prioritized, args);
node.increaseThreadNum();
node.addPassRequest(count);

限流源码

限流规则主要在FlowSlot中实现,通过检查静态流量规则来判断是否达到阈值。

com.alibaba.csp.sentinel.slots.block.flow.FlowSlot#entry
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {
    this.checkFlow(resourceWrapper, context, node, count, prioritized);
    this.fireEntry(context, resourceWrapper, node, count, prioritized, args);
}

public void checkFlow(Function<String, Collection<FlowRule>> ruleProvider, ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {
    if (ruleProvider != null && resource != null) {
        Collection<FlowRule> rules = (Collection)ruleProvider.apply(resource.getName());
        if (rules != null) {
            Iterator var8 = rules.iterator();
            while(var8.hasNext()) {
                FlowRule rule = (FlowRule)var8.next();
                if (!this.canPassCheck(rule, context, node, count, prioritized)) {
                    throw new FlowException(rule.getLimitApp(), rule);
                }
            }
        }
    }
}

熔断源码

熔断规则主要在DegradeSlot中实现,通过检查熔断器状态来判断是否触发熔断。

public class DegradeSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
    public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {
        this.performChecking(context, resourceWrapper);
        this.fireEntry(context, resourceWrapper, node, count, prioritized, args);
    }

    void performChecking(Context context, ResourceWrapper r) throws BlockException {
        List<CircuitBreaker> circuitBreakers = DegradeRuleManager.getCircuitBreakers(r.getName());
        if (circuitBreakers != null && !circuitBreakers.isEmpty()) {
            Iterator var4 = circuitBreakers.iterator();
            CircuitBreaker cb;
            do {
                if (!var4.hasNext()) {
                    return;
                }
                cb = (CircuitBreaker)var4.next();
            } while(cb.tryPass(context));
            throw new DegradeException(cb.getRule().getLimitApp(), cb.getRule());
        }
    }
}

总结

Sentinel通过限流和熔断机制,有效保护了微服务架构中的系统稳定性。其核心实现基于责任链模式,通过多个插槽(slot)协同工作,实现流量控制和故障隔离。本文详细介绍了Sentinel的源码实现,希望对读者理解其工作原理有所帮助。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号