乐趣区

使用Arthas-获取Spring-ApplicationContext还原问题现场

背景

最近来了个实习僧小弟,安排他实现对目标网站 连通性检测的小功能, 简单讲就是将下边的 shell 脚本换成 Java 代码来实现

 1#!/bin/bash
 2URL="https://www.baidu"
 3HTTP_CODE=`curl -o /dev/null -s -w "%{http_code}" "${URL}"`
 4#echo $HTTP_CODE
 5if [$HTTP_CODE != '200'];then
 6curl 'https://oapi.dingtalk.com/robot/send?access_token=xx' \
 7   -H 'Content-Type: application/json' \
 8   -d '{"msgtype":"text",
 9        "text": {
10             "content": "百度平台状态不正常,请注意!"
11        },
12        "isAtAll": true
13      }'
14
15fi

功能实现

使用 spring task

 1@Scheduled(cron = "0 0 0/1 * * ?")
 2public void startSchedule() {3    log.info("开始执行定时任务,检测百度网站连通性");
 4    try {5        HttpResponse response = HttpRequest.get("").execute();
 6        if (HttpStatus.HTTP_OK != response.getStatus()) {7            this.send2DingTalk(response.getStatus());
 8        }
 9        log.info("请求百度成功,返回报文:{}",response.body());
10    } catch (HttpException e) {11        log.error("请求异常百度:{}", e);
12        this.send2DingTalk(e.getMessage());
13    }
14    log.info("执行检测百度网站连通任务完毕");
15}

问题描述

部署在服务器上,我的老 jio 本 都已经呼叫任务状态不正常了, 可是小弟的 Java 代码还是没有执行通知

  • 去翻生产日志,只输入了开始并没有输出定时任务结束,感觉是哪里卡死,想当然以为如果超时总会到 catch 逻辑,排查无果
  • 由于任务是一小时一次,如何快速触发一下这个异常,还原事故现场
  • 由于使用简单的 Spring Task 没有图形化界面和 API 接口

Arthas 还原事故现场,重新触发任务

核心拿到 spring context 然后执行它的 startSchedule 方法

确定监控点

  • SpringMVC 的请求会通过 RequestMappingHandlerAdapter 执行invokeHandlerMethod 到达目标接口上进行处理
  • 而在 RequestMappingHandlerAdapter类中有 getApplicationContext()
1@Nullable
2public final ApplicationContext getApplicationContext() throws IllegalStateException {3    if (this.applicationContext == null && this.isContextRequired()) {4        throw new IllegalStateException("ApplicationObjectSupport instance [" + this + "] does not run in an ApplicationContext");
5    } else {
6        return this.applicationContext;
7    }
8}
  • 任意执行一次请求获取到 RequestMappingHandlerAdaptertarget 目标,然后执行 getApplicationContext

tt 命令 获取到 ApplicationContext

  • arthas 执行 tt
1tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod
  • 任意执行一次 web 请求,tt 即可捕获

  • 根据目标的索引,执行自定义 OGNL 表达式即可
1tt -i 1019 -w 'target.getApplicationContext()'

使用 ApplicationContext 获取 定时任务 bean 执行 startSchedule

1tt -i 1000 -w 'target.getApplicationContext().getBean("baiduSchedule").startSchedule()'

ok 任务重新触发了

事故原因调查清楚,由于使用 hutool 的工具类 没有设置 timeout 导致无限等待,所以没有执行 catch 逻辑

总结

以上吓哭实习僧的操作 禁止 生产操作,只是提供个思路,当然可以衍生其他业务场景的操作

核心是通过 Arthas 来抓取 Spring ApplicationContext 对象,然后获取 bean 进行执行方法

关于 Arthas 是 Alibaba 开源的 Java 诊断工具,深受开发者喜爱

欢迎关注我们获得更多的好玩 JavaEE 实践

推荐阅读:

《深入理解 Java 内存模型》读书笔记

面试 - 基础篇

Spring Boot 2.0 迁移指南

SpringBoot 使用 Docker 快速部署项目

为什么选择 Spring 作为 Java 框架?

SpringBoot RocketMQ 整合使用和监控

Spring Boot 面试的十个问题

使用 Spring Framework 时常犯的十大错误

SpringBoot Kafka 整合使用

SpringBoot RabbitMQ 整合使用

上篇好文:

如何优雅关闭 Spring Boot 应用

退出移动版