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

news2024/11/28 14:53:56

问题描述

总所周知,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.coloradmin.cn/o/1650138.html

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

相关文章

高项第四版 十大管理及49个过程【背】作业分享

项目管理 1.十大管理【背】 包括&#xff08;口诀:范进整狗子&#xff08;沟质&#xff09; 才&#xff08;采&#xff09;干成疯子&#xff08;风资&#xff09;&#xff09;: &#xff08;1&#xff09;项目整合管理:识别、定义、组合、统一和协调各项目管理过程组的各个过…

记一些内存取证题

生活若循规蹈矩&#xff0c;我们便随心而动 1.Suspicion 给了俩文件 python2 vol.py -f mem.vmem imageinfo 查看可疑进程 python2 vol.py -f mem.vmem --profileWinXPSP2x86 pslist 发现可疑进程TrueCrypt.exe 把这个进程提取出来。memdump -p 进程号 -D 目录 python2 vol…

QT+网络调试助手+TCP服务器

一、UI界面设计 二、单线程 代码设计 1、 查找合法的本地地址&#xff0c;用于当作服务器的IP地址 #include <QThread> #include <QTcpSocket> #include <QNetworkInterface> #include <QMessageBox>QList<QHostAddress> ipAddressesList QNe…

华为机考入门python3--(23)牛客23- 删除字符串中出现次数最少的字符

分类&#xff1a;字符串 知识点&#xff1a; 访问字典中keychar的值&#xff0c;不存在则返回0 my_dict.get(char, 0) 字典的所有值 my_dict.value() 列表中的最小值 min(my_list) 题目来自【牛客】 import sysdef delete_min_freq_char(s):# 计算字母出现的频次…

Arduino控制继电器,制作智能浇水系统

所需硬件材料 Arduino模块、继电器、直流电机、3-6v电池&#xff08;这个是必须的&#xff0c;电机不能直接接在arduino的5v引脚上&#xff0c;会引起电压不足&#xff09;、杜邦线 实现效果&#xff1a; 电机转动一秒停一秒 将硬件连接如下&#xff1a; 将电机连接到继电…

(MATLAB)安装指南

参考链接&#xff1a;MATLAB2019a安装教程&#xff08;避坑版&#xff09;

应用层协议——HTTP协议

1. 认识HTTP协议 HTTP&#xff08;Hyper Text Transfer Protocol&#xff09;协议又叫做超文本传输协议&#xff0c;是一个简单的请求-响应协议&#xff0c;HTTP通常运行在TCP之上。 超文本的意思就是超越普通的文本&#xff0c;http允许传送文字&#xff0c;图片&#xff0c…

PostgreSQL连接拒绝如何解决和排查?

1. 服务器未运行 解决方案&#xff1a;确保 PostgreSQL 服务已启动。在 Linux 上&#xff0c;你可以使用如下命令来检查服务状态&#xff1a;sudo systemctl status postgresql如果服务未运行&#xff0c;使用以下命令启动它&#xff1a;sudo systemctl start postgresql2. Po…

基于Springboot+Vue的Java项目-旅游网站系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…

shell脚本编写-测试同一网段内主机是否在线

除了可以使用ansible自动化运维工具判断主机是否在线以外&#xff0c;还可以通过编写Shell脚本来实现。 1、编写脚本 #! /bin/bash #测试192.168.81.0/24网段中哪些主机处于开机状态&#xff0c;哪些主机处于关机状态# #方法一&#xff1a;使用for循环判断 # for i in {1..25…

亿发解密:数据中台管理系统,引领企业数字化转型的智能数据体系

在当今数字化时代&#xff0c;数据已成为企业发展的关键驱动力。为了更好地利用数据&#xff0c;提升业务水平&#xff0c;企业需要建立一套完备的数据管理体系&#xff0c;而数据中台便应运而生。 什么是数据中台 数据中台是集方法论、组织和工具于一体的智能大数据体系。它…

通信录的动态版本

一. 增加需求 在学习了动态开辟内存之后 我们对于通讯录产生了新的需求 要求我们做出一个动态增长的版本 即 随着我们储存联系人的增加 储存的空间增加 要求 &#xff1a; 1 初始空间为3 2 每次达到上限之后 扩容两个内存 二. 动手实施 我们首先要创建一个结构体 结构体…

计算图:深度学习中的链式求导与反向传播引擎

在深度学习的世界中&#xff0c;计算图扮演着至关重要的角色。它不仅是数学计算的图形化表示&#xff0c;更是链式求导与反向传播算法的核心。本文将深入探讨计算图的基本概念、与链式求导的紧密关系及其在反向传播中的应用&#xff0c;旨在为读者提供一个全面而深入的理解。 计…

Springboot项目学习之各组件的用法和逻辑结构

1.Controller层&#xff08;Controller&#xff09;&#xff1a; 也称为前端控制器或请求处理器&#xff0c;它是项目与用户交互的入口。Controller接收HTTP请求&#xff0c;解析请求参数&#xff0c;调用Service层处理业务逻辑&#xff0c;并返回响应给客户端。 Controller通…

Python实现txt转Excel(坐标)

import pandas as pddef txt_to_excel(txt_file, excel_file):# 读取 txt 文件with open(txt_file, r) as f:lines f.readlines()# 将每行数据分割成多个单元格data []for line in lines:row line.strip().split( )data.append(row)# 将数据保存到 Excel 文件df pd.DataFra…

阿里巴巴alibaba国际站API接口:商品详情和关键词搜索商品列表

阿里巴巴国际站&#xff08;Alibaba.com&#xff09;提供了API接口供开发者使用&#xff0c;以实现与平台的数据交互。然而&#xff0c;由于API的详细内容和调用方式可能会随着时间和平台更新而发生变化&#xff0c;以下是一个概述和一般性的指导&#xff0c;关于如何使用阿里巴…

代码随想录第52天|300.最长递增子序列 718. 最长重复子数组

300.最长递增子序列 300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 动态规划之子序列问题&#xff0c;元素不连续&#xff01;| LeetCode&#xff1a;300.最长递增子序列_哔哩哔哩_bilibili 给你一个整数数组 nums &#xff0…

xmind的13个快捷方式

1.新建导图 CtrlshiftN 2.编辑文字 空格键 3.插入图片 Ctrli 4. 插入主题 Enter键 5. 插入主题之前 ShiftEnter键 6. 插入子主题 Tab键 7. 放大导图 “Ctrl”“” 8. 缩小导图 “Ctrl”“-” 9. 复制 CtrlInsert 10. 粘贴 Shift Insert 11. 剪切 ShiftDelete 12. 截图 F7 13. 保…

神经网络怎么把隐含层变量融合到损失函数中?

&#x1f3c6;本文收录于「Bug调优」专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&&…

Unity初级---初识生命周期

1. Awake() &#xff1a;唤醒函数&#xff0c;最先执行的函数&#xff0c;只执行一次&#xff0c;当脚本文件挂载的对象被激活时调用 2. OnEnable() &#xff0c;OnDisable()&#xff1a;当脚本启用和禁用时触发&#xff0c;可执行多次&#xff0c;触发的前提是脚本挂载的对象…