千家信息网

如何将Arthas集成进Spring Boot监控平台中

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,这篇文章将为大家详细讲解有关如何将Arthas集成进Spring Boot监控平台中,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。前言Arthas 是 Alibab
千家信息网最后更新 2026年10月31日如何将Arthas集成进Spring Boot监控平台中

这篇文章将为大家详细讲解有关如何将Arthas集成进Spring Boot监控平台中,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。

前言

Arthas 是 Alibaba 开源的 Java 诊断工具,具有实时查看系统的运行状况;查看函数调用参数、返回值和异常;在线热更新代码;秒解决类冲突问题;定位类加载路径;生成热点;通过网页诊断线上应用。如今在各大厂都有广泛应用,也延伸出很多产品。

SpringBoot Admin

为了方便,SpringBoot Admin 简称为 SBA(版本:1.5.x)。

1.5 版本的 SBA 如果要开发插件比较麻烦,需要下载 SBA 的源码包,再按照 Spring-boot-admin-server-ui-hystrix的形式 Copy 一份,由于 JS 使用的是 Angular,本人尝试了很久,虽然掌握了如何开发插件,奈何不会 Angular,遂放弃????

版本:2.x 2.x 版本的 SBA 插件开发,官网有介绍如何开发,JS 使用 Vue,方便很多,由于我们项目还在使用 1.5,所以并没有使用该版本,请读者自行尝试。

不能使用 SBA 的插件进行集成,那还有什么办法呢?????

SBA 集成

鄙人的办法是将 Arthas 的相关文件直接 Copy 到 Admin 服务中,这些文件都来自 Arthas-all 项目 Tunnel-server。

admin 目录结构

1. Arthas 目录

该包下存放的是所有 Arthas 的 Java 文件。

  • Endpoint 包下的文件可以都注释掉,没多大用。

  • ArthasController 这个文件是我自己新建的,用来获取所有注册到 Arthas 的客户端,这在后面是有用的。

  • 其他文件直接 Copy 过来就行。

@RequestMapping("/api/arthas")@RestControllerpublic class ArthasController { @Autowired private TunnelServer tunnelServer;   @RequestMapping(value = "/clients", method = RequestMethod.GET) public Set getClients() {  Map agentInfoMap = tunnelServer.getAgentInfoMap();  return agentInfoMap.keySet(); }}

spring-boot-admin-server-ui

该文件建在 Resources.META-INF 下,Admin 会在启动的时候加载该目录下的文件。

2. Resources 目录

  • index.html 覆盖 SBA 原来的首页,在其中添加一个 Arthas 导航

            Spring Boot Admin                    
  • Arthas.html

新建页面,用于显示 Arthas 控制台页面。

这个文件中有两个隐藏文本域,这两个用于连接 Arthas 服务端,在页面加载的时候会自动将 Admin 的 Url 赋值给 Ip。

            Spring Boot Admin                                                
Select Application:
  • Arthas.js 存储页面控制的 js

var registerApplications = null;var applications = null;$(document).ready(function () {    reloadRegisterApplications();    reloadApplications();});/** * 获取注册的arthas客户端 */function reloadRegisterApplications() {    var result = reqSync("/api/arthas/clients", "get");    registerApplications = result;    initSelect("#selectServer", registerApplications, "");}/** * 获取注册的应用 */function reloadApplications() {    applications = reqSync("/api/applications", "get");    console.log(applications)}/** * 初始化下拉选择框 */function initSelect(uiSelect, list, key) {    $(uiSelect).html('');    var server;    for (var i = 0; i < list.length; i++) {        server = list[i].toLowerCase().split("@");        if ("phantom-admin" === server[0]) continue;        $(uiSelect).append("");    }}/** * 重置配置文件 */function release() {    var currentServer = $("#selectServer").text();    for (var i = 0; i < applications.length; i++) {        serverId = applications[i].id;        serverName = applications[i].name.toLowerCase();        console.log(serverId + "/" + serverName);        if (currentServer === serverName) {            var result = reqSync("/api/applications/" +serverId+ "/env/reset", "post");            alert("env reset success");        }    }}function reqSync(url, method) {    var result = null;    $.ajax({        url: url,        type: method,        async: false, //使用同步的方式,true为异步方式        headers: {            'Content-Type': 'application/json;charset=utf8;',        },        success: function (data) {            // console.log(data);            result = data;        },        error: function (data) {            console.log("error");        }    });    return result;}
  • Web-console.js

修改了连接部分代码,参考一下。

var ws;var xterm;/**有修改**/$(function () {    var url = _window.location.href;    var ip = getUrlParam('ip');    var port = getUrlParam('port');    var agentId = getUrlParam('agentId');    if (ip != '' && ip != null) {        $('#ip').val(ip);    } else {        $('#ip').val_(window.location.hostname);    }    if (port != '' && port != null) {        $('#port').val(port);    }    if (agentId != '' && agentId != null) {        $('#selectServer').val(agentId);    }    // startConnect(true);});/** get params in url **/function getUrlParam (name, url) {    if (!url) url = _window.location.href;    name = name.replace(/[\[\]]/g, '\\$&');    var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),        results = regex.exec(url);    if (!results) return null;    if (!results[2]) return '';    return decodeURIComponent(results[2].replace(/\+/g, ' '));}function getCharSize () {    var tempDiv = $('
').attr({'role': 'listitem'}); var tempSpan = $('
').html('qwertyuiopasdfghjklzxcvbnm'); tempDiv.append(tempSpan); $("html body").append(tempDiv); var size = { width: tempSpan.outerWidth() / 26, height: tempSpan.outerHeight(), left: tempDiv.outerWidth() - tempSpan.outerWidth(), top: tempDiv.outerHeight() - tempSpan.outerHeight(), }; tempDiv.remove(); return size;}function getWindowSize () { var e = window; var a = 'inner'; if (!('innerWidth' in window )) { a = 'client'; e = document.documentElement || document.body; } var terminalDiv = document.getElementById("terminal-card"); var terminalDivRect = terminalDiv.getBoundingClientRect(); return { width: terminalDivRect.width, height: e[a + 'Height'] - terminalDivRect.top };}function getTerminalSize () { var charSize = getCharSize(); var windowSize = getWindowSize(); console.log('charsize'); console.log(charSize); console.log('windowSize'); console.log(windowSize); return { cols: Math.floor((windowSize.width - charSize.left) / 10), rows: Math.floor((windowSize.height - charSize.top) / 17) };}/** init websocket **/function initWs (ip, port, agentId) { var protocol= location.protocol === 'https:' ? 'wss://' : 'ws://'; var path = protocol + ip + ':' + port + '/ws?method=connectArthas&id=' + agentId; ws = new WebSocket(path);}/** init xterm **/function initXterm (cols, rows) { xterm = new Terminal({ cols: cols, rows: rows, screenReaderMode: true, rendererType: 'canvas', convertEol: true });}/** 有修改 begin connect **/function startConnect (silent) { var ip = $('#ip').val(); var port = $('#port').val(); var agentId = $('#selectServer').val(); if (ip == '' || port == '') { alert('Ip or port can not be empty'); return; } if (agentId == '') { if (silent) { return; } alert('AgentId can not be empty'); return; } if (ws != null) { alert('Already connected'); return; } // init webSocket initWs(ip, port, agentId); ws.onerror = function () { ws.close(); ws = null; !silent && alert('Connect error'); }; ws.onclose = function (message) { if (message.code === 2000) { alert(message.reason); } }; ws.onopen = function () { console.log('open'); $('#fullSc').show(); var terminalSize = getTerminalSize() console.log('terminalSize') console.log(terminalSize) // init xterm initXterm(terminalSize.cols, terminalSize.rows) ws.onmessage = function (event) { if (event.type === 'message') { var data = event.data; xterm.write(data); } }; xterm.open(document.getElementById('terminal')); xterm.on('data', function (data) { ws.send(JSON.stringify({action: 'read', data: data})) }); ws.send(JSON.stringify({action: 'resize', cols: terminalSize.cols, rows: terminalSize.rows})); window.setInterval(function () { if (ws != null && ws.readyState === 1) { ws.send(JSON.stringify({action: 'read', data: ""})); } }, 30000); }}function disconnect () { try { ws.close(); ws.onmessage = null; ws.onclose = null; ws = null; xterm.destroy(); $('#fullSc').hide(); alert('Connection was closed successfully!'); } catch (e) { alert('No connection, please start connect first.'); }}/** full screen show **/function xtermFullScreen () { var ele = document.getElementById('terminal-card'); requestFullScreen(ele);}function requestFullScreen (element) { var requestMethod = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || element.msRequestFullScreen; if (requestMethod) { requestMethod.call(element); } else if (typeof window.ActiveXObject !== "undefined") { var wscript = new ActiveXObject("WScript.Shell"); if (wscript !== null) { wscript.SendKeys("{F11}"); } }}
  • 其他文件

    • jquery-3.3.1.min.js 新加 Js

    • copy 过来的 js

    • popper-1.14.6.min.js

    • web-console.js

    • xterm.css

    • xterm.js

  • bootstrap.yml

# arthas端口arthas:  server:    port: 9898

这样子,admin 端的配置完成了。

客户端配置

  • 在配置中心加入配置

#arthas服务端域名arthas.tunnel-server = ws://admin域名/ws#客户端id,应用名@随机值,js会截取前面的应用名arthas.agent-id = ${spring.application.name}@${random.value}#arthas开关,可以在需要调式的时候开启,不需要的时候关闭spring.arthas.enabled = false
  • 需要自动 Attach 的应用中引入 Arthas-spring-boot-starter 需要对 Starter 进行部分修改,要将注册 Arthas 的部分移除,下面是修改后的文件。

这里是将修改后的文件重新打包成 Jar 包,上传到私服,但有些应用会有无法加载 ArthasConfigMap 的情况,可以将这两个文件单独放到项目的公共包中。

@EnableConfigurationProperties({ ArthasProperties.class })public class ArthasConfiguration { private static final Logger logger = LoggerFactory.getLogger(ArthasConfiguration.class); @ConfigurationProperties(prefix = "arthas") @ConditionalOnMissingBean @Bean public HashMap arthasConfigMap() {  return new HashMap(); }}
@ConfigurationProperties(prefix = "arthas")public class ArthasProperties { private String ip; private int telnetPort; private int httpPort; private String tunnelServer; private String agentId; /**  * report executed command  */ private String statUrl; /**  * session timeout seconds  */ private long sessionTimeout; private String home; /**  * when arthas agent init error will throw exception by default.  */ private boolean slientInit = false; public String getHome() {  return home; } public void setHome(String home) {  this.home = home; } public boolean isSlientInit() {  return slientInit; } public void setSlientInit(boolean slientInit) {  this.slientInit = slientInit; } public String getIp() {  return ip; } public void setIp(String ip) {  this.ip = ip; } public int getTelnetPort() {  return telnetPort; } public void setTelnetPort(int telnetPort) {  this.telnetPort = telnetPort; } public int getHttpPort() {  return httpPort; } public void setHttpPort(int httpPort) {  this.httpPort = httpPort; } public String getTunnelServer() {  return tunnelServer; } public void setTunnelServer(String tunnelServer) {  this.tunnelServer = tunnelServer; } public String getAgentId() {  return agentId; } public void setAgentId(String agentId) {  this.agentId = agentId; } public String getStatUrl() {  return statUrl; } public void setStatUrl(String statUrl) {  this.statUrl = statUrl; } public long getSessionTimeout() {  return sessionTimeout; } public void setSessionTimeout(long sessionTimeout) {  this.sessionTimeout = sessionTimeout; }}
  • 实现开关效果

为了实现开关效果,还需要一个文件用来监听配置文件的改变。

我这里使用的是在 SBA 中改变环境变量,对应服务监听到变量改变,当监听 spring.arthas.enabled 为 true 的时候,注册 Arthas,到下面是代码。

@Componentpublic class EnvironmentChangeListener implements ApplicationListener {    @Autowired    private Environment env;    @Autowired    private Map arthasConfigMap;    @Autowired    private ArthasProperties arthasProperties;    @Autowired    private ApplicationContext applicationContext;    @Override    public void onApplicationEvent(EnvironmentChangeEvent event) {        Set keys = event.getKeys();        for (String key : keys) {            if ("spring.arthas.enabled".equals(key)) {                if ("true".equals(env.getProperty(key))) {                    registerArthas();                }            }        }    }    private void registerArthas() {        DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();        String bean = "arthasAgent";        if (defaultListableBeanFactory.containsBean(bean)) {            ((ArthasAgent)defaultListableBeanFactory.getBean(bean)).init();            return;        }        defaultListableBeanFactory.registerSingleton(bean, arthasAgentInit());    }    private ArthasAgent arthasAgentInit() {        arthasConfigMap = StringUtils.removeDashKey(arthasConfigMap);        // 给配置全加上前缀        Map mapWithPrefix = new HashMap(arthasConfigMap.size());        for (Map.Entry entry : arthasConfigMap.entrySet()) {            mapWithPrefix.put("arthas." + entry.getKey(), entry.getValue());        }        final ArthasAgent arthasAgent = new ArthasAgent(mapWithPrefix, arthasProperties.getHome(),                arthasProperties.isSlientInit(), null);        arthasAgent.init();        return arthasAgent;    }}

结束

到此可以愉快的在 SBA 中调式应用了,看看最后的页面。

  • 调式流程

流程如下:

  1. 开启 Arthas

  2. 在 Select Application 中选择应用

  3. Connect 连接应用

  4. DisConnect 断开应用

  5. Release 释放配置文件

一些缺陷:

  • 使用 jar 包的方式引入应用,具有一定的侵略性,如果 Arthas 无法启动,会导致应用也无法启动。

  • 如果使用 Docker,需要适当调整 JVM 内存,防止开启 Arthas、调试的时候,内存炸了。

  • 没有使用 SBA 插件的方式集成如上集成仅供参考,请根据自己企业的情况来集成。

关于"如何将Arthas集成进Spring Boot监控平台中"这篇文章就分享到这里了,希望以上内容可以对大家有一定的帮助,使各位可以学到更多知识,如果觉得文章不错,请把它分享出去让更多的人看到。

文件 应用 配置 时候 插件 版本 页面 客户 客户端 方式 目录 开发 服务 两个 代码 篇文章 调式 部分 项目 平台 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技园 上海挚品互联网科技有限公司 互联网科技行业发展 小马过河互联网科技 上海万想互联网科技 大白互联网科技 互联网养老 桔豆科技 互联网 科技服务平台 美国互联网前沿科技 济南华为互联网科技产业总部基地 小米互联网科技园设置 银川新华互联网科技职业技术培训学校图片 互联网科技项目计划书材料类 注册互联网科技公司经营范围大全 科技赋能视角下互联网保险发展 滨湖区互联网智能科技产品有哪些 有人说阿里系是互联网科技企业 北京互联网科技类企业分布 长沙易易互联网科技有限公司 科技互联网专业务虚会材料 国智互联网科技 时间互联网络科技 青海省互联网新科技 成都互联网科技公司100强 搜狐科技互联网 坤平互联网科技 徐州市互联网科技有限公司 互联网公司高科技产品 广州智享互联网科技 恒生互联网科技趋势
0