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

一、配置初见

源码里提供的控制台截图如下:(怎么搭建自己去百度) 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.rhkb.cn/news/137396.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SunTorque亮相GAF2023数字化智能装配工程与装备技术大会

智能扭矩系统-智能拧紧系统-智能扭矩控制-SunTorque GAF2023数字化智能装配工程与装备技术大会在中国上海汽车会展中心盛大开幕&#xff0c;青创智通与装配领域、智能制造、数字化应用等相关先进智造技术的知名企业一齐亮相。 本次展会&#xff0c;我们带来了扭矩相关解决方案…

Vivado IP中Generate Output Products的设置说明

文章目录 Vivado IP中Generate Output Products的设置说明Synthesis OptionsRun Settings 官方文档中的介绍Generate Output ProductsSynthesis Options for IP 参考文献 Vivado IP中Generate Output Products的设置说明 在创建IP核时&#xff0c;将IP核的信息配置完成之后会弹…

数据预处理方式合集

删除空行 #del all None value data_all.dropna(axis1, howall, inplaceTrue) 删除空列 #del all None value data_all.dropna(axis0, howall, inplaceTrue) 缺失值处理 观测缺失值 观测数据缺失值有一个比较好用的工具包——missingno&#xff0c;直接传入DataFrame&…

面向面试知识-Redis

面向面试知识-Redis 什么是Redis 运行于内存的基于key-value的非关系型数据库。 一款开源的内存数据结构存储&#xff0c;用作数据库、缓存、消息代理等。&#xff08;可以基于Redis实现分布式锁、以及消息队列&#xff09; 发布订阅&#xff1f;&#xff1f; 对数据类型的操…

【操作系统笔记】内存分配

内存对齐 问题&#xff1a;为什么需要内存对齐呢&#xff1f; 主要原因是为了兼容&#xff0c;为了让程序可以运行在不同的处理器中&#xff0c;有很多处理器在访问内存的时候&#xff0c;只能从特定的内存地址读取数据。换个说法就是处理器每次只能从内存取出特定个数字节的数…

Spring Boot实现对超大文件进行异步压缩下载

在Web应用中&#xff0c;文件下载功能是一个常见的需求&#xff0c;特别是当你需要提供用户下载各种类型的文件时。本文将演示如何使用Spring Boot框架来实现一个简单而强大的文件下载功能。我们将创建一个RESTful API&#xff0c;通过该API&#xff0c;用户可以下载问价为ZIP压…

Python计算机二级知识点整理

1.当一个进程在运行过程中释放了系统资源后要调用 唤醒进程原语 唤醒进程原语是把进程从等待队列里移出到就绪队列并设置进程为就绪状态&#xff0c;当一个进程在运行过程中释放了系统资源后进入就绪状态&#xff0c;调用唤醒进程原语。 2. 3. 4.在希尔排序法中&#x…

免费开源 | 基于SSM的校园订餐系统

源码下载地址获取 关注并私信回复“订餐”&#xff0c;获取源码下载方式 一定要关注后发消息&#xff0c;否则系统限制无法回复消息 感谢开源&#xff01;侵删&#xff01; 一、功能实现 1. 前台模块 前台主要功能有&#xff1a;用户注册、用户登录、我的购物车、我的订单、…

人类认知的贝叶斯与机器的贝叶斯

贝叶斯原理是一种基于概率的分析方法&#xff0c;可以用来估计一个事件发生的概率。在人类认知和机器学习领域中&#xff0c;都有对应的贝叶斯原理。 人类认知的贝叶斯原理&#xff1a; 在人类认知研究中&#xff0c;贝叶斯原理被认为是一种重要的思维方式。人类的认知过程通常…

大数据与云计算实验一

检查是否开启 sudo service docker status 开启服务 sudo service docker start 运行服务 sudo docker run -itd -p 8080:80 nginx 查询ID docker ps -all 进入容器shell sudo docker exec -it <容器ID或容器名称> /bin/bash 找到/usr/share/nginx/html/index.…

DevSecOps内置安全保护

前言 随着DevOps的发展&#xff0c;DevOps大幅提升了企业应用迭代的速度。但同时&#xff0c;安全如果不能跟上步伐&#xff0c;不仅会抵消DevOps变革带来的提升&#xff0c;拖慢企业数字化转型进程&#xff0c;还会导致漏洞与风险不约而至。所以安全能力在全球范围内受到的重…

图像识别-YOLO V8安装部署-window-CPU-Pycharm

前言 安装过程中发现&#xff0c;YOLO V8一直在更新&#xff0c;现在是2023-9-20的版本&#xff0c;已经和1月份刚发布的不一样了。 eg: 目录已经变了&#xff0c;旧版预测:在ultralytics/yolo/v8/下detect 新版&#xff1a;ultralytics/models/yolo/detect/predict.py 1.安…

aspose-words导出word方法

一、引用依赖 <dependency><groupId>com.aspose</groupId><artifactId>aspose-words</artifactId><version>19.5</version><classifier>jdk17</classifier></dependency>二、导出类 public class WordTable {//定…

腾讯云阿里云云服务器 Linux 操作系统 BT 宝塔面板快速建站教程

宝塔面板概述 宝塔面板是一款服务器管理软件&#xff0c;支持Windows和Linux系统&#xff0c;可以通过Web端轻松管理服务器&#xff0c;提升运维效率。总体来说&#xff0c;宝塔面板具有操作简单、功能丰富、安全可靠等特点&#xff0c;是一款非常实用的服务器管理软件。 宝塔…

GPT,GPT-2,GPT-3,InstructGPT的进化之路

ChatGPT 火遍圈内外&#xff0c;突然之间&#xff0c;好多人开始想要了解 NLP 这个领域&#xff0c;想知道 ChatGPT 到底是个什么&#xff1f;作为在这个行业奋斗5年的从业者&#xff0c;真的很开心让人们知道有一群人在干着这么样的一件事情。这也是我结合各位大佬的文章&…

滚雪球学Java(25):动态代理

&#x1f3c6;本文收录于「滚雪球学Java」专栏&#xff0c;专业攻坚指数级提升&#xff0c;助你一臂之力&#xff0c;带你早日登顶&#x1f680;&#xff0c;欢迎大家关注&&收藏&#xff01;持续更新中&#xff0c;up&#xff01;up&#xff01;up&#xff01;&#xf…

IPv6协议基本概念

目前大多数设备节点支持IPv6和IPv4双栈,但随着IPv6逐渐应用,某些设备已经只支持纯IPv6,即IPv6 Only。 一、IPv6地址格式 1、IPv6地址表示方式 IPv6可以写成一组8个十六进制数,用冒号(:)分割。也可以写成128位2进制的0和1。 32即8x4,8表示8组16进制数,4表示每组16禁止包…

Git基础操作

前言 本文会向您介绍如何安装git&#xff0c;以及快速地上手add&#xff0c;commit&#xff0c;push&#xff0c;版本回退操作 基础配置 关于windous上的安装git官网已经介绍的很清楚了&#xff0c;您可以直接点入链接windows安装 如果你的平台是centos&#xff0c;以centos…

【Verilog教程】2.4Verilog表达式

表达式 表达式由操作符和操作数构成&#xff0c;其目的是根据操作符的意义得到一个计算结果。表达式可以在出现数值的任何地方使用。 例如&#xff1a; a^b ; //a与b进行异或操作 address[9:0] 10b1 ; //地址累加 flag1 && flag2 ; //逻辑与操作操作数 …

有没有普通人可以做的项目?分享几个项目适合普通人做的!

当谈到副业时&#xff0c;我们通常会想到能够轻松实施的兼职方式&#xff0c;并且希望通过这些副业增加我们的收入。那么&#xff0c;以下是我推荐的几种副业&#xff0c;这些副业不仅有良好的收入潜力&#xff0c;而且在执行过程中也很有趣。让我们一起看看吧&#xff01; 第…