我想测量Spring中某些任务的性能统计数据,每个任务包含几个方法调用。这些性能统计信息不应包括数据库查询所需的时间。因此,我使用Spring AOP方面拦截DAO方法,其中我停止测量任务并开始单独测量数据库调用。两个测量都有效,问题是,ThreadMXBean获得的CPU和用户时间有时不会在其中一个不同的测量部分中增加。因为这些部分我的意思是A)之前的任务测量DB调用,B)DB调用的测量和C)DB调用后的任务测量。我对每个部分都进行了合理的测量,但不是同时测量所有三个部分。似乎至少其中一个(看起来是随机的)显示CPU和用户时间没有增加。
以下是一些可视化我的问题案例的示例代码:
@Api
@Path("task1")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class task1Resource {
@Autowired
private MeasurementService measurementService;
@POST
public Response startTask(...){
measurementService.start("task1", false);
//Transactional service methods with the business logic for task1
//are called
measurementService.end("task1");
return Response.ok(entity).build();
}
}
@Service
@Transactional
public class MeasurementService {
@Autowired
private MeasurementDAO measurementDao
public void start(String task, boolean interrupted){
MeasurementObject measurementObj = new MeasurementObject();
ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
if(interrupted){
//Get the previously persisted measurement object for the task
measurementObj = measurementDao.get(task);
//Calculate the differences with the values of the previous section
measurementObj.calcCpuTimeDifference(mxBean.getCurrentThreadCpuTime();
measurementObj.calcUserTimeDifference(mxBean.getCurrentThreadCpuTime();
}else{
//Initialize
measurementObj.setTask(task);
Set<DAOMeasurement> daoCalls = new HashSet<DAOMeasurement>();
measurementObj.setDaoCalls(daoCalls);
Thread currentThread = Thread.currentThread();
measurementObj.setThread(currentThread);
measurementObj.setCpuTime(mxBean.getCurrentThreadCpuTime);
measurementObj.setUserTime(mxBean.getCurrenThreadUserTime);
}
measurementDao.save(measurementObj);
}
public MeasurementObject end(String task){
//Get the measurement object for the task
MeasurementObject measurementObj = measurementDao.get(task);
ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
//Calculate the differences with the values from the start
//or the previously calculated differences
measurementObj.calcCpuTimeDifference(mxBean.getCurrentThreadCpuTime());
measurementObj.calcUserTimeDifference(mxBean.getCurrentThreadUserTime());
return measurementDao.save(measurementObj);
}
}
@Aspect
@Component
public class daoAspect {
@Autowired
private MeasurementService measurementService;
@Autowired
private DAOMeasurementService daoMeasurementService;
@Before("daoMethods() && !measurementMethods()")
private void start(Joinpoint jopo){
Thread currentThread = Thread.currentThread();
//Get the task measurement object that was started in the same thread
MeasurementObject measurementObj = measurementService.byThread(currentThread);
measurementObj = measurementService.end(measurementObj.getTask());
//Start the measurement for the DB call
DAOMeasurementObject daoMeasurementObj = daoMeasurementService.start(measurementObj);
}
@After("daoMethods() && !measurementMethods()")
private void end(Joinpoint jopo){
Thread currentThread = Thread.currentThread();
/*Please assume here that we only have one dao method
* (In my more complex case I query the database for
* the correct daoMeasurementObject by using additional parameters)
* So here we find the DB call measurement object that was started
* in this thread and end it. */
DAOMeasurementObject daoMeasurementObj = daoMeasurementService.byThread(currentThread);
daoMeasurementService.end(daoMeasurementObj);
//We get the task measurement object for this thread and start it again
MeasurementObject measurementObj = measurementService.byThread(currentThread);
measurementService.start(measurementObj.getTask(),true);
}
@Pointcut("execution(* com.foo.bar.dao.*(..))")
public void daoMethods(){}
@Pointcut("@annotation(Measurement)")
public void measurementMethods(){} //I annotated the measurement dao classes with this annotation
}
@Service
@Transactional
public class DAOMeasurementService {
/*This class is identical to the MeasurementService class with the
* difference that a DAOMeasurementObject gets a MeasurementObject
* reference. But the cpu-&user-times are calculated in the same manner */
}
正如我所说一般设置。任务测量按预期开始和结束,并且其间的DAO方法被方面中断。此外,测量似乎正常,因为我有时会得到正确的值。 唯一的问题是,有时cpu-&amp; user-times不会从三个测量部分中的一个或两个的开头变为它们的末尾,导致计算的差值为0.
此外,我检查了所有这些方法都有相同的 Thread.currentThread()和 ThreadMXBean.isCurrentThreadCpuTimeSupported()和 ThreadMXBean.isThreadCpuTimeEnabled()在所有测量点上返回 true 。
我真的很高兴得到一些帮助或提示!如果您需要更多代码或信息,请随时提出要求。
更新
我使用@Around
方面测试了系统,而不是@Before
和@After
方面以及多次运行。结果仍然相同。 {1}}和ThreadMXBean.getCurrentThreadCpuTime
值在一到两个测量阶段中随机不增加,而是在它们之间增加。例如:阶段A)任务开始时的测量显示CPU时间为280801800,用户时间为265201700,当DAO截取期间停止测量时,它再次显示CPU时间280801800和用户 - 然后在阶段B)的开始,测量DAO方法,它显示CPU时间为296401900,用户时间为280801800.但是在DAO方法测量结束时,它仍然显示相同的CPU时间为296401900,相同的用户时间为280801800.在完成阶段C)期间,时间与B)相同。
- &GT;是不是ThreadMXBean只在某些时间间隔更新其统计信息而不是在调用其方法时?我目前尝试测量的阶段相对较短,持续时间为1到10毫秒(使用LocalDateTime)。
答案 0 :(得分:0)
更新2&amp;回答:强>
答案似乎是ThreadMXBean
定期更新,我的方法运行得如此之快,以至于ThreadMXBean
在运行时没有更新。
我通过在每次测量的开始和结束之间插入Thread.sleep(200);
命令来测试它。结果是我更新(并且看似正确)我想要测量的每个部分的CPU时间和用户时间值。
更新3:
好的,经过进一步的尝试,我意识到这不仅仅是一个线程在测量之间运行的时间问题。 似乎我尝试测量的阶段消耗的CPU和用户时间不足以由ThreadMXBean
正确测量。我在三个阶段内添加了随机数据库查询测量似乎工作得更好,但仍存在随机测量问题。我似乎通过结合更多工作量(消耗的cpu-和用户时间)和Thread.sleep(X)
来获得最佳测量结果。
但这并不令人满意。 是否有人偶然获得有关ThreadMXBean
措施准确程度或更新时间的详细信息?或者我是如何找到它的任何想法?