随机测量的CPU&弹簧中的ThreadMXBean与AOP

时间:2017-08-29 22:28:18

标签: java spring performance aspectj spring-aop

我想测量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)。

1 个答案:

答案 0 :(得分:0)

更新2&amp;回答:

答案似乎是ThreadMXBean定期更新,我的方法运行得如此之快,以至于ThreadMXBean在运行时没有更新。

我通过在每次测量的开始和结束之间插入Thread.sleep(200);命令来测试它。结果是我更新(并且看似正确)我想要测量的每个部分的CPU时间和用户时间值。

更新3:

好的,经过进一步的尝试,我意识到这不仅仅是一个线程在测量之间运行的时间问题。 似乎我尝试测量的阶段消耗的CPU和用户时间不足以由ThreadMXBean正确测量。我在三个阶段内添加了随机数据库查询测量似乎工作得更好,但仍存在随机测量问题。我似乎通过结合更多工作量(消耗的cpu-和用户时间)和Thread.sleep(X)来获得最佳测量结果。

但这并不令人满意。 是否有人偶然获得有关ThreadMXBean措施准确程度或更新时间的详细信息?或者我是如何找到它的任何想法?