arthas 源码分析 篇一 结构篇

Posted 乾坤瞬间

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了arthas 源码分析 篇一 结构篇相关的知识,希望对你有一定的参考价值。

框架

前期准备

1、 github源码地址
1.1、 官方文档
2、 下载下来 idea打开。
3、选择idea build操作。如果出现cannot resolve symbol。需要重新建立索引

重建索引资料点击查看

此时就可以看到正常的项目

模块 - 1、arthas-agent

该模块主要是基于javaagent技术,让用户通过两种方式干预java应用过程中对 java class字节码进行crud的一种操作。
该操作是非常危险的,但往往伴随着

a) 通过 附属agent包方式

-javaagent:/xxx/arthas-agent.jar

该式在用户启动目标jar包的过程中附属 arthas-agent.jar 包,在agentjar包中根据Premain-Class指定的类中premain方法动态修改字节码。

b) 通过 attach方式,在agentjar包中根据Agent-Class指定的类中定义的agentmain方法中作为主入口加载并在运行时【实时】修改字节码。

后面会详细讲解 agent内容

2、模块 - arthas-agent-attach

attach包 使用 ArthasBootstrap启动类【core模块中】,
通过 ArthasBootstrap.getInstance(inst);实例化一个arhtas的启动实例,并bind绑定端口启动一个arthas-server实例。在实例化server的过程中,可以分多个阶段执行。代码分析后续再聊。其功能就是为了隔离生成自己的classLoader,并启动一个一个代理服务【agent server】,代理服务目前划分了三种类型
1)HttpTelnetTermServer
2)HttpTermServer
这两个服务,根据用户的配置自动生成,并通过shell窗口界面与其进行交互。server把用户的执行操作,通过attach远程方式直接与被监控的java 虚拟机进行交互。后面用几章节去研究探讨一下。

3、模块 arthas-spring-boot-starter

顾名思义,就是一个springboot项目的starter,可以方便地继承进spring boot 项目,在依赖了该项目之后会自动在后端启动一个
ArthasAgent,参考一下自动配置类ArthasConfiguration

4、模块 arthas-vmtool

如果读者使用过arthas ,那么想必对arthas 中的 vmtool 命令行操作还是比较深刻的吧。她能帮我们分析被检测jvm中指定类实例的内存大小以及实例个数。对于如何实现内存统计与计算。也可以通过该模块进行深入探索和学习。先透露一点点,其原理是使用native api写的。如果要啃下这块骨头,那么就需要有c语言基础。看不懂的跑跑几个用例就行。代码在VmToolTest中。
这里根据VmToolMXBean接口,我们可以总结出vmtool的几个功能
a) forceGc 强制gc,源码中也给出了jvmti工具中强制gc的来源https://docs.oracle.com/javase/8/docs/platform/jvmti/jvmti.html#ForceGarbageCollection
b)getInstances(Class klass); 给定指定的class,返回对应的实例。
getInstances(Class klass, int limit); 可能实例太多,比如String.class ,用户可以自定义限制数量。
c) sumInstanceSize: 统计某个class在jvm中当前所有存活实例的总占用内存,单位:Byte
d)getInstanceSize(Object instance); 获取某个实例的占用内存,单位:Byte
e)getAllLoadedClasses() 获取所有已加载的类
这里有个疑问,在哪个领域利用了vmtool,通过调用链发现ProfilerCommand、VmToolCommand 使用了vmtool,通过命名我们很容易发现,作者使用了命令模式。该模式常常用于根据用户的输入来执行相关的操作【action/handler】。后面会单独讲解,各种命令的操作。

5、模块 boot

待写

6、core

待写

7、其他

这里是指在arthas核心功能的外围封装的让用户使用起来更加方便的模块。这些模块并不影响核心模块的,只是在核心之上更多元化,满足用户的各种应用层面上的需求,比如弄个web界面,比命令行使用起来更方便。

Spring Boot v2.4.4源码解析配置文件加载篇一 #2.4版本与2.3版本差异

Spring Boot 2.4版本与2.3版本处理配置文件差异

一、概述

Spring Boot v2.4.4源码解析(三)事件机制篇一这篇博客中可以看出,在Spring Boot启动过程中包含很多关键时间节点,EventPublishingRunListener会将这些时间节点封装成事件并通过SimpleApplicationEventMulticaster广播出去。
在这些时间节点中,有一个environmentPrepared,表示环境已经准备好,如果需要对环境做定制化操作的组件,可以监听该事件。Spring Boot正是在该时间节点将配置文件加载到环境EnvironmentpropertySources属性中。

在事件广播器SimpleApplicationEventMulticaster 广播事件方法multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) 内部打断点,断点条件是event instanceof org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent,表示发布ApplicationEnvironmentPreparedEvent事件时进入断点。

然后在Variables中添加getApplicationListeners(event, type)查看有哪些事件监听器监听ApplicationEnvironmentPreparedEvent事件。
在Spring Boot 2.3版本和2.4版本中会发现有如下ApplicationEnvironmentPreparedEvent事件监听器:

二、变更原因

注意,2.3版本中ConfigFileApplicationListener和2.4版本中EnvironmentPostProcessorApplicationListener正是处理配置文件的事件监听器。
为什么Spring Boot 2.4版本要放弃ConfigFileApplicationListener而使用EnvironmentPostProcessorApplicationListener处理配置文件加载呢?

Config file processing in Spring Boot 2.4这篇博客给出了答案。

总结主要原因如下:
  • 支持Kubernetes卷挂载配置;
    卷挂载配置是 Kubernetes 的一个流行功能,其中ConfigMap可以将环境配置信息和容器镜像解耦便于应用配置的修改。
    这块内容可以参考K8s 集群使用 ConfigMap 优雅加载 Spring Boot 配置文件
  • ConfigFileApplicationListener本身存在一些问题;
    Config file processing in Spring Boot 2.4文章中, 作者将ConfigFileApplicationListener 比喻为陷阱冒险(trap adventures),并不是指其代码质量不高,或者测试覆盖不全面,而是指很难添加新功能。
    在处理特定profile配置文件(主要YAML文件)会遇到两个问题:
    • 可以在这些配置文件中激活附加的profiles;
    • 很难弄清楚配置文件的添加顺序;

例如,在YAML文件中可以包含多个逻辑文件,这些逻辑文件之间用---分隔开。考虑如下包含三个逻辑文件的YAML文件:

security.user.password: usera
---
spring.profiles: local
security.user.password: userb
runlocal: true
---
spring.profiles: prod
spring.profiles.include: local
security.user.password: userc

在Spring Boot 2.3中添加VM参数-Dspring.profiles.active=prod并启动,获取security.user.passwordrunlocal配置,会发现其值分别为userbtrue。是不是感觉有点奇怪,在处理中间文件时,该文件的profiles还未被激活,但是最终该文件还是被包含进来了。
所以在Spring Boot 2.4中对加载properties和YAML配置文件方式作出了两个重大改变,旨在简化和合理化加载外部配置的方式:

  • 将按照定义顺序加载文件;
  • 不能在特定profile文件中激活profiles;

三、主要变更点

1. YAML 多逻辑文件顺序变更

对于Spring Boot 2.3及更早版本, YAML中的多个逻辑文件基于profile激活顺序添加,而在2.4版本中,则按照文件声明顺序添加。
例如,对于如下YAML文件:

test: "value"
---
test: "overridden-value"

最终test属性值为overridden-value

2. 增加 Properties 文件多逻辑文件

在 Spring Boot 2.4 中,在Java properties文件中可以使用和YAML类似的多逻辑文件,多个逻辑文件之间用#---隔开。
例如:

test=value
#---
test=overridden-value

3. Jar 包外部配置文件和内部配置文件顺序变更

在Spring Boot 2.3及更早版本中,Jar包外部的application.properties文件不会覆盖Jar包内部的application-<profile>.properties文件。
从Spring Boot 2.4开始,外部文件总是覆盖Jar包内部文件(application-<profile>.properties文件或者其他文件);
具体配置文件加载顺序可以参考Application properties outside of jar should take precedence over profile-specific variants inside the jarSpring Boot Reference Documentation 2.3.6.RELEASESpring Boot Reference Documentation 2.4.1

Spring Boot 2.3及更早版本配置文件优先级(从上到下优先级降低):

  • Jar包外部的application-{profile}.properties文件及application-{profile}.yml
  • Jar包内部的application-{profile}.properties文件及application-{profile}.yml
  • Jar包外部的application.propertiesapplication.yml;
  • Jar包内部的application.propertiesapplication.yml;

Spring Boot 2.4配置文件优先级(从上到下优先级降低):

  • Jar包外部的application-{profile}.properties文件及application-{profile}.yml
  • Jar包外部的application.propertiesapplication.yml;
  • Jar包内部的application-{profile}.properties文件及application-{profile}.yml
  • Jar包内部的application.propertiesapplication.yml;

4. spring.profiles 配置项迁移

在Spring Boot 2.3及更早版本中,可以在YAML的多逻辑文件中使用spring.profiles指定该配置文件生效的profile,在2.4版本中,将该配置项迁移到spring.config.activate.on-profile

5. Profile 激活

Spring Boot中可以使用spring.profiles.active配置项激活特定的profiles,例如可以使用如下命令行激活prod

$ java -jar myapp.jar --spring.profiles.active=prod

当然也可以在application.properties或者application.yaml文件中配置该配置项,但是在Spring Boot 2.4中,不能在特定profile文件中设置该配置项值。也就是说,在Spring Boot 2.4中,配置项spring.config.activate.on-profilespring.profiles.active不能同时设置。类似地,配置项spring.config.activate.on-profilespring.profiles.include也不能同时设置。
例如,如下配置将会报错:

spring:
  config:
    activate:
      on-profile: "prod"
  profiles:
    include: "metrics"

为什么会增加这样一个限制呢?
加了这个限制后,同一个文件中on-profile条件只会计算一次,前文这个问题就会解决。

6. Profile 组

由于Spring Boot 2.4不能同时设置spring.config.activate.on-profilespring.profiles.active,也不能同时设置spring.config.activate.on-profilespring.profiles.include,那对于2.3版本使用spring.profiles + spring.profiles.include扩展激活profiles这种情况怎么兼容呢?
例如,如下application.yaml

spring.profiles: "debug"
spring.profiles.include: "debugdb,debugcloud"

如果迁移到Spring Boot 2.4 application.yaml变更为:

spring:
  config:
    activate:
      on-profile: "debug"
  profiles:
    include: "debugdb,debugcloud"

但是不能同时设置spring.config.activate.on-profilespring.profiles.include
为了解决这类问题,Spring Boot 2.4 提出Profile组概念,Profile组可以完成这样一件事:如果profile 'x’处于激活状态,profiles ‘y’ 和 ‘z’ 也处于激活状态。配置项格式为spring.profiles.group.<source>,例如,上面的配置可以这样写:

spring:
  profiles:
    group:
      "debug": "debugdb,debugcloud"

同样,也不能同时设置配置项spring.profile.groupspring.config.activate.on-profile的值。

7. 导入附加配置

配置文件加载成功后,会以OriginTrackedMapPropertySource形式存在EnvironmentPropertySources属性中。可以在Spring Boot启动类中增加如下代码,查看引入了哪些配置文件:

ConfigurableApplicationContext context = SpringApplication.run(DemoApplication.class, args);
MutablePropertySources propertySources = context.getEnvironment().getPropertySources();
propertySources.forEach( s -> {
    if(s instanceof OriginTrackedMapPropertySource){
        System.err.println(s);
    }
});

在Spring Boot 2.3版本中,配置文件默认搜索顺序为(从上到下优先级降低):

  • file:./config/
  • file:./config/*/
  • file:./
  • classpath:/config/
  • classpath:/

在Jar包内部resources目录,Jar包内部resources/config目录,Jar包当前目录,Jar包当前/config目录,Jar包当前/config/fsx目录下面分别放置application.yml,然后以java -jar target/demo-0.0.1-SNAPSHOT.jar命令行运行该Jar包,会发现如下输出:

OriginTrackedMapPropertySource {name='applicationConfig: [file:./config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [config/fsx/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [file:./application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}

搜索配置文件名称默认为application,可以通过配置项spring.config.name配置,也可以使用配置项spring.config.location明确指定配置文件搜索顺序(多个文件或者目录质检用逗号隔开)。
例如,以命令行java -jar target/demo-0.0.1-SNAPSHOT.jar --spring.config.location=classpath:/,file:./ 启动时,输出如下:

OriginTrackedMapPropertySource {name='applicationConfig: [file:./application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}

可以通过spring.config.additional-location在默认的搜索顺序上增加搜索路径,该配置项的值优先默认的搜索顺序。
在Jar包路径/custom-config目录下添加application.yml文件,并以命令行java -jar target/demo-0.0.1-SNAPSHOT.jar --spring.config.additional-location=file:./custom-config启动时,输出如下:

OriginTrackedMapPropertySource {name='applicationConfig: [file:custom-config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [file:./config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [config/fsx/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [file:./application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/config/application.yml]'}
OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application.yml]'}

此时,配置文件默认搜索顺序为(从上到下优先级降低):

  • file:./custom-config/
  • classpath:custom-config/
  • file:./config/
  • file:./config/*/
  • file:./
  • classpath:/config/
  • classpath:/

但有一个问题,这几个配置项必须配置为环境属性,例如操作系统环境变量,系统变量,命令行参数,不能中application.properties或者 application.yml中指定,且文件名仅限于.properties, .yaml, 或者.yml
在Spring Boot 2.4 中新增了配置项spring.config.import,可以使用该配置项导入附加配置, 且该配置项可以在application.properties或者 application.yml中设置,导入文件添加顺序在定义该配置项文件之后。

8. 卷挂载配置树

如果使用spring.config.import导入附加配置时,该配置项的值没有前缀,就认为导入常规文件或者文件夹,如果以configtree作为前缀,则告诉Spring Boot该路径为Kubernetes风格的卷加载配置树。
例如,在application.properties中定义如下配置项:

spring.config.import=configtree:/etc/config

如果有如下挂载内容:

etc/
 +- config/
     +- my/
     |  +- application
     +- test

那么,最终会在Environment中增加两个属性:my.applicationtest,其值分别为/etc/config/my/application/etc/config/test

9. 云平台激活

如果只想卷挂载配置树在指定的云平台下才激活,可以使用spring.config.activate.on-cloud-platform配置。该配置和spring.config.activate.on-profile相似,只不过使用的是云平台的值,而非profile名称。

例如,上面配置只在Kubernetes下部署才生效,可以如下配置

spring.config.activate.on-cloud-platform=kubernetes
spring.config.import=configtree:/etc/config

10. 第三方配置源

配置项spring.config.import的值完全是可拔插的,我们可以自定义扩展。可以想象第三方Jar来支持诸如archaius://...vault://...zookeeper://...之类的配置路径。
这部分源码可以参考org.springframework.boot.context.config包下的ConfigDataLocationResolverConfigDataLoader类。

11. 仍然使用Spring Boot 2.3配置方式

如果Spring Boot 2.4这种变更让你很不爽,想在Spring Boot 2.4 下使用原有配置文件加载方式, 可以在application.properties或者 application.yml文件中将配置项spring.config.use-legacy-processing置为true,那么Spring Boot将沿用2.3版本方式处理配置文件。
Spring Boot版本迁移时,一些配置项的变更可能对我们造成困惑,我们可以在pom文件中引入spring-boot-properties-migrator模块,引入后在项目启动时,当你的配置文件中存在被识别的已经移除的属性时,将会日志打印提示。

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-properties-migrator</artifactId>
	<scope>runtime</scope>
</dependency>

版本迁移完成后,再将该模块从pom文件中移除。

参考

以上是关于arthas 源码分析 篇一 结构篇的主要内容,如果未能解决你的问题,请参考以下文章

测试需要了解的技术之基础篇一

Arthas,7000 字入门篇

Spring Boot v2.4.4源码解析配置文件加载篇一 #2.4版本与2.3版本差异

Arthas--Java在线分析诊断工具(阿尔萨斯)

Java面试题-基础篇一

Java面试题-基础篇一