SpringBoot-Admin集成诊断利器Arthas实践

Select Application: Connect Disconnect Release
  • 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(“” + server[0] + “”);
    }
    }
    /
    *

  • 重置配置文件
    */
    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, ‘\KaTeX parse error: Expected 'EOF', got '&' at position 1: &̲'); var regex =…)’),
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 = s p r i n g . a p p l i c a t i o n . n a m e @ {spring.application.name}@ 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<String, String> arthasConfigMap() {
return new HashMap<String, String>();
}
}

@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,到下面是代码。

@Component
public class EnvironmentChangeListener implements ApplicationListener {
@Autowired
private Environment env;
@Autowired
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。

面试了阿里,滴滴,网易,蚂蚁,最终有幸去了网易【面试题分享】

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
community.csdnimg.cn/images/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

总结

无论是哪家公司,都很重视高并发高可用的技术,重视基础,重视JVM。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,是不是能真的得到锻炼。其实我写了这么多,只是我自己的总结,并不一定适用于所有人,相信经过一些面试,大家都会有这些感触。

最后我整理了一些面试真题资料,技术知识点剖析教程,还有和广大同仁一起交流学习共同进步,还有一些职业经验的分享。

[外链图片转存中…(img-F8ADjb39-1713309367583)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值