Sentinel源码阅读:工作流程

本文深入探讨Sentinel的工作流程,从主流程、Sentinel-dubbo-adapter的使用到核心API的源码解析。Sentinel采用插槽(Slot)机制,利用职责链设计模式实现限流功能,通过SPI拓展。文章详细分析了ContextUtil、SphU、SlotChain的构建以及ProcessorSlot的工作方式,揭示了Sentinel如何通过拦截器模式和SPI实现插件化加载。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.工作主流程

介绍sentinel工作主要流程前我们先思考一个问题:如果是你来实现一个限流框架你会怎么做?

我先讲下我的一些思考,主要从这么几个方面去做:

    • 确定好限流算法,如计数器、漏桶、令牌桶等算法
    • 实现限流需要哪些条件?装配配置的限流接口限流阈值限流策略等,采集调用信息、触发限流规则
    • 框架拓展性考虑引入spi机制
    • 用什么设计模式去实现,方便拓展?

接下来我们带着我们的思考与实现思路,看下Sentinel的主要工作原理,以及他的设计实现。

Sentinel整体架构设计图:

https://note.youdao.com/yws/public/resource/86f71d2ec31c9088533c11cfe86a11ba/xmlnote/B660FCBBBE2641B98EDAB461AC55E927/9374

sentinel 通过一个个插槽来实现限流功能,采用职责链设计模式去组装插槽,每个插槽的功能职责都不一样,Sentinel提供的插槽如下:

  • NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;
  • ClusterBuilderSlot 则用于存储资源的统计信息以及调用者信息,例如该资源的 RT, QPS, thread count 等等,这些信息将用作为多维度限流,降级的依据;
  • StatisticSlot  则用于记录、统计不同纬度的 runtime 指标监控信息;
  • FlowSlot  则用于根据预设的限流规则以及前面 slot 统计的状态,来进行流量控制;
  • AuthoritySlot  则根据配置的黑白名单和调用来源信息,来做黑白名单控制;
  • DegradeSlot  则通过统计信息以及预设的规则,来做熔断降级;
  • SystemSlot  则通过系统的状态,例如 load1 等,来控制总的入口流量;

 

sentinel内部实现了SPI机制,提供了拓展功能,所以我们可以去自定义实现插槽并添加到Sentinel工作主流程中,如下图所示:

https://note.youdao.com/yws/public/resource/86f71d2ec31c9088533c11cfe86a11ba/xmlnote/F916396860B74E338EFE881002C65E30/9393

2.工作流程源码解析

Sentinel-dubbo-adapter适配dubbo做限流降级,它的使用示例代码

 

2.1示例

限流:

public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    // Get origin caller.
    String origin = DubboOriginParserRegistry.getDubboOriginParser().parse(invoker, invocation);
    if (null == origin) {
        origin = "";
    }
    Entry interfaceEntry = null;
    Entry methodEntry = null;
    try {
        String resourceName = getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix());
        String interfaceName = invoker.getInterface().getName();
        ContextUtil.enter(resourceName, origin);
        interfaceEntry = SphU.entry(interfaceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN);
        methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC,
            EntryType.IN, invocation.getArguments());

        Result result = invoker.invoke(invocation);
        if (result.hasException()) {
            Throwable e = result.getException();
            // Record common exception.
            Tracer.traceEntry(e, interfaceEntry);
            Tracer.traceEntry(e, methodEntry);
        }
        return result;
    } catch (BlockException e) {
        return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e);
    } catch (RpcException e) {
        Tracer.traceEntry(e, interfaceEntry);
        Tracer.traceEntry(e, methodEntry);
        throw e;
    } finally {
        if (methodEntry != null) {
            methodEntry.exit(1, invocation.getArguments());
        }
        if (interfaceEntry != null) {
            interfaceEntry.exit();
        }
        ContextUtil.exit();
    }
}

熔断:

@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
    Entry interfaceEntry = null;
    Entry methodEntry = null;
    try {
        String resourceName = getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix());
        interfaceEntry = SphU.entry(invoker.getInterface().getName(), ResourceTypeConstants.COMMON_RPC,
            EntryType.OUT);
        methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT, invocation.getArguments());

        Result result = invoker.invoke(invocation);
        if (result.hasException()) {
            Throwable e = result.getException();
            // Record common exception.
            Tracer.traceEntry(e, interfaceEntry);
            Tracer.traceEntry(e, methodEntry);
        }
        return result;
    } catch (BlockException e) {
        return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e);
    } catch (RpcException e) {
        Tracer.traceEntry(e, interfaceEntry);
        Tracer.traceEntry(e, methodEntry);
        throw e;
    } finally {
        if (methodEntry != null) {
            methodEntry.exit(1, invocation.getArguments());
        }
        if (interfaceEntry != null) {
            interfaceEntry.exit();
        }
    }
}

从以上代码可看出来,Sentinel限流熔断功能的核心API主要有:

  1. ContextUtil.enter(resourceName, origin);
  2. SphU.entry();
  3. interfaceEn
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值