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.mzph.cn/news/83676.shtml

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

相关文章

工业检测 ocr

采用OpenCV和深度学习的钢印识别_菲斯奇的博客-CSDN博客采用OpenCV和深度学习的钢印识别[这个帖子标题党了很久&#xff0c;大概9月初立贴&#xff0c;本来以为比较好做&#xff0c;后来有事情耽搁了&#xff0c;直到现在才有了一些拿得出手的东西。肯定不会太监的。好&#xf…

2023.9.19 关于 数据链路层 和 DNS 协议 基本知识

目录 数据链路层 MTU DNS 协议 补充 DHCP协议 数据链路层 基本概念&#xff1a; 考虑相邻两个节点之间的传输&#xff08;通过 网线 / 光纤 / 无线 直接相连的两个设备&#xff09;以太网协议 规定了 数据链路层 和 物理层 的内容 IP地址 与 mac地址 的相互配合 IP地址 描…

vue的模板语法(下篇)

目录 一.事件处理 二.表单的综合案例 三.组件通信⭐⭐ 3.1 自定义组件 3.2 组件通信之父传子 3.3组件通信之子传父 一.事件处理 Vue通过由点(.)表示的指令后缀来调用修饰符&#xff0c; .stop .prevent .capture .self .once 如下&#xff1a; 阻止单击事件冒泡 <a v-on…

踩坑:Invalid character found in method name. HTTP method names must be tokens

一、原因 在进行本地小程序与服务端请求时,由于加了签名认证,访问接口时报错 Spring boot端 小程序端 二、解决方案 2.1 更改访问路径 将https:更换成http: 示例:https://localhost:8080 改为 http://localhost:8080 2.2其他原因 ssl证书到期了Tomcat的header缓冲区大小不…

2023数学建模国赛游记

第一参加数学建模国赛&#xff0c;大概也是最后一次参加了&#xff0c;记录一下这几天的历程吧。 我们队的情况是计算机电气数统&#xff0c;计算机负责编程&#xff0c;电气学院的负责论文部分&#xff0c;数统的同学负责建模&#xff0c;数据处理部分我们是共同承担。 第一天…

秦丝9周年 | 各行业实体生意如何实现数字化转型?

近期&#xff0c;北京、深圳、天津、重庆等全国27个省都在推进“一刻钟便民生活圈”——以社区居民为服务对象&#xff0c;在步行15分钟左右的范围内&#xff0c;满足居民日常生活基本消费和品质消费。 而各行业的实体店是这个“圈”中的重要组成部分&#xff0c;很多入驻的实…

【使用Cpolar将Tomcat网页传输到公共互联网上】

文章目录 1.前言2.本地Tomcat网页搭建2.1 Tomcat安装2.2 配置环境变量2.3 环境配置2.4 Tomcat运行测试2.5 Cpolar安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2 Cpolar本地设置 4.公网访问测试5.结语 1.前言 Tomcat作为一个轻量级的服务器&#xff0c;不仅名字很有趣&#…

pdfjs解决ie浏览器预览pdf问题

pdfjs是一个js库&#xff0c;可以将pdf文件用canvas重新绘制&#xff0c;从而无需借助pdf读取插件就可以直接预览。 目前chrome内核的浏览器已内置pdf读取插件&#xff0c;但ie浏览器还没有。而我们最近在做的一个项目使用对象是医院&#xff0c;使用的浏览器竟然还是ie。所以我…

Python 数据分析学习路线

Python 数据分析学习路线 第一阶段&#xff1a;Python语言基础第二阶段&#xff1a;数据采集和持久化第三阶段&#xff1a;数据分析第四阶段&#xff1a;数据挖掘与机器学习书籍介绍参与方式 第一阶段&#xff1a;Python语言基础 在学习数据分析之前&#xff0c;首先需要掌握P…

iOS加固保护技术:保护你的iOS应用免受恶意篡改

目录 转载&#xff1a;开始使用ipaguard 前言 下载ipa代码混淆保护工具 获取ipaguard登录码 代码混淆 文件混淆 IPA重签名与安装测试 转载&#xff1a;开始使用ipaguard 前言 iOS加固保护是直接针对ios ipa二进制文件的保护技术&#xff0c;可以对iOS APP中的可执行文件…

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

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

ClickHouse与Elasticsearch比较总结

目录 背景 分布式架构 存储架构 写入链路设计 Elasticsearch 再谈Schemaless 查询架构 计算引擎 数据扫描 再谈高并发 性能测试 日志分析场景 access_log&#xff08;数据量197921836&#xff09; trace_log&#xff08;数据量569816761&#xff09; 官方Ontime测…

云原生的简单理解

一、何谓云原生&#xff1f; 一种构建和运行应用软件的方法 应用程序从设计之初即考虑到云的环境&#xff0c;原生为云而设计&#xff0c;在云上以最佳姿势运行&#xff0c;充分利用和发挥云平台的弹性分布式优势。 二、包括以下四个要素 采用容器化部署&#xff1a;实现云平…

el-table表格中加入输入框

<template><div class"box"><div class"btn"><el-button type"primary">发送评委</el-button><el-button type"primary" click"flag true" v-if"!flag">编辑</el-button…

win系统环境搭建(九)——Windows安装chatGPT

windows环境搭建专栏&#x1f517;点击跳转 win系统环境搭建&#xff08;九&#xff09;——Windows安装chatGPT 本系列windows环境搭建开始讲解如何给win系统搭建环境&#xff0c;本人所用系统是腾讯云服务器的Windows Server 2022&#xff0c;你可以理解成就是你用的windows…

全球南方《乡村振兴战略下传统村落文化旅游设计》许少辉八一著辉少许

全球南方《乡村振兴战略下传统村落文化旅游设计》许少辉八一著辉少许

Unity云原生分布式运行时

// 元宇宙时代的来临对实时3D引擎提出了诸多要求&#xff0c;Unity作为游戏行业应用最广泛的3D实时内容创作引擎&#xff0c;为应对这些新挑战&#xff0c;提出了Unity云原生分布式运行时的解决方案。LiveVideoStack 2023上海站邀请到Unity中国的解决方案工程师舒润萱&#x…

倒计时列表实现(小程序端Vue)

//rich-text主要用来将展示html格式的&#xff0c;可以直接使用这个标签 <view class"ptBox" v-for"(item,index) in orderList" :key"index"> <rich-text :nodes"item.limit_time|limitTimeFilter"></rich-text>…

2023_Spark_实验十二:Spark高级算子使用

掌握Spark高级算子在代码中的使用 相同点分析 三个函数的共同点&#xff0c;都是Transformation算子。惰性的算子。 不同点分析 map函数是一条数据一条数据的处理&#xff0c;也就是&#xff0c;map的输入参数中要包含一条数据以及其他你需要传的参数。 mapPartitions函数是一个…

网络编程day03(UDP中的connect函数、tftp)

今日任务&#xff1a;tftp的文件上传下载&#xff08;服务端已经准备好&#xff09; 服务端&#xff08;已上传&#xff09; 客户端&#xff1a; 代码&#xff1a; #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/types.h…