首页 > 其他分享 >sentinel中的扩展

sentinel中的扩展

时间:2024-07-31 21:58:20浏览次数:15  
标签:触发 自定义 初始化 扩展 接口 sentinel class

为什么优秀的框架总是提供用户扩展的能力?

我想这是因为业务逻辑是千变万化的,框架中能抽象的只是部分的公共逻辑,在应用接入框架时,难免需要做一些本地化调整。框架一般通过接口(interface)的形式来提供扩展(用户通过实现这些接口来获得某些能力或完成某些逻辑),那么框架如何知道这些接口实现呢?

一般就两种方式:

  • 用户告诉框架接口实现:通过框架提供的 SDK 注册对应的接口实现
  • 框架自己来找接口实现:框架按照约定(SPI、包扫描等)方式来发现对应的接口实现

什么意思呢,我们以大名鼎鼎的 Spring 来举例:

// 通过注解 + 包扫描的方式来查找 WebMvcConfigurer 接口实现
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    private void addSpringMvcInterceptor(InterceptorRegistry registry) {
        SentinelWebMvcConfig config = new SentinelWebMvcConfig();
        // 通过 registry.addInterceptor 注册 Interceptor 接口实现
        registry.addInterceptor(new SentinelWebInterceptor(config)).addPathPatterns("/**");
    }

}

那么 Sentinel 中的扩展中有哪些呢?其又是如何感知到这些扩展实现呢?

sentinel spi

Java 中提供了 SPI(Service Provider Interface)机制来扩展、增强我们的应用。Sentinel 中定义了自己的 SPI 实现,用于加载对应接口实现。对应的 sentinel spi 实现代码:

class SpiLoader {
    public void load() {
        // 判断是否已经加载过
        if (!loaded.compareAndSet(false, true)) {
            return;
        }

        // 读取的文件全路径(包含类的全路径名称)
        // 如:/META-INF.services/com.alibaba.csp.sentinel.init.initFunc
        String fullFileName = SPI_FILE_PREFIX + service.getName();
        // 判断使用哪种 classLoader
        ClassLoader classLoader;
        // ...
        
        // 从当前 classPath 读取 jar 包中的 spi 文件
        Enumeration<URL> urls = null;
        try {
            urls = classLoader.getResources(fullFileName);
        } catch (IOException e) {
            fail("Error locating SPI configuration file,filename=" + fullFileName + ",classloader=" + classLoader, e);
        }
        // ...

        // 解析 spi 文件中的内容,并加载为 class
        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) {
                    // 忽略空行,忽略以 # 开始的注释 容
                    // ...

                    // 根据文本创建对应的 class
                    Class<S> clazz = null;
                    try {
                        clazz = (Class<S>) Class.forName(line, false, classLoader);
                    } catch (ClassNotFoundException e) {
                        fail("class " + line + " not found", e);
                    }

                    // 判断是否重复加载,class 是否为 service 类型子类
                    // ...
                    
                    // 读取 @Spi 注解属性
                    classList.add(clazz);
                    Spi spi = clazz.getAnnotation(Spi.class);
                    String aliasName = spi == null || "".equals(spi.value()) ? clazz.getName() : spi.value();
                    // ...
                }
            } catch (IOException e) {
                fail("error reading SPI configuration file[" + url + "]", e);
            } finally {
                closeResources(in, br);
            }
        }

        sortedClassList.addAll(classList);
        // 根据 class 中定义的 @Spi 注解 order 属性进行排序
        Collections.sort(sortedClassList, new Comparator<Class<? extends S>>() {
            @Override
            public int compare(Class<? extends S> o1, Class<? extends S> o2) {
                // ...
            }
        });
    }
}

核心的扩展接口如下:

接口接口描述创建时机运行时机
InitFunc用于实现自定义的初始化逻辑sentinel 初始化时触发(sentinel core 模块在进入资源时会通过 InitExecutor.doInit 触发初始化动作)仅初始化触发一次
MetricExtension用于实现sentinel内部统计的自定义逻辑扩展sentinel 初始化时触发进入资源时触发
SlotChainBuilder用于创建自定义的slot chain为资源创建 slot chain 实例时触发仅初始化创建一次
ProcessorSlot用于实现自定义的资源进入、退出逻辑在为资源创建 slot chain 实例时触发进入、退出资源时触发
CommandCenter用于commandCenter开始前、开始、结束阶段的自定义逻辑扩展在transport模块初始化时触发仅初始化调用一次
CommandHandler用于实现自定义的请求(commend request)处理在transport模块初始化时触发发生请求(CommandRequest)时触发
CommandHandlerInterceptor用于实现自定义的请求(commend request)拦截在transport模块初始化时触发发生请求(CommandRequest)时触发
HeartbeatSender用于实现自定义的心跳发送器sentinel 初始化时触发定时触发

接口注册

上述代码中, sentinel 实现了一套自己的 SPI 机制来加载依赖的接口,以保证框架正常的运行。

另外 sentinel 还提供了一些接口方便用户在特定时机去自定义自己的逻辑,我们可以通过接口注册的方式告知 sentinel 我们的接口实现。
核心的扩展接口信息如下:

接口接口描述运行时机
ReadableDataSource用于读取外部数据源获取最新的配置1. 在定义时初始化拉取一次
2.后续更新时由外部数据源推送最新规则或定时从外部数据源拉取最新规则
WritableDataSource用于将最新配置写入外部数据源在1.8.7版本中以下动作会触发
- 网关api组更新
- 网关规则更新
- 热点规则更新时
ProcessorSlotEntryCallback进入资源时回调方法在 StatisticSlot 进入资源后触发
ProcessorSlotExitCallback退出资源时回调方法在 StatisticSlot 退出资源后触发

使用方法很简单,一般通过对应 Registry 类的静态方法进行注册,我们以 ProcessorSlotEntryCallback 接口为例演示一下基础使用方法:

class Demo {
    public static void main(String[] args) {
        StatisticSlotCallbackRegistry.addEntryCallback("entryCb1", new ProcessorSlotEntryCallback<DefaultNode>() {
            @Override
            public void onPass(Context context, ResourceWrapper resourceWrapper, DefaultNode param, int count, Object... args) throws Exception {
                System.out.println("entry resource success : " + resourceWrapper.getName());
            }
            @Override
            public void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper, DefaultNode param, int count, Object... args) {
                System.out.println("entry resource blocked : " + resourceWrapper.getName());
            }
        });
    }
}

总结

在本章我们了解到在 sentinel 中注册扩展主要有两种方式:

  1. 实现对应接口,在类上使用 @Spi 注解从而被 sentinel 扫描并注册
  2. 实现对应接口,通过对应的 API 方法手动注册到 sentinel 中

文章编写不易,如有帮助,欢迎 star:github 地址gitee 地址

标签:触发,自定义,初始化,扩展,接口,sentinel,class
From: https://blog.csdn.net/qq_31996357/article/details/140812561

相关文章

  • 探索WebKit的媒体源扩展:Web应用的多媒体革新
    探索WebKit的媒体源扩展:Web应用的多媒体革新在当今的Web应用开发中,多媒体内容的流畅播放是用户体验的关键组成部分。WebKit,作为Safari浏览器的心脏,提供了一套强大的API,即媒体源扩展(MediaSourceExtensions,MSE),它允许开发者在Web页面上动态地创建、修改和控制媒体内容的播放......
  • 如何设计可扩展的RPC协议
    如何设计可扩展且向后兼容的RPC协议为什么不直接用现成的HTTP协议?RPC的职责是负责应用间的通信,所以性能要求相对更高。而HTTP协议的数据包相对于请求数据本身要大很多,有很多冗余内容(如换行回车等),会影响性能。另外HTTP协议是无状态的协议,每次请求都要重新建立连接。因此对于要......
  • 扩展 BSGS 学习笔记
    在之前,我们学习了BSGS。设有\(a,b,m\),且\((a,m)\ne1\),求解:\[a^x\equivb\pmodm\]这玩意如何求解?把它变成BSGS能做的形式不就行了!具体的,设\(d_1=(a,m)\),若\(d_1\not|b\)则方程无解。否则我们可以得到:\[\dfrac{a}{d_1}\cdota^{x-1}\equiv\dfrac{b}{d_1}\pmod{\d......
  • 类型提示和@singledispatch:如何以可扩展的方式包含 `Union[...]`?
    我正在重构一个函数,该函数将各种日期格式(即ISO8601字符串、datetime.date、datetime.datetime等)转换为Unix时间戳。我希望新函数使用@singledispatch而不是类型检查,但我不知道如何保留以前函数的类型提示:旧函数:使用类型检查importdat......
  • Sentinel - 滑动窗口算法分析
    文章目录前言一、StatisticSlot1、StatisticNode2、Metric3、ArrayMetric4、LeapArray5、WindowWrap6、MetricBucket二、FlowSlot1、FlowRuleChecker2、DefaultController总结前言前序文章介绍了常见的限流算法,包括滑动窗口,并且简单地进行了实现。对于更复杂的场......
  • 网络安全靶场系列(臃肿版):PbootCMS(V3.2.5)遇到的坑(您的服务器环境不支持gd扩展,将无法使
    PbootCMS(V3.2.5)遇到的坑(您的服务器环境不支持gd扩展,将无法使用验证码!)前言dockerphpgd扩展,docker安装php扩展gd库一、安装nginx、php、mysql镜像二、运行、停止、删除容器三、安装php扩展gd库四、扩展知识1、docker-php-source2、docker-php-ext-enable......
  • Ansible 学习与扩展整理
    一、Ansible基础知识回顾核心组件主机清单(HostInventory):定义了Ansible可以管理的目标机器列表。模块(Modules):Ansible执行特定任务的最小单位,类似于命令行工具或脚本。插件(Plugins):扩展Ansible功能,如连接插件、回调插件等。Playbook:YAML格式的文件,定义了Ansi......
  • k8s命令行工具扩展
    工具kubectxwhat?切换context、namespace$kubectxminikubeSwitchedtocontext"minikube".$kubenskube-systemContext"test"set.Activenamespaceis"kube-system".fzfwhat?fzf:fuzzyfinder,一个通用的命令行模糊搜索工具主要用来列举env、cont......
  • Intel CPU处理器可扩展平台
    一、Whitley平台第三代Intel可扩展处理器平台Whitley采用IceLake-SP(IceLakeSP:概述和技术文档(intel.cn)),面向2P服务器(2路服务器)。该架构支持每插槽64个PCIeGen4通道,可实现更高的每核I/O带宽,并在8个通道、2DPC(每通道2个DIMM)配置下支持频率高达3200MT/秒的DDR4内存。搭载C......
  • VSCode 的 Python 扩展中更详细的属性提示
    假设我有一个对象args由parser.parse_args()返回,并且它应该具有像args.port=6001、args.seed=1234这样的属性。当我在VSCode中按args.时,port和seed不会显示在建议的属性列表中,因为这些属性可能会......