关于java:SpringBoot-Admin20-集成-Java-诊断神器-Arthas-实践

28次阅读

共计 6391 个字符,预计需要花费 16 分钟才能阅读完成。

简介:我的项目最后应用 Arthas 次要有两个目标:1. 通过 arthas 解决实现测试环境、性能测试环境以及生产环境性能问题剖析工具的问题。2. 通过应用 jad、mc、redefine 性能组合实现生产环境局部节点代码热更新的能力。

作者 | sparrow
起源 | 阿里巴巴云原生公众号

本文来自 Arthas 2021 年 3 月征文投稿,4 月有奖征文参加形式可见文末。

我的项目最后应用 Arthas 次要有两个目标:

通过 arthas 解决实现测试环境、性能测试环境以及生产环境性能问题剖析工具的问题。
通过应用 jad、mc、redefine 性能组合实现生产环境局部节点代码热更新的能力。
技术选型相干
因为公司还未能建设起较为对立的生产微服务配置以及状态治理的能力,各自零碎的研发运维较为独立。当初我的项目应用了 Spring Cloud 以及 Eureka 的框架结构,和 SBA 的根底撑持能力较为匹配,同时,SBA 曾经能够提供服务感知,日志级别配置管理,以及基于 actuator 的 JVM、Spring 容器的泛滥治理插件,能够满足根底应用的需要。

在调研期间,Arthas 整体版本为 3.4.5,提供了基于 Webconsole 的 Tunner Server 模式,通过后面链接文章曾经实际,与 SBA 曾经能够实现集成。因为我的项目自身没有历史包袱,在理论集成的过程中采纳了 SBA 2.0 版本以提供更多的治理性能和图形界面能力。其余长处:

web console 界面嵌入 SBA 整体明码登录和网页权限治理,实现登陆 SBA 后才能够应用相干 arthas web console 的性能。
基于 SBA 客户端依赖的 jolokia-core 凋谢指标服务过程的 jmx 治理,通过实现 jmx 接口复用 SBA 的相干操作界面,缩小前端界面开发能力的要求。
整体构造

几个关键点,应用 JVM 内置 Arthas Spring Boot 插件,参考工商银行的模式建设欠缺的客户端下载以及批改脚本实现近程管制。内置计划工作开发量小,只须要集成相干的开源组件即可实现相干的近程应用的模式并兼顾平安。工银的计划大而全适宜整体架构布局后配置专有研发团队之城。内置计划同时蕴含通过 JMX 的启停操作(基于 3.4.5 的 Spring Boot 插件无奈取得相干句柄,临时无奈实现),默认不启动。通过近程 JMX 开明后,JVM 新增相干线程 8 个,新增虚拟机内存 30MB 左右,和本文参考的 SBA1.0 计划雷同,须要思考在线开启前 JVM 内存是否能够反对。

实现成果
SBA 2.0 最大的不便就是提供了配置化链接内部网页的能力,同时如果网页实现在以后 JVM 过程,能够实现 Spring-Security 的本地权限治理,在生产环境下只有在登录 SBA 后能力应用相干集成的 arthas 性能。

登录界面

外嵌连贯地位

JMX 的应用

跳转 arthas web console

革新计划
参考原文 -SpringBoot Admin 集成 Arthas 实际中实现的几个步骤。

  1. 整体工程构造

整体工程批改自 SBA 开源我的项目的 example 工程,具体应用 custom-ui 的工程链接为:[_[spring-boot-admin-sample-custom-ui]_](https://github.com/codecentri… arthas web console 的全副动态文件,通过 Maven Resource 的指定配置打入指定目录,实现 SBA 启动时的自定义加载。maven resource 配置 – 下:

<resource>

            <directory>static</directory>
            <targetPath>${project.build.directory}/classes/META-INF/spring-boot-admin-server-ui/extensions/arthas
            </targetPath>
            <filtering>false</filtering>
        </resource>

最终构建的 jar 中 META-INFO 中蕴含相干的文件即可在 SBA 自带的 tomcat 启动后加载到相干的动态资源,最初的 url 和自定义实现的 arthas console 配置的内部 URL 对应即可。

  1. 内部链接配置
    SBA 2.0 开始曾经应用 vue 全家桶了,扩大集成均比拟不便。其中,官网文档给出了外嵌连贯的配置形式:[_[Linking / Embedding External Pages]_](https://codecentric.github.io…

参考 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[]
  1. 对应 Spring MVC controller 实现
    参考援用原实现的 SBA 集成局部,该局部次要批改实现如下性能:

实现 tunnel server 曾经加载实例列表的刷新并展现到前段 AgentID 框供选择点击链接。
实现自定义 IP 地址的刷新(解决生产环境双生产 IP 和运维段 IP 不统一的问题)。

  1. Arthas Spring Boot 插件批改和配置
    参考援用原实现的 SBA 集成中插件批改以及客户端配置 application.yml。

对原版 Spring boot 插件批改次要在于原有插件是通过 Spring 的 @ConditionalOnMissingBean 实现主动加载。

批改次要是通过批改这部分实现通过配置文件默认不启动,而后应用时通过近程启动相干 agent 线程。

  1. 基于 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;
   }

   @ManagedOperation(description = "启动 Arthas agent")
   public Boolean startArthasAgent() {DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
       String bean = "arthasAgent";
       if (defaultListableBeanFactory.containsBean(bean)) {((ArthasAgent) defaultListableBeanFactory.getBean(bean)).init();
           return true;
       }
       defaultListableBeanFactory.registerSingleton(bean, arthasAgentInit());
       return true;
   }

   @ManagedOperation(description = "敞开 Arthas agent,暂未实现")
   public Boolean stopArthasAgent() {
       // TODO 无奈获取自定义 tmp 文件夹加载的 classLoader,因而无奈获取到 com.taobao.arthas.core.server.ArthasBootstrap 类并调用 destroy 办法
       DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
       String bean = "arthasAgent";
       if (defaultListableBeanFactory.containsBean(bean)) {defaultListableBeanFactory.destroySingleton(bean);
           return true;
       } else {return false;}
   }

}
理论应用
治理工程投产后,屡次在生产环境用于问题排查和代码热修复。性能问题次要用于性能流控组件以及灰度公布相干配置参数的在线验证和 debug。

代码热加载相干初期通过 jad+mc 的形式进行操作,后续发现 jad 在局部代码上因环境配置以及 jvm 问题产生反编译代码不统一的状况,后续通过 maven 打包部署应用程序 source 压缩包的形式解决,间接应用和利用 jar 同版本构建的 source 进行批改更加牢靠。整体计划在治理较为严格的生产环境提供了无效的性能剖析以及热修复的能力。

遗留问题
现有官网提供的 com.taobao.arthas.agent.attach.ArthasAgent 中启动 arthas agent 的客户端应用的 arthasClassLoader 和 bootstrapClass 均为办法内的长期变量,内部无奈获取相干句柄实现通过 bootstrapClass 敞开 arthas agent 的性能;长期解决方案为通过 JMX 启动后,在 web console 连贯应用后,应用 stop 命令实现目标过程中 arthas agent 的敞开。

现有字节码加载工具能够很好的实现外部类,公有类的在线热部署替换,同时经测试能够兼容 SkyWalk8.x 版本的 javaagent 插件,然而在测试环境因为配置有 jacoco 覆盖度采集插件与 Arthas 字节码产生了不兼容的状况,在局部环境应用时须要先敞开对应的 agent 后能力失常应用 arthas 的相干性能。

欢送登陆 start.aliyun.com 知口头手实验室体验 Arthas 57 个入手试验:

Arthas 试验预览
原文链接
本文为阿里云原创内容,未经容许不得转载。

正文完
 0