前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >聊聊arthas的spring-boot-starter

聊聊arthas的spring-boot-starter

原创
作者头像
code4it
发布2024-02-19 09:20:16
1260
发布2024-02-19 09:20:16
举报
文章被收录于专栏:码匠的流水账码匠的流水账

本文主要研究一下arthas的spring-boot-starter

ArthasConfiguration

arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/ArthasConfiguration.java

代码语言:javascript
复制
@ConditionalOnProperty(name = "spring.arthas.enabled", matchIfMissing = true)
@EnableConfigurationProperties({ ArthasProperties.class })
public class ArthasConfiguration {
	private static final Logger logger = LoggerFactory.getLogger(ArthasConfiguration.class);

	@Autowired
	ConfigurableEnvironment environment;

	/**
	 * <pre>
	 * 1. 提取所有以 arthas.* 开头的配置项,再统一转换为Arthas配置
	 * 2. 避免某些配置在新版本里支持,但在ArthasProperties里没有配置的情况。
	 * </pre>
	 */
	@ConfigurationProperties(prefix = "arthas")
	@ConditionalOnMissingBean(name="arthasConfigMap")
	@Bean
	public HashMap<String, String> arthasConfigMap() {
		return new HashMap<String, String>();
	}

	@ConditionalOnMissingBean
	@Bean
	public ArthasAgent arthasAgent(@Autowired @Qualifier("arthasConfigMap") Map<String, String> arthasConfigMap,
			@Autowired ArthasProperties arthasProperties) throws Throwable {
        arthasConfigMap = StringUtils.removeDashKey(arthasConfigMap);
        ArthasProperties.updateArthasConfigMapDefaultValue(arthasConfigMap);
        /**
         * @see org.springframework.boot.context.ContextIdApplicationContextInitializer#getApplicationId(ConfigurableEnvironment)
         */
        String appName = environment.getProperty("spring.application.name");
        if (arthasConfigMap.get("appName") == null && appName != null) {
            arthasConfigMap.put("appName", appName);
        }

		// 给配置全加上前缀
		Map<String, String> mapWithPrefix = new HashMap<String, String>(arthasConfigMap.size());
		for (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();
		logger.info("Arthas agent start success.");
		return arthasAgent;

	}
}

ArthasConfiguration注册了arthasConfigMap及arthasAgent两个bean

ArthasAgent

arthas-agent-attach/src/main/java/com/taobao/arthas/agent/attach/ArthasAgent.java

代码语言:javascript
复制
public class ArthasAgent {
    private static final int TEMP_DIR_ATTEMPTS = 10000;

    private static final String ARTHAS_CORE_JAR = "arthas-core.jar";
    private static final String ARTHAS_BOOTSTRAP = "com.taobao.arthas.core.server.ArthasBootstrap";
    private static final String GET_INSTANCE = "getInstance";
    private static final String IS_BIND = "isBind";

    private String errorMessage;

    private Map<String, String> configMap = new HashMap<String, String>();
    private String arthasHome;
    private boolean slientInit;
    private Instrumentation instrumentation;

    public ArthasAgent() {
        this(null, null, false, null);
    }

    //......

    public void init() throws IllegalStateException {
        // 尝试判断arthas是否已在运行,如果是的话,直接就退出
        try {
            Class.forName("java.arthas.SpyAPI"); // 加载不到会抛异常
            if (SpyAPI.isInited()) {
                return;
            }
        } catch (Throwable e) {
            // ignore
        }

        try {
            if (instrumentation == null) {
                instrumentation = ByteBuddyAgent.install();
            }

            // 检查 arthasHome
            if (arthasHome == null || arthasHome.trim().isEmpty()) {
                // 解压出 arthasHome
                URL coreJarUrl = this.getClass().getClassLoader().getResource("arthas-bin.zip");
                if (coreJarUrl != null) {
                    File tempArthasDir = createTempDir();
                    ZipUtil.unpack(coreJarUrl.openStream(), tempArthasDir);
                    arthasHome = tempArthasDir.getAbsolutePath();
                } else {
                    throw new IllegalArgumentException("can not getResources arthas-bin.zip from classloader: "
                            + this.getClass().getClassLoader());
                }
            }

            // find arthas-core.jar
            File arthasCoreJarFile = new File(arthasHome, ARTHAS_CORE_JAR);
            if (!arthasCoreJarFile.exists()) {
                throw new IllegalStateException("can not find arthas-core.jar under arthasHome: " + arthasHome);
            }
            AttachArthasClassloader arthasClassLoader = new AttachArthasClassloader(
                    new URL[] { arthasCoreJarFile.toURI().toURL() });

            /**
             * <pre>
             * ArthasBootstrap bootstrap = ArthasBootstrap.getInstance(inst);
             * </pre>
             */
            Class<?> bootstrapClass = arthasClassLoader.loadClass(ARTHAS_BOOTSTRAP);
            Object bootstrap = bootstrapClass.getMethod(GET_INSTANCE, Instrumentation.class, Map.class).invoke(null,
                    instrumentation, configMap);
            boolean isBind = (Boolean) bootstrapClass.getMethod(IS_BIND).invoke(bootstrap);
            if (!isBind) {
                String errorMsg = "Arthas server port binding failed! Please check $HOME/logs/arthas/arthas.log for more details.";
                throw new RuntimeException(errorMsg);
            }
        } catch (Throwable e) {
            errorMessage = e.getMessage();
            if (!slientInit) {
                throw new IllegalStateException(e);
            }
        }
    }
}    

ArthasAgent的init方法先尝试加载java.arthas.SpyAPI,若SpyAPI.isInited()为true则直接返回;之后执行ByteBuddyAgent.install();对于arthasHome为null则尝试读取arthas-bin.zip文件,接着创建AttachArthasClassloader,加载com.taobao.arthas.core.server.ArthasBootstrap,执行其getInstance方法,再对实例执行isBind

ArthasEndPointAutoConfiguration

arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/endpoints/ArthasEndPointAutoConfiguration.java

代码语言:javascript
复制
@ConditionalOnProperty(name = "spring.arthas.enabled", matchIfMissing = true)
public class ArthasEndPointAutoConfiguration {

	@Bean
	@ConditionalOnMissingBean
	@ConditionalOnAvailableEndpoint
	public ArthasEndPoint arthasEndPoint() {
		return new ArthasEndPoint();
	}
}

ArthasEndPointAutoConfiguration则创建ArthasEndPoint

ArthasEndPoint

arthas-spring-boot-starter/src/main/java/com/alibaba/arthas/spring/endpoints/ArthasEndPoint.java

代码语言:javascript
复制
@Endpoint(id = "arthas")
public class ArthasEndPoint {

	@Autowired(required = false)
	private ArthasAgent arthasAgent;

	@Autowired(required = false)
	private HashMap<String, String> arthasConfigMap;

	@ReadOperation
	public Map<String, Object> invoke() {
		Map<String, Object> result = new HashMap<String, Object>();

		if (arthasConfigMap != null) {
			result.put("arthasConfigMap", arthasConfigMap);
		}

		String errorMessage = arthasAgent.getErrorMessage();
		if (errorMessage != null) {
			result.put("errorMessage", errorMessage);
		}

		return result;
	}

}

ArthasEndPoint提供了一个读方法返回arthasConfigMap

小结

arthas的spring-boot-starter有两个自动配置,分别是ArthasConfiguration及ArthasEndPointAutoConfiguration,其中ArthasConfiguration注册了arthasConfigMap及arthasAgent两个bean,而ArthasEndPointAutoConfiguration则创建ArthasEndPoint。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ArthasConfiguration
  • ArthasAgent
  • ArthasEndPointAutoConfiguration
  • ArthasEndPoint
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档