一、需求痛点
线上应用常见问题:
- 某些接口偶尔变慢,但日志看不出问题;
- 方法调用次数不透明,性能瓶颈难找;
- 线上出现失败/超时,但缺乏统计维度;
- 想要监控,却不想引入重量级的
apm方案。
常见 apm 工具功能强大,但部署复杂、学习成本高,不适合中小团队或者单机项目。
那有没有可能,基于 springboot 实现一个轻量级耗时监控器,做到方法级监控 + 可视化统计 ?
二、功能目标
我们希望监控器能做到:
基础监控能力:
• 方法调用次数:统计某方法被调用了多少次
• 耗时指标:平均耗时、最大耗时、最小耗时
• 成功/失败次数:区分正常与异常调用
• 多维排序:支持按调用次数、平均耗时、失败次数等维度排序
进阶功能:
• 时间段过滤:选择时间范围(如最近 5 分钟、1 小时、1 天)查看数据
• 接口搜索:快速定位特定接口的性能数据
• 可视化控制台:实时展示接口调用统计
三、技术设计
1. 基于aop的耗时监控
1.1 添加依赖
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-aop</artifactid>
</dependency>1.2 自定义监控注解
@target(elementtype.method)
@retention(retentionpolicy.runtime)
public @interface timemonitor {
string value() default "";
timeunit timeunit() default timeunit.milliseconds;
boolean logresult() default false;
}1.3 实现aop切面
@aspect
@component
@slf4j
public class executiontimeaspect {
@around("@annotation(timemonitor)")
public object monitorexecutiontime(proceedingjoinpoint joinpoint,
timemonitor timemonitor) throws throwable {
long starttime = system.currenttimemillis();
object result = null;
try {
result = joinpoint.proceed();
return result;
} finally {
long endtime = system.currenttimemillis();
long duration = endtime - starttime;
long convertedduration = converttimeunit(duration, timemonitor.timeunit());
logexecutiontime(joinpoint, timemonitor, convertedduration, result);
}
}
private long converttimeunit(long duration, timeunit timeunit) {
switch (timeunit) {
case seconds: return duration / 1000;
case microseconds: return duration * 1000;
case nanoseconds: return duration * 1000000;
default: return duration;
}
}
private void logexecutiontime(proceedingjoinpoint joinpoint, timemonitor timemonitor,
long duration, object result) {
string methodname = joinpoint.getsignature().toshortstring();
string message = string.format("方法 %s 执行耗时: %d %s",
methodname, duration, gettimeunitstring(timemonitor.timeunit()));
if (timemonitor.logresult() && result != null) {
message += string.format(" | 返回结果: %s", result.tostring());
}
log.info(message);
}
private string gettimeunitstring(timeunit timeunit) {
switch (timeunit) {
case seconds: return "s";
case milliseconds: return "ms";
case microseconds: return "μs";
case nanoseconds: return "ns";
default: return "ms";
}
}
}1.4 使用示例
@service
public class userservice {
@timemonitor(value = "用户查询", timeunit = timeunit.milliseconds, logresult = true)
public user getuserbyid(long id) {
// 业务逻辑
return userrepository.findbyid(id).orelse(null);
}
@timemonitor("批量用户查询")
public list<user> getusers(list<long> ids) {
// 业务逻辑
return userrepository.findallbyid(ids);
}
}2. 基于spring boot actuator的监控
2.1 添加依赖
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-actuator</artifactid>
</dependency>
<dependency>
<groupid>io.micrometer</groupid>
<artifactid>micrometer-core</artifactid>
</dependency>2.2 配置metrics监控
@component
public class methodmetrics {
private final meterregistry meterregistry;
private final map<string, timer> timers = new concurrenthashmap<>();
public methodmetrics(meterregistry meterregistry) {
this.meterregistry = meterregistry;
}
public void recordexecutiontime(string methodname, long duration, timeunit unit) {
timer timer = timers.computeifabsent(methodname,
key -> timer.builder("method.execution.time")
.tag("method", methodname)
.register(meterregistry));
timer.record(duration, unit);
}
@eventlistener
public void handlemethodexecutionevent(methodexecutionevent event) {
recordexecutiontime(event.getmethodname(),
event.getduration(),
event.gettimeunit());
}
}3. 高级功能:统计和告警
3.1 监控统计类
@component
@slf4j
public class methodperformancemonitor {
private final map<string, methodstats> statsmap = new concurrenthashmap<>();
private final long warningthreshold;
public methodperformancemonitor(@value("${monitor.warning-threshold:1000}") long warningthreshold) {
this.warningthreshold = warningthreshold;
}
public void recordmethodexecution(string methodname, long duration) {
methodstats stats = statsmap.computeifabsent(methodname, k -> new methodstats());
stats.recordexecution(duration);
// 超过阈值告警
if (duration > warningthreshold) {
log.warn("方法 {} 执行耗时 {}ms 超过阈值 {}ms",
methodname, duration, warningthreshold);
}
// 定期输出统计信息
if (stats.getexecutioncount() % 100 == 0) {
log.info("方法 {} 统计: {}", methodname, stats.getstatssummary());
}
}
@scheduled(fixedrate = 60000) // 每分钟输出一次汇总统计
public void printsummary() {
log.info("=== 方法执行耗时统计汇总 ===");
statsmap.foreach((method, stats) -> {
log.info("方法 {}: {}", method, stats.getstatssummary());
});
}
@data
public static class methodstats {
private long executioncount;
private long totaltime;
private long maxtime;
private long mintime = long.max_value;
public void recordexecution(long duration) {
executioncount++;
totaltime += duration;
maxtime = math.max(maxtime, duration);
mintime = math.min(mintime, duration);
}
public double getaveragetime() {
return executioncount == 0 ? 0 : (double) totaltime / executioncount;
}
public string getstatssummary() {
return string.format("调用次数: %d, 平均耗时: %.2fms, 最大耗时: %dms, 最小耗时: %dms",
executioncount, getaveragetime(), maxtime, mintime);
}
}
}3.2 增强的aop切面
@aspect
@component
@slf4j
public class enhancedexecutiontimeaspect {
private final methodperformancemonitor performancemonitor;
public enhancedexecutiontimeaspect(methodperformancemonitor performancemonitor) {
this.performancemonitor = performancemonitor;
}
@around("@annotation(timemonitor)")
public object monitorexecutiontime(proceedingjoinpoint joinpoint,
timemonitor timemonitor) throws throwable {
string methodname = getmethodname(joinpoint);
long starttime = system.currenttimemillis();
try {
object result = joinpoint.proceed();
return result;
} catch (throwable throwable) {
log.error("方法 {} 执行异常", methodname, throwable);
throw throwable;
} finally {
long endtime = system.currenttimemillis();
long duration = endtime - starttime;
// 记录执行时间
performancemonitor.recordmethodexecution(methodname, duration);
// 记录详细日志
if (log.isdebugenabled()) {
log.debug("方法 {} 执行耗时: {}ms", methodname, duration);
}
}
}
private string getmethodname(proceedingjoinpoint joinpoint) {
methodsignature signature = (methodsignature) joinpoint.getsignature();
return signature.getdeclaringtype().getsimplename() + "." + signature.getname();
}
}4. 配置类
@configuration
@enableaspectjautoproxy
@enablescheduling
public class monitorconfig {
@bean
@conditionalonmissingbean
public methodperformancemonitor methodperformancemonitor() {
return new methodperformancemonitor(1000l);
}
@bean
public enhancedexecutiontimeaspect enhancedexecutiontimeaspect(
methodperformancemonitor performancemonitor) {
return new enhancedexecutiontimeaspect(performancemonitor);
}
}5. 应用配置
# application.yml
monitor:
warning-threshold: 500 # 告警阈值,单位ms
logging:
level:
com.yourpackage.monitor: debug
management:
endpoints:
web:
exposure:
include: metrics
endpoint:
metrics:
enabled: true四、使用方式
- 基本使用:在需要监控的方法上添加
@timemonitor注解 - 自定义配置:通过注解参数调整时间单位和是否记录返回值
- 查看统计:系统会自动输出方法执行统计信息
- 监控告警:当方法执行时间超过阈值时会输出警告日志
以上就是springboot方法级耗时监控实现方案的详细内容,更多关于springboot方法级耗时监控的资料请关注代码网其它相关文章!
发表评论