jvm-sandbox-repeater源码解析-配置管理

news2025/11/11 5:59:06

一、配置初见

源码里提供的控制台截图如下:(怎么搭建自己去百度) alt 从中取出对应的配置如下:

{
  "degrade"false,  //阻断能力
  "exceptionThreshold"1000//异常采样率
  "httpEntrancePatterns": [
    "^/app/v1/order/.*$"
  ],
  "javaEntranceBehaviors": [ //主调用配置
    {
      "classPattern""com.test.order.service.pay.PayService",
      "includeSubClasses"false,  //是否对内部类生效
      "methodPatterns": [
        "payCallback"
      ]
    }
  ],
  "javaSubInvokeBehaviors": [ //子调用配置
    {
      "classPattern""com.test.backend.util.RedisUtil",
      "includeSubClasses"false,
      "methodPatterns": [
        "*"
      ]
    }
  ],
  "pluginIdentities": [  //加载的插件
      "redis",
     "http",
    "java-entrance",
    "java-subInvoke",
    "mybatis",
    "ibatis",
    "dubbo-provider",
    "dubbo-consumer"
  ],
  "repeatIdentities": [ //回放插件
    "java",
    "http",
    "dubbo"
  ],
  "sampleRate"10000//采样率,最高10000
  "useTtl"true  //开启ttl主子线程变量同步,主要应对多线程场景下的采集
}

接下来,我们通过源码,一步一步来看下具体的配置过程

二、启动过程

repeater插件的启动入口见com.alibaba.jvm.sandbox.repeater.module.RepeaterModule#loadCompleted, 基本流程就是:

  1. 拉取配置
  2. 初始化配置
  3. 发起心跳
@Override
    public void loadCompleted() {
        //这里启动一个线程去进行做加载,主要是为了不阻断sandbox的模块加载流程
        ExecutorInner.execute(new Runnable() {
            @Override
            public void run() {
                //standalone模式 我们在项目应用过程中是不需要考虑的,所以configManager就是DefaultConfigManager
                configManager = StandaloneSwitch.instance().getConfigManager();
                broadcaster = StandaloneSwitch.instance().getBroadcaster();
                invocationListener = new DefaultInvocationListener(broadcaster);
                RepeaterResult<RepeaterConfig> pr = configManager.pullConfig();
                if (pr.isSuccess()) {
                    log.info("pull repeater config success,config={}", pr.getData());
                    ClassloaderBridge.init(loadedClassDataSource);
                    
                    //启动的核心逻辑就在这里
                    initialize(pr.getData());
                }
            }
        });
        
        //心跳机制
        heartbeatHandler = new HeartbeatHandler(configInfo, moduleManager);
        heartbeatHandler.start();
    }

所以,配置相关的核心使用逻辑在 com.alibaba.jvm.sandbox.repeater.module.RepeaterModule#initialize里

  1. 读取配置
  2. 初始化invokePlugin插件列表
  3. 初始化repeater插件列表(目前实现的有http、java、dubbo)
  4. SubscribeSupporter插件初始化(默认实现 RepeatSubscribeSupporter )
private synchronized void initialize(RepeaterConfig config) {
        //确保一个周期里面,只初始化一次
        if (initialized.compareAndSet(falsetrue)) {
            try {
                ApplicationModel.instance().setConfig(config);
                // 特殊路由表; 这个特殊路由表是方便插件需要访问repeater不支持的类
                PluginClassLoader.Routing[] routingArray = PluginClassRouting.wellKnownRouting(configInfo.getMode() == Mode.AGENT, 20L);
                String pluginsPath;
                if (StringUtils.isEmpty(config.getPluginsPath())) {
                    pluginsPath = PathUtils.getPluginPath();
                } else {
                    pluginsPath = config.getPluginsPath();
                }
                //这个其实是一个类加载器
                lifecycleManager = new JarFileLifeCycleManager(pluginsPath, routingArray);
                // 装载插件
                invokePlugins = lifecycleManager.loadInvokePlugins();
                for (InvokePlugin invokePlugin : invokePlugins) {
                    try {
                        // 根据配置中的pluginIdentities,判断是否要注入相关拦截机制
                        if (invokePlugin.enable(config)) {
                            log.info("enable plugin {} success", invokePlugin.identity());
                            invokePlugin.watch(eventWatcher, invocationListener);
                            invokePlugin.onConfigChange(config);
                        }
                    } catch (PluginLifeCycleException e) {
                        log.info("watch plugin occurred error", e);
                    }
                }
                // 装载回放器
                List<Repeater> repeaters = lifecycleManager.loadRepeaters();
                for (Repeater repeater : repeaters) {
                    if (repeater.enable(config)) {
                        repeater.setBroadcast(broadcaster);
                    }
                }
                RepeaterBridge.instance().build(repeaters);
                // 装载消息订阅器
                List<SubscribeSupporter> subscribes = lifecycleManager.loadSubscribes();
                for (SubscribeSupporter subscribe : subscribes) {
                    subscribe.register();
                }
                
                //用于开启ttl的
                TtlConcurrentAdvice.watcher(eventWatcher).watch(config);
            } catch (Throwable throwable) {
                initialized.compareAndSet(truefalse);
                log.error("error occurred when initialize module", throwable);
            }
        }
    }
alt

所以回过头再看配置信息里,启动配置用到的是pluginIdentities、repeatIdentities;

三、java插件启动读取配置javaEntranceBehaviors、javaSubInvokeBehaviors

我们发现配置里还有javaEntranceBehaviors 和 javaSubInvokeBehaviors,这2个配置顾名思义就是java主子调用的意思;

我们去看代码com.alibaba.jvm.sandbox.repeater.plugin.java.JavaEntrancePlugin的实现,发现JavaEntrancePlugin重写了getEnhanceModels

@MetaInfServices(InvokePlugin.class)
public class JavaEntrancePlugin extends AbstractInvokePluginAdapter 
{

    private RepeaterConfig config;

    @Override
    protected List<EnhanceModel> getEnhanceModels() {
         //在这个地方读取对应配置的
        if (config == null || CollectionUtils.isEmpty(config.getJavaEntranceBehaviors())) { return null;}
        List<EnhanceModel> ems = Lists.newArrayList();
        for (Behavior behavior : config.getJavaEntranceBehaviors()) {
            ems.add(EnhanceModel.convert(behavior));
        }
        return ems;
    }
    
    ... 其余代码省略
}

同理 JavaSubInvokePlugin 也重写了getEnhanceModels()方法; 这里关注一个点,java主子调用配置的生效,最终是在 com.alibaba.jvm.sandbox.repeater.plugin.core.impl.AbstractInvokePluginAdapter#watchIfNecessary里,通过调用sandbox的接口com.alibaba.jvm.sandbox.api.listener.ext.EventWatchBuilder.IBuildingForBehavior#onWatch()来生效的,也就意味着需要真正字节码注入之后才会生效,所以本质上它的配置,也是启动配置的一部分;

四、http插件的配置 httpEntrancePatterns

首先肯定是看http插件关于 植入点的实现, 我们发现它的注入点是固定的javax.servlet.http.HttpServlet#service()方法,那有关httpEntrancePatterns是在哪里生效的呢?

@Override
    protected List<EnhanceModel> getEnhanceModels() {
        // 拦截javax.servlet.http.HttpServlet#service(HttpServletRequest req, HttpServletResponse resp)
        EnhanceModel.MethodPattern mp = EnhanceModel.MethodPattern.builder()
                .methodName("service")
                .parameterType(new String[]{"javax.servlet.http.HttpServletRequest""javax.servlet.http.HttpServletResponse"})
                .build();
        EnhanceModel em = EnhanceModel.builder()
                .classPattern("javax.servlet.http.HttpServlet")
                .methodPatterns(new EnhanceModel.MethodPattern[]{mp})
                .watchTypes(Event.Type.BEFORE, Event.Type.RETURN, Event.Type.THROWS)
                .build();
        return Lists.newArrayList(em);
    }

通过代码最终,我们最后发现是在com.alibaba.jvm.sandbox.repater.plugin.http.HttpStandaloneListener#doBefore里,也就是真正的 切点通知逻辑做的, 在拦截http的每次请求后判断是否需要采集;

@Override
protected void doBefore(BeforeEvent event) throws ProcessControlException {
        ... 冗余代码省略
        
        Object request = event.argumentArray[0];
        Object response = event.argumentArray[1];
        if (!(request instanceof HttpServletRequest && response instanceof HttpServletResponse)) {
            return;
        }
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;
        // 根据 requestURI 进行采样匹配, 这里取的配置
        List<String> patterns = ApplicationModel.instance().getConfig().getHttpEntrancePatterns();
        if (!matchRequestURI(patterns, req.getRequestURI())) {
            LogUtil.debug("current uri {} can't match any httpEntrancePatterns, ignore this request", req.getRequestURI());
            Tracer.getContext().setSampled(false);
            return;
        }
        
        ...冗余代码省略
  }

四、可优化的点

通过上述源码阅读,配置这块我觉得有以下几个可优化的点:

  1. 像sampleRate采样率配置,是经常需要手动调整的,这类配置可以跟启动配置pluginIdentities等区分开;启动配置的修改是需要重启生效或者重新attach生效的
  2. http的配置 以及 java的配置也可以区分开; 以下是我这边优化后的界面, 有需要欢迎多交流
alt
alt

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1026907.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RuoYi-Vue 新增子module模块,接口报404

新建maven子模块 参考若依添加子模块 解决接口报404 官方用例没有父版本号 需要在ruoyi-admin加上父版本号

淘宝分布式文件存储系统( 三 ) -TFS

淘宝分布式文件存储系统( 三 ) ->>TFS 目录 : 文件重新映射的接口介绍文件映射 mmap_file.cpp的实现进行测试 文件重新映射 (增加 或者 减少 文件映射区域的大小) mremap() 函数的原型如下 #include <sys/mman.h> void *mremap( void * old_address , size_…

git 使用之撤销暂存区提交

有时候git add 不想要的文件,不想要继续commit了, 可以使用如下命令: git reset HEAD如下命令 比较工作区和暂存区差异,查询是否恢复成功 git diff --cached

【Vue+NodeJS】vue路由及NodeJS环境搭建(Windows版)

一、Vue路由 1、什么是Vue路由 Vue路由是Vue.js框架中用于实现单页面应用&#xff08;SPA&#xff09;的路由管理器。它允许您创建多个页面之间的导航&#xff0c;并通过URL的变化来动态加载不同的组件。Vue路由通过声明式的方式定义页面的导航规则&#xff0c;并提供了一些内置…

机器视觉Halcon中read_image算子探究与学习

目录 算子作用参数注意点算子 作用 读取图片,用于从硬盘上读取图像数据并将其加载到Halcon中进行进一步的显示,分析,处理。 参数 Image:输出参数,表示读取的图像数据。 FileName:输入参数,表示待读取的图像文件的路径和名称 注意点 FileName有相对路径和绝对路径…

K8S:pod集群调度及相关操作

文章目录 一.pod集群调度概念1.调度约束( List-Watch组件)2.List-Watch的工作机制&#xff08;1&#xff09;List-Watch的工作机制流程&#xff08;2&#xff09;List-Watch的工作机制图示 3.调度的过程&#xff08;1&#xff09;调度的任务&#xff08;2&#xff09;调度选择p…

如何选择适合爬虫的动态住宅套餐

在当今互联网时代&#xff0c;爬虫已经成为了一项非常重要的技术。爬虫技术可以帮助企业和个人获取大量的数据&#xff0c;从而进行数据分析和决策。但是&#xff0c;要想让爬虫技术发挥最大的作用&#xff0c;就需要选择一款动态住宅套餐。那么&#xff0c;在选择动态住宅套餐…

vue内置组件Transition的详解

1. Transition定义 Vue 提供了两个内置组件&#xff0c;可以帮助你制作基于状态变化的过渡和动画&#xff1a; <Transition>会在一个元素或组件进入和离开 DOM 时应用动画。 <TransitionGroup> 会在一个 v-for 列表中的元素或组件被插入&#xff0c;移动&#xff0…

第七章 查找 五、二叉排序树

目录 一、定义 二、代码实现 1、查找 2、插入 3、构造 4、删除 三、查找效率分析 1、查找成功ASL 2、查找失败ASL 四、总结 一、定义 二叉排序树&#xff08;Binary Search Tree&#xff0c;BST&#xff09;是一种特殊的二叉树&#xff0c;它满足以下条件&#xff1a…

计算机竞赛 深度学习 机器视觉 车位识别车道线检测 - python opencv

0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习 机器视觉 车位识别车道线检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&#xff01; &#x1f947;学长这里给一个题目综合评分(每项满分5分) …

通讯网关软件007——利用CommGate X2Mbt实现Modbus TCP访问MSSQL服务器

本文介绍利用CommGate X2Mbt实现Modbus TCP访问MS SQL数据库。CommGate X2MBT是宁波科安网信开发的网关软件&#xff0c;软件可以登录到网信智汇(wangxinzhihui.com)下载。 【案例】如下图所示&#xff0c;实现上位机通过Modbus TCP来获取MS SQL数据库的数据。 【解决方案】设…

查看linux是centos还是Ubuntu

查看linux是centos还是Ubuntu 命令&#xff1a;cat /etc/os-release

【操作系统笔记】任务调度信号处理CPU上下文

任务调度 何时需要调度执行一个任务&#xff1f; 第一&#xff1a;当任务创建的时候&#xff0c;需要决定是继续执行父进程&#xff0c;还是调度执行子进程 第二&#xff1a;在一个任务退出时&#xff0c;需要做出调度决策&#xff0c;需要从 TASK_RUNNING 状态的所有任务中选…

java Spring Boot验证码美化,白色背景 随机四个数 每个字随机颜色

我前文 Spring Boot2.7生成用于登录的图片验证码讲述了生成验证码的方法 但是这样生成验证码 非常难看 比较说 验证码是要展示到web程序中的 这样让用户看着 属实不太好 我们可以将接口改成 GetMapping(value "/captcha", produces MediaType.IMAGE_PNG_VALUE) …

Webpack 热更新原理

什么是热更新 模块热替换(hot module replacement 或 HMR)是 webpack 提供的最有用的功能之一。它允许在运行时更新所有类型的模块&#xff0c;而无需完全刷新 一般的刷新我们分两种&#xff1a; 一种是页面刷新&#xff0c;不保留页面状态&#xff0c;就是简单粗暴&#xf…

思维链(Chain-of-Thought Prompting Elicits Reasoning in Large Language Models)

概括 论文主要描述了一种用思维链的提升LLM模型推理能力的方式&#xff0c;并且通过实验的方式&#xff0c;证明了思维链在算术、常识和符号等任务方面的显著效果。仅通过540B大小的PaLM模型&#xff0c;通过8个思维链样例就可以实现在GSM8K上的sota效果。 具体工作 这篇论文…

STM32 OLED屏幕显示详解

目录 1.OLED介绍 2.OLED如何显示一个点&#xff1f; 内存管理​编辑​编辑 页地址模式 水平地址模式​编辑 垂直地址模式 ​编辑 3.OLED显示图片 用到的库函数&#xff1a; 向OLED写命令的封装&#xff1a; 显示图片代码示例&#xff1a; 1.OLED介绍 OLED是有机发光…

论文笔记 DETR

detr 摘要和引言 2020论文facebook不需要proposal&#xff0c;不需要基于anchor的先验知识(比如预训练的模型)&#xff0c;也不需要NMS进行筛选&#xff0c;直接端到端不需要后处理利用transformer的全局建模能力&#xff0c;看成集合预测问题&#xff0c;不会输出很多冗余的…

谷歌AI机器人Bard发布强大更新,支持插件功能并增强事实核查;全面整理高质量的人工智能、机器学习、大数据等技术资料

&#x1f989; AI新闻 &#x1f680; 谷歌AI机器人Bard发布强大更新&#xff0c;支持插件功能并增强事实核查 摘要&#xff1a;谷歌的人工智能聊天机器人Bard发布了一项重大更新&#xff0c;增加了对谷歌应用的插件支持&#xff0c;包括 Gmail、Docs、Drive 等&#xff0c;并…

visual studio 安装包丢失或损坏

visual studio 安装包丢失或损坏 如下图所示为vs2015报错信息。 解决方案&#xff1a; 找到镜像文件或者压缩包的解压位置&#xff1b; 路径&#xff1a;C:\Users\Administrator\Desktop\packages 复制该路径至上图的请提供搜索包的位置。