不久前写过一篇Admin服务监控报警的文章springboot集成Admin「告警通知」
这实现很简单,只需增加继承 AbstractStatusChangeNotifier 配置报警机制就可以。但是这个报警只是简单的服务状态的报警,上线、下线通知,这满足不了我的预警目标。我的目标是实现更详细的报警信息,比如说服务CPU使用情况、Memory使用情况和Thread使用情况等。
使用过Admin的开发者都知道,Admin页面可以看到非常详细的服务状态,CPU、JVM、THREAD等等。本贴主要是在Admin服务端编写自定义定时任务,定时查询每个服务实例的状态,推送服务报警信息。
刚开始的实现思路,因为我的Admin服务端已引入了springboot-admin的pom依赖,完全可以直接调用admin源码去实现,这个思路确实没毛病,也能实现。
翻阅源码后,实现了一半后,发现实现服务实例的详细状态信息,需要构造参数,实现的时间成本比较大。所以换了一个思路,直接使用httpclient调用admin服务接口实现。
如果通过请求接口的方式实现,在未登录的状态下,会直接跳转到登录页面,所以Admin服务端存在登录鉴权,直接调用接口去使用的话,还得去搞一下鉴权问题,回忆一下集成Admin服务端的帖子。
在集成Admin服务端的时候,有一段鉴权的代码
http.authorizeRequests()
.antMatchers(adminContextPath + "/login",
adminContextPath + "/assets/**",
adminContextPath + "/manage/**",
/*新增鉴权开放接口配置-start*/
adminContextPath + "/applications/**",
adminContextPath + "/instances/**",
/*新增鉴权开放接口配置-end*/
adminContextPath + "/actuator/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().loginPage(adminContextPath + "/login").successHandler(successHandler)
.and()
.logout().logoutUrl(adminContextPath + "/logout")
.and()
.csrf().disable();
}
实现我的预警目标的话,需要调用的接口有:
- applications/**:获取服务实例列表
- /instances/**:获取实例详情,包括cpu、memory、thread等数据。
process.cpu.usage | 进程的cpu使用率 |
---|---|
system.cpu.usage | 系统的cpu使用率 |
jvm.memory.max | 最大内存数 |
jvm.memory.used | 已使用的内存数 |
jvm.threads.live | 当前活跃的线程数量,包括守护线程和非守护线程 |
jvm.threads.peak | 自Java虚拟机启动以来的活跃线程数峰值 |
jvm.threads.states | 当前处于终止状态的线程数 |
直接上代码
/**
* 实现思路:
* 定时调用springboot-admin接口,解析JSON响应数据
* 获取服务实例列表
* 获取每个实例的指标数据
* 根据自定义的阈值推送服务报警信息
*/
@Configuration
@EnableScheduling
public class SchedulingTask {
@Resource
private RestTemplate restTemplate;
//Admin服务端地址
private static final String SERVER_HOST = "http://localhost:7001";
private static final Logger log = LoggerFactory.getLogger(SchedulingTask.class);
@Scheduled(cron = "0 0/1 * * * ?")
public void task(){
String bodyData = getReqBody("/applications");
JSONArray bodyDataArray = JSONArray.parseArray(bodyData);
for(int i=0; i<bodyDataArray.size(); i++){
JSONObject serviceObject = bodyDataArray.getJSONObject(i);
//服务名称
String serviceName = serviceObject.getString("name");
//服务实例列表
JSONArray instanceArray = (JSONArray) serviceObject.get("instances");
for(int j=0; j<instanceArray.size(); j++){
JSONObject instanceObject = instanceArray.getJSONObject(j);
//服务-实例ID
String instanceId = instanceObject.getString("id");
/**
* 获取实例CPU、MEMORY、THREAD...
* 下面以获取CPU指标数据为例
*/
//Java虚拟机进程的“最近cpu使用率”
BigDecimal processCpuUsage = (BigDecimal) getDetails(instanceId, "process.cpu.usage");
//整个系统的“最近cpu使用率”
BigDecimal systemCpuUsage = (BigDecimal) getDetails(instanceId, "system.cpu.usage");
log.info("serviceName:{},instanceId:{},processCpuUsage:{},systemCpuUsage:{}",
serviceName,instanceId,processCpuUsage,systemCpuUsage);
//自定义阈值
if(processCpuUsage.doubleValue() > 0.95){
log.error("服务CPU使用过高,推送告警邮件");
//推送邮件、钉钉、企业微信或公司OA等。
}
}
}
}
/**
* 请求封装方法
* @param path
* @return
*/
private String getReqBody(String path){
ResponseEntity<String> entity = restTemplate.getForEntity(SERVER_HOST + path, String.class);
return entity.getBody();
}
/**
* 获取指标数据封装方法
* @param id
* @param pathSuffix
* @return
*/
public Object getDetails(String id,String pathSuffix){
/**
* /instances/id//actuator/metrics/xxx.xxx.xxx
*/
String bodyData = getReqBody("/instances/"+ id +"/actuator/metrics/" + pathSuffix);
JSONObject jsonObject = JSONObject.parseObject(bodyData);
JSONArray measurements = jsonObject.getJSONArray("measurements");
// 这个数组目前看可能只有一个数据
for(int i=0; i<measurements.size(); i++){
return measurements.getJSONObject(i).get("value");
}
return null;
}
}
实现的方式很简单、很粗暴,但是也实现自己想要的结果。
查看一段时间看看效果!!!!!!!!!