在多线程java程序中定时远程调用

时间:2013-11-05 22:44:21

标签: java multithreading benchmarking

我正在编写一个压力测试,它将向远程服务器发出许多调用。我想在测试后收集以下统计数据:

  1. 远程呼叫的延迟(以毫秒为单位)。
  2. 远程服务器每秒可以处理的操作数。
  3. 我可以成功地获得(2),但我遇到了(1)的问题。我目前的实现与this other SO question中显示的非常相似。我遇到了该问题中描述的相同问题:使用System.currentTimeMillis()报告的延迟比使用多个线程运行测试时的预期长。

    我分析了问题并且我很肯定问题来自线程交错(请参阅我上面链接的其他问题的答案以获取详细信息),并且System.currentTimeMillis()不是解决此问题的方法。

    似乎我应该能够使用java.lang.management来完成它,它有一些有趣的方法,如:

    1. ThreadMXBean.getCurrentThreadCpuTime()
    2. ThreadMXBean.getCurrentThreadUserTime()
    3. ThreadInfo.getWaitedTime()
    4. ThreadInfo.getBlockedTime()
    5. 我的问题是,即使我已经阅读过API,我仍然不清楚这些方法中的哪一个会给我我想要的东西。在我链接的其他SO问题的背景下,这就是我需要的:

      long start_time = **rightMethodToCall()**;
      
      result = restTemplate.getForObject("Some URL",String.class);
      long difference = (**rightMethodToCall()** - start_time);
      

      即使在多线程环境中,difference也能让我非常接近远程调用的时间。

      限制:我想避免使用synchronized块来保护该代码块,因为我的程序还有其他线程,我希望允许它继续执行。

      编辑:提供更多信息。:

      问题是:我想要远程呼叫的时间,而只是远程呼叫。如果我使用System.currentTimeMillisSystem.nanoTime(),并且如果我有比线程更多的线程,那么我可以将此线程交错:

      1. Thread1:long start_time ...
      2. Thread1:result = ...
      3. Thread2:long start_time ...
      4. Thread2:result = ...
      5. 线程2:差异很大......
      6. 线程1:差异很大......
      7. 如果发生这种情况,那么Thread2计算的差异是正确的,但是Thread1计算的差异是不正确的(它应该大于它应该是的)。换句话说,为了测量Thread1中的差异,我想排除第4行和第5行的时间。这次是线程正在等待吗?

        以不同的方式总结问题,以防其他人更好地理解它(这句话是@jason-c如何将其纳入评论中。):

        [我]试图对远程调用进行计时,但运行多个线程的测试只是为了增加测试量。

1 个答案:

答案 0 :(得分:1)

使用System.nanoTime()(但请参阅此答案末尾的更新)。

您绝对不希望使用当前线程的CPU或用户时间,因为用户感知的延迟是挂钟时间,而不是线程CPU时间。您也不想使用当前线程的阻塞或等待时间,因为它会测量每个线程的争用时间,这也不能准确地表示您要测量的内容。

System.nanoTime()将返回相对准确的结果(虽然技术上只保证粒度与currentTimeMillis()一样好或更好,但实际上它往往要好得多,通常用硬件时钟或其他性能来实现定时器,例如Windows上的QueryPerformanceCounter或Linux上的clock_gettime,来自具有固定参考点的高分辨率时钟,并且将准确测量您要测量的内容。

long start_time = System.nanoTime();
result = restTemplate.getForObject("Some URL",String.class);
long difference = (System.nanoTime() - start_time);
long milliseconds = difference / 1000000;

System.nanoTime()确实有own set of issues,但要注意不要被妄想放逐;对于大多数应用来说,这已经足够了。您只是不想将它用于将音频样本发送到硬件或其他东西时的精确计时(无论如何都不能用Java直接进行)。

更新1:

更重要的是,您如何知道测量值比预期更长?如果您的测量显示真正的挂钟时间,并且某些线程比其他线程花费的时间更长,那么仍然是准确表示用户感知延迟,因为某些用户体验那些较长的延迟时间。

更新2(根据评论中的说明):

我上面的大部分答案仍然有效;但出于不同的原因。

使用每个线程的时间并不能为您提供准确的表示,因为在远程请求仍在处理时,线程可能处于空闲/非活动状态,因此您将从测量中排除该时间,即使它是感知延迟的一部分。

远程服务器需要花费更长时间来处理您正在进行的同时请求,这会引入更多不准确之处 - 这是您要添加的额外变量(尽管可以接受远程服务器正忙的代表)。

挂机时间也不完全准确,因为正如您所看到的,本地线程开销的差异可能会增加单一请求客户端应用程序中通常不存在的额外延迟(尽管这仍然可以作为客户端的代表)多线程的应用程序,但它是一个你无法控制的变量。)

在这两个中,壁挂时间仍然会比实际结果更接近每线程时间,这就是我之前留下上一个答案的原因。您有几个选择:

  • 您可以在一个线程上连续进行测试 - 这最终是实现您声明要求的最准确方法
  • 您无法创建比核心更多的线程,例如一个固定大小的线程池,具有绑定的亲和力(棘手:Java thread affinity)到每个核心和测量作为每个任务运行。当然,由于您无法控制的基础机制的同步,这仍会添加任何变量。这个可以降低交错的风险(特别是如果你设置了亲和力),但是你仍然无法完全控制交错。 JVM正在运行的其他线程或系统上其他不相关的进程。
  • 您可以测量远程服务器上的请求处理时间;当然,这不会考虑网络延迟。
  • 您可以继续使用当前的方法,对结果进行一些统计分析,以消除异常值。
  • 根本无法衡量这一点,只需进行用户测试并等待对其进行评论,然后再尝试对其进行优化(即与人进行测量,无论如何都要对其进行测量)。如果优化这一点的唯一原因是用户体验,很可能是用户有愉快的体验,等待时间完全可以接受。

此外,这些都不能保证系统上其他不相关的线程不会影响您的时间,但这就是为什么a)运行测试多次和平均(显然)和b)的重要性。设置一个可接受的定时误差要求,你可以(你真的需要知道这个,例如0.1ms准确度?)。

就个人而言,我要么采用第一种单线程方法,让它在一夜之间或周末运行,要么使用现有方法并从结果中删除异常值并接受时间上的误差。您的目标是在满意的错误范围内找到现实估算值。在决定什么是可接受的时候,您还需要考虑最终会对这些信息做些什么。