=========================================================================
SBA 2.0 最大的方便就是提供了配置化链接外部网页的能力,同时如果网页实现在当前 JVM 进程,可以实现 Spring-Security 的本地权限管理,在生产环境下只有在登录 SBA 后才能使用相关集成的 arthas 功能。
- 登录界面
- 外嵌连接位置
- JMX 的使用
- 跳转 arthas web console
=========================================================================
参考原文 -SpringBoot Admin 集成 Arthas 实践中实现的几个步骤。
整体工程修改自 SBA 开源项目的 example 工程,具体使用 custom-ui 的工程链接为:[spring-boot-admin-sample-custom-ui]_,_红色框的部分是 arthas web console 的全部静态文件,通过 Maven Resource 的指定配置打入指定目录,实现 SBA 启动时的自定义加载。maven resource 配置–下:
static
${project.build.directory}/classes/META-INF/spring-boot-admin-server-ui/extensions/arthas
false
最终构建的 jar 中 META-INFO 中包含相关的文件即可在 SBA 自带的 tomcat 启动后加载到相关的静态资源,最后的 url 和自定义实现的 arthas console 配置的外部 URL 对应即可。
SBA 2.0 开始已经使用 vue 全家桶了,扩展集成均比较方便。其中,官方文档给出了外嵌连接的配置方式:[Linking / Embedding External Pages]。
参考 sba example 工程的 application.yml 配置即可:
tag::customization-external-views[]
spring:
boot:
admin:
ui:
external-views:
- label: “Arthas Console”
url: http://21.129.49.153:8080/
order: 1900
end::customization-external-views[]
3. 对应 Spring MVC controller 实现
参考引用原实现的 SBA 集成部分,该部分主要修改实现如下功能:
-
实现 tunnel server 已经加载实例列表的刷新并展示到前段 AgentID 框供选择点击链接。
-
实现自定义 IP 地址的刷新(解决生产环境双生产 IP 和运维段 IP 不一致的问题)。
参考引用原实现的 SBA 集成中插件修改以及客户端配置 application.yml。
对原版 Spring boot 插件修改主要在于原有插件是通过 Spring的@ConditionalOnMissingBean 实现自动加载。
修改主要是通过修改这部分实现通过配置文件默认不启动,然后使用时通过远程启动相关 agent 线程。
5. 基于 Spring Actuator 的 JMX 实现
SBA client 在 maven 引入中会默认引入 jolokia-core.jar,如果没有因为 SBA client 依赖可以自行引入该包,可以实现通过 actuator 开放基于 http 的 jmx 操作能力和 SBA 控制台的相关功能无缝配合。
application.yml 中开放 management 相关配置,根据自身环境情况,也可以开在客户端侧开启 Spring security 认证,SBA 也可以很好的支持通过服务发现实现密码保护 actuator 端点的访问。
#放开management
management:
endpoints:
web:
exposure:
这里用* 代表暴露所有端点只是为了观察效果,实际中按照需进行端点暴露
include: “*”
exclude: env
endpoint:
health:
详细信息显示给所有用户。
show-details: ALWAYS
health:
status:
http-mapping:
自定义健康检查返回状态码对应的 http 状态码
FATAL: 503
JMX 实现参考原文中 EnvironmentChangeListener 的实现思路,基于 Spring 的 JMX 注解实现即可。
@Component
@ManagedResource(objectName = “com.ArthasAgentManageMbean:name=ArthasMbean”, description = “Arthas远程管理Mbean”)
public class ArthasMbeanImpl {
@Autowired
private Map<String, String> arthasConfigMap;
@Autowired
private ArthasProperties arthasProperties;
@Autowired
private ApplicationContext applicationContext;
/**
-
初始化
-
@return
*/
private ArthasAgent arthasAgentInit() {
arthasConfigMap = StringUtils.removeDashKey(arthasConfigMap);
// 给配置全加上前缀
Map<String, String> mapWithPrefix = new HashMap<String, String>(arthasConfigMap.size());
for (Map.Entry<String, String> 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;
}
@ManagedOperation(description = “获取配置Arthas Tunnel Server地址”)
public String getArthasTunnelServerUrl() {
return arthasProperties.getTunnelServer();
}
@ManagedOperation(description = “设置Arthas Tunnel Server地址,重新attach后生效”)
@ManagedOperationParameter(name = “tunnelServer”, description = “example:ws://127.0.0.1:7777/ws”)
public Boolean setArthasTunnelServerUrl(String tunnelServer) {
if (tunnelServer == null || tunnelServer.trim().equals(“”) || tunnelServer.indexOf(“ws://”) < 0) {
return false;
}
arthasProperties.setTunnelServer(tunnelServer);
return true;
}
@ManagedOperation(description = “获取AgentID”)
public String getAgentId() {
return arthasProperties.getAgentId();
}
@ManagedOperation(description = “获取应用名称”)
public String getAppName() {
return arthasProperties.getAppName();
}
@ManagedOperation(description = “获取ArthasConfigMap”)
public HashMap<String, String> getArthasConfigMap() {
return (HashMap) arthasConfigMap;
}
@ManagedOperation(description = “返回是否已经加载Arthas agent”)
public Boolean isArthasAttched() {
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
String bean = “arthasAgent”;
if (defaultListableBeanFactory.containsBean(bean)) {
return true;
}
return false;
总结
本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!
MySQL50道高频面试题整理:
tory) applicationContext.getAutowireCapableBeanFactory();
String bean = “arthasAgent”;
if (defaultListableBeanFactory.containsBean(bean)) {
return true;
}
return false;
总结
本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!
MySQL50道高频面试题整理:
[外链图片转存中…(img-ixCtUC14-1718777412203)]