研发效能 | Jacoco dump基于k8s的实现

问题描述

总所周知,jacoco的dump操作如果是使用server模式只需要使用以下命令就能获取到 exec 文件。

java -jar jacococli.jar dump --address 192.169.110.1 --port 6300 --destfile ./jacoco-demo.exec

如果是非 k8s 的集群,也只需要遍历执行这条命令即可,但是对于 k8s 服务的处理有有点力所不逮。

当我们使用 k8s 部署服务后,应用实例将会无状态话,用户不再去关心实例的 ip,端口等信息,service 自动会帮我们做负载均衡等操作,pod 不会暴露出 ip 和端口等信息给集群外部访问,这样对我们的 dump 操作带来了困难。

问题解决

针对上述问题,网络上也有一些解决方案,最常用的方式是切换 jacooc server 模式为 client 模式,这样当 jvm 关闭时就会将 dump 数据写入指定服务的文件里。虽然能从一定程度解决问题,但是这样生成报告的节奏就会被打断,就不能随时生成报告了,这里提供一种解决方式。

首先,我们还是采用 server 模式,在服务启动时注入

-javaagent:/jacoco/agent/jacocoagent.jar=includes=*,output=tcpserver,port=6300,address=0.0.0.0

然后,当我们想要去获取 exec 文件时,可以在 pod 中执行

java -jar /jacoco/agent/jacococli.jar dump --address 127.0.0.1 --port 36300 --destfile /app/jacoco.exec

然后我们从 pod 读取文件/app/jacoco.exec 写入我们的报告生成服务即可

怎么去 pod 内部执行 shell 命令,各种手动都有,这里我们 java 基于一个 k8s 的 sdk 工具 fabric8 实现

  1. public List<String> dumpK8sExecData(K8sDumpParam k8sDumpParam) {

  2.    try {

  3.        String dumpCmd = "JAVA_TOOL_OPTIONS=\"\" java -jar /jacoco/agent/jacococli.jar dump --address 127.0.0.1 --port 6300 --destfile /app/jacoco.exec";

  4.        if (k8sDumpParam.getResetFlag()) {

  5.            dumpCmd += " --reset";

  6.        }

  7.        String[] cmd = {"sh", "-c", dumpCmd};

  8.        K8sCmdParam k8sCmdParam = OrikaMapperUtils.map(k8sDumpParam, K8sCmdParam.class);

  9.        k8sCmdParam.setCmd(cmd);

  10.        k8sCmdParam.setExecutor(executor);

  11.        return executeCmd(k8sCmdParam);

  12.    } catch (Exception e) {

  13.        log.error("dump操作失败,失败原因:", e);

  14.        throw new BizException(BizCode.JACOCO_DUMP_ERROR);

  15.    }

 
  1. public List<String> executeCmd(K8sCmdParam k8sCmdParam) {

  2.    KubernetesClient client = K8sClientProxy.getOrCreateClient(k8sCmdParam.getKubeConfig());

  3.    if (client == null || k8sCmdParam.getNameSpace() == null || CollectionUtil.isEmpty(k8sCmdParam.getPodList())) {

  4.        throw new BizException(BizCode.JACOCO_DUMP_PARAM_ERROR);

  5.    }

  6.    List<CompletableFuture<String>> priceFuture = k8sCmdParam.getPodList().stream().map(pod ->

  7.            CompletableFuture.supplyAsync(() -> {

  8.                String filename = "";

  9.                // 异步操作

  10.                dumpFileService.podExec(pod, k8sCmdParam.getCmd(), k8sCmdParam.getNameSpace(), client);

  11.                try {

  12.                    //中间等待文件写入一段时间,再去尝试获取

  13.                    Thread.sleep(1000);

  14.                    filename = dumpFileService.downloadFile(pod, k8sCmdParam.getNameSpace(), client, k8sCmdParam.getTaskWorkspace());

  15.                } catch (Exception e) {

  16.                    throw new BizException(BizCode.DUMP_FILE_GET_ERROR);

  17.                }

  18.                return filename;

  19.            }, k8sCmdParam.getExecutor())

  20.    ).collect(Collectors.toList());

  21.    // 等待所有异步操作完成,多个pod并发执行以上操作,减少dump的时间消耗

  22.    CompletableFuture.allOf(priceFuture.toArray(new CompletableFuture[0])).join();

  23.    return priceFuture.stream().map(CompletableFuture::join).filter(Objects::nonNull).collect(Collectors.toList());

  24. }

  1. /**

  2. * 执行单个pod命令

  3. *

  4. * @param podName   pod名字

  5. * @param cmd       cmd

  6. * @param namespace 名称空间

  7. * @param client    客户端

  8. */

  9. public void podExec(String podName, String[] cmd, String namespace, KubernetesClient client) {

  10.    try (ExecWatch watch = client.pods().inNamespace(namespace)

  11.            .withName(podName)

  12.            .redirectingOutput()

  13.            .exec(cmd)) {

  14.    }

  15. }

  16. /**

  17. * 获取文件

  18. *

  19. * @param podName   pod名字

  20. * @param namespace 名称空间

  21. * @param client    客户端

  22. * @param workspace 工作空间

  23. */

  24. @Retryable(value = {IOException.class}, backoff = @Backoff(delay = 1000))

  25. public String downloadFile(String podName, String namespace, KubernetesClient client, String workspace) throws IOException {

  26.    try (InputStream is = client.pods().inNamespace(namespace)

  27.            .withName(podName)

  28.            .file("/app/jacoco.exec").read()) {

  29.        String execPath = workspace + "/exec/" + podName + "/jacoco.exec";

  30.        FileUtil.writeFromStream(is, execPath);

  31.        return execPath;

  32.    }

  33. }

这里有两个细节点

  • Thread.sleep(1000) 操作,是因为执行 dump 命令后,我们无法判定 exec 文件什么时候能在本地生成完成,立马获取就会抛出 IO 异常,等待一定时间后即可获取到文件,这个时间的等待只是第一层保障,具体等待时间,可以视自己的 dump 文件大小调整,当然哪怕没调整也没有关系

  • @Retryable(value = {IOException.class}, backoff = @Backoff(delay = 1000)) 这段代码是使用了 spring 的一个重试框架,当文件获取失败后,默认会重试 3 次,每次重试间隔 1 秒,这是获取文件的第二步保障,用户可以通过调整重试次数来减少文件获取失败风险

这里说明下 spring Retryable 必须在 public 方法上,而且调用它的方法不能和他处于同一个类,否则不会生效重试。

通过以上手段就可以主动去 dump 出想要的数据,当然更好的方式是判断 exec 文件是否存在,或者还在写入中,等写入完成再去获取文件,这个操作也可以通过 shell 去完成,本文只是提供一种实现方案。

行动吧,在路上总比一直观望的要好,未来的你肯定会感 谢现在拼搏的自己!如果想学习提升找不到资料,没人答疑解惑时,请及时加入扣群: 320231853,里面有各种软件测试+开发资料和技术可以一起交流学习哦。

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴上万个测试工程师们走过最艰难的路程,希望也能帮助到你!

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

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

相关文章

Python实现打砖块游戏

提供学习或者毕业设计使用&#xff0c;功能基本都有&#xff0c;不能和市场上正式游戏相提比论&#xff0c;请理性对待&#xff01; 在本文中&#xff0c;我们将使用 Pygame 和 Tkinter 创建一个简单的打砖块游戏。游戏的目标是通过控制挡板来击碎屏幕上的砖块&#xff0c;同时…

基于 OpenHarmony compress 三方件使用指南~

关于 提供了一个轻量级的图像压缩库。将允许您将大照片压缩成小 尺寸的照片&#xff0c;图像质量损失或可以忽略不计 compress 的依赖添加 为你的应用添加 compress-debug.har。将 compress-debug.har 复制到 entry\libs 目录下即可&#xff08;由于 build.gradle 中已经依赖…

HALPWM配置占空比频率2按键控制思路

title: HALPWM配置占空比频率 tags: STM32ClionHal 控制pwm开关 //启动定时器 HAL_StatusTypeDef HAL_TIM_Base_Start(TIM_HandleTypeDef *htim);//关闭定时器 HAL_StatusTypeDef HAL_TIM_Base_Stop(TIM_HandleTypeDef *htim);//启动指定通道PWM HAL_StatusTypeDef HAL_TIM_P…

【负载均衡式在线OJ项目day1】项目结构

一.功能 查看题目列表&#xff0c;在线编程&#xff0c;判题功能&#xff0c;即leetcode的部分功能 二.宏观结构 整个项目是BS模式&#xff0c;客户端是浏览器&#xff0c;和用户交互并向服务器发起请求。 服务端从功能上来说分为两个模块&#xff0c;第一个是OJServer&…

小红书餐饮推广怎么合作?纯干货

小红书作为国内领先的生活方式分享平台&#xff0c;其用户群体主要集中在一二线城市&#xff0c;年龄分布在18-35岁之间&#xff0c;其中女性用户占比高达80%。这部分用户具有较高的消费能力、审美追求和品质生活需求&#xff0c;对美食有着极高的兴趣和消费意愿&#xff0c;为…

Mysql 行格式 innodb_default_row_format 可以配置那些值, 有什么区别

在MySQL中&#xff0c;innodb_default_row_format选项指定了InnoDB表创建时的默认行格式。这个设置对于新创建的表非常重要&#xff0c;因为它影响了数据的存储方式、空间利用率和性能。innodb_default_row_format可以配置为以下几个值&#xff0c;每个值代表不同的行格式&…

【计算机网络】计算机网络的性能指标

计算机网络的性能指标被用来从不同方面度量计算机网络的性能。常用的八个计算机网络性能指标&#xff1a;速率、带宽、吞吐量、时延、时延带宽积、往返时间、利用率、丢包率。 一.速率 (1) 数据量 比特&#xff08;bit&#xff0c;记为小写b&#xff09;是计算机中数据量的基…

python使用mongo操作

目前有个需求&#xff0c;就是把所有sql转为mongo管道查询 知识点 在 MongoDB 中&#xff0c;allowDiskUse 选项应该作为聚合命令的一个选项&#xff0c;而不是聚合管道的一个阶段。allowDiskUse 选项用于允许聚合操作使用磁盘空间来临时存储数据&#xff08;当聚合操作的数据…

力扣顺序表思路讲解

本篇文章&#xff0c;我给大家带来的是顺序表题目讲解&#xff0c;希望大家看完有所收获&#xff0c;废话不多说&#xff0c;我们现在开始 审题 大白话&#xff1a;给了一个数组和一个目标值。如果数组里的两个元素相加 目标值&#xff0c;则返回这两个元素的下标。那么大家需…

java编程中,实现分页对象的类型转换

一、背景 当数据库分页查询返回的对象与接口要返回的对象类型不一致时&#xff0c;不可避免需要进行类型转换。 示例&#xff1a;数据库分页查询返回的对象是PageDTO&#xff0c;而接口返回的对象类型是PageVO。 PageDTO Data public class PageDTO<T> {/*** Current…

Python专题:一、安装步骤

1、下载地址&#xff1a;Welcome to Python.org 勾选这个add 其他的全部下一步即可。 运行出现这个即代表安装成功。 Python自带编辑器。 2、推荐使用的sublime 编辑器下载 全部下一步安装。

C++ 函数与指针

函数内部数据是地址需要传递给调用函数&#xff0c;返回的当然是指针了&#xff01;当然&#xff0c;这个返回地址也可以通过函数参数返回&#xff01; 函数的参数是指针可以输出函数多个结果&#xff0c;返回值本身就是返回数据&#xff0c;什么时候需要返回指针呢&#xff1f…

Spring从零开始学使用系列(五)--Bean定义继承与容器扩展点

1.引言 Spring框架因其强大的依赖注入和灵活的配置管理而广受欢迎。Bean定义继承和容器扩展点是其提供的两个核心特性&#xff0c;它们为开发者在维护大型应用时提供了极大的便利。本文旨在详细探讨这些特性的工作原理和实际应用&#xff0c;帮助开发者更好地理解和利用这些高级…

学习记录之数学表达式(4)

文章目录 八、min 与 argmin8.1 min8.2 argmin8.3 作业 九、累加、累乘与积分9.1 累加9.2 累乘9.3 定积分9.4 作业 八、min 与 argmin min 和 argmin 在机器学习中常用&#xff1b;max 和argmax 同理&#xff1b; 8.1 min min 是 minimal 的缩写&#xff0c;用于获得集合中的…

5.6代码

1.最大公约数 这个题最重要的是要找到一个区间是1&#xff0c;找到之后就可以直接加次数就可以了 #include <bits/stdc.h>using namespace std;main() {long long n,i,j,a0,b,ans99999;cin>>n;long long s[n],dp[n][n];for(i0;i<n;i){cin>>s[i];if(s[i]1…

sorted_numbers = numbers_int.sort()对不对??不对!!!

不对&#xff0c;numbers_int.sort() 方法会直接修改 numbers_int 列表&#xff0c;并且它不返回任何值&#xff08;在 Python 中&#xff0c;对列表的原地修改方法通常返回 None&#xff09;。因此&#xff0c;将 numbers_int.sort() 的结果赋值给 sorted_numbers 变量会导致 …

238. 除自身以外数组的乘积/41. 缺失的第一个正数

238. 除自身以外数组的乘积 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&a…

shell常用文件处理命令

1. 解压 1.1 tar 和 gz 文件 如果你有一个 .tar 文件,你可以使用以下命令来解压: tar -xvf your_file.tar在这个命令中,-x 表示解压缩,-v 表示详细输出(可选),-f 后面跟着要解压的文件名。 如果你的 .tar 文件同时被 gzip 压缩了(即 .tar.gz 文件),你可以使用以下…

黑马程序员HarmonyOS4+NEXT星河版入门到企业级实战教程笔记

HarmonyOS NEXT是纯血鸿蒙&#xff0c;鸿蒙原生应用&#xff0c;彻底摆脱安卓 本课程是基于harmony os4的&#xff0c;与next仅部分api有区别 套件 语言&框架 harmony os design ArkTs 语言 ArkUI 提供各种组件 ArkCompiler 方舟编译器 开发&测试 DevEco Studio 开发…

Vue3工程化配置

Vue3工程化配置 目录 Vue3工程化配置创建项目vue-clivite(推荐) 快速体验2和3的差别vue3vue2 ref和reactive 创建项目 vue-cli 具体环境配置请点这里 记得新建配置时这里选vue3 vite(推荐) 注&#xff1a;Vite 需要 Node.js 版本 18&#xff0c;20 1.选定路径后再cmd输入创建…