我的上一篇文章描述了如何使用JMeter完成异步REST / HTTP服务的压力测试或负载测试。 但是,运行这样的测试通常表明被测系统不能很好地应对不断增加的负载。 现在的问题是如何找到瓶颈?
深入研究代码以检测可疑部分可能是另一种选择。 但是考虑到潜在的巨大代码库,因此考虑到瓶颈隐藏1的众多可能性,这似乎不太有希望。 幸运的是,有一些工具可以在遥测2的基础上提供有效的分析功能。 记录和检查此类测量值通常被称为概要分析,而本博文简要介绍了如何使用Yourkit 3进行此操作。
首先,我们推出我们的SUT( 变体系ü的nDer 牛逼 EST),并使用JMeter的建立系统的负荷。 为此,JMeter可能执行一个测试方案,该方案模拟了多个用户向SUT发送大量请求的情况。 在测试场景中定义的测试计划 。 后者可能包含侦听器 ,这些侦听器允许捕获请求的执行时间并提供统计信息,例如最大/最小/可用性请求持续时间,偏差,吞吐量等。 这就是我们检测到我们的系统扩展性不佳的方式…
在发现之后,我们使Yourkit可以检索遥测。 因此,SUT的VM由特殊的探查器代理启动。 探查器工具提供了多个视图,这些视图允许实时检查CPU利用率,内存消耗等。 但是,为了全面分析例如SUT在负载下的性能,Yourkit需要通过所谓的快照捕获代理提供的CPU信息。
建议在单独的计算机上运行SUT,JMeter和Yourkit,以避免篡改测试结果。 在同一台机器上运行例如SUT和JMeter可能会降低吞吐量,因为JMeter线程可能会消耗大量可用的计算时间。
考虑到此设置,我们将介绍一个概要分析会话的小示例。 以下代码段摘录了我们用作SUT的基于JAX-RS的服务4 。
@Path( '/resources/{id}' )
public class ExampleResourceProvider {
private List<ExampleResource> resources;
[...]
@Override
@GET
@Produces( MediaType.TEXT_PLAIN )
public String getContent( @PathParam( 'id' ) String id ) {
ExampleResource found = NOT_FOUND;
for( ExampleResource resource : resources ) {
if( resource.getId().equals( id ) ) {
found = resource;
}
}
return found.getMessage();
}
该服务在ExampleResource
实例列表中执行查找。 ExampleResource
对象只是将标识符映射到以String
表示的消息。 找到给定标识符的消息将返回。 在使用GET
请求调用服务时,您可以使用浏览器测试结果:
出于演示目的,服务的粘合代码以无序方式使用500000个元素初始化列表。
一旦运行了SUT,就可以使用JMeter在负载下对其进行设置。 该测试计划一次执行大约100个并发请求。 如下图所示,平均请求执行时间约为1秒。
Yourkit在JMeter测试计划执行期间记录的CPU遥测揭示了请求执行时间长的原因。 选择配置文件快照的“ Hot spots
选项卡显示列表迭代消耗了大约72%的CPU利用率。 查看列出了所选热点方法的调用者树的Back Traces
视图,我们发现示例服务方法导致列表迭代。
因此,我们在下一步中更改服务实现,以对有序列表使用二进制搜索进行ExampleResource
查找。
@Override
@GET
@Produces( MediaType.TEXT_PLAIN )
public String getContent( @PathParam( 'id' ) String id ) {
ExampleResource key = new ExampleResource( id, null );
int position = Collections.binarySearch( resources, key );
return resources.get( position ).getMessage();
}
之后,我们重新运行JMeter测试计划:
现在平均请求大约需要3毫秒,这是一个很大的改进。
查看相应的CPU性能分析会话的Hot spots
,可以确认由我们的方法引起的瓶颈已经消失。
诚然,以上示例中的问题似乎非常明显。 但是我们在生产代码中发现了一个非常相似的代码,它隐藏在系统的深度中(可耻的是……)。 重要的是要注意,在开始压力和负载测试之前,问题并没有变得很明显5 。
我想我们在找到原因之前(如果有的话)将花费大量时间手动检查代码库。 然而,剖析会议将我们直接指出了所有邪恶的根源。 通常情况下,实际问题并不难解决。 因此,概要分析可以帮助您更有效地处理一些工作。
至少它对我有用-顺便说一下-这也很有趣
- 请注意,导致瓶颈的代码也可能属于第三个库。 ↩
- 当我现在在一个客户项目中进行这种分析时,我想到了写这篇文章的想法↩
- 我在这里没有做任何工具广告或评级-我只是使用我熟悉的工具来给出一个更抽象的概念的可复制示例。 市场上有很多更好的工具可以满足您的需求↩
- 请注意,本文中的代码片段的唯一目的是作为如何查找和解决性能瓶颈的示例。 这些代码片段编写得很差,不应以任何方式重复使用! ↩
- 根据我的经验,新创建的代码库包含其中的一些块是很常见的。 因此,有这样的测试是必须的,以便找到性能问题的顾客发现他们在生产之前... ↩
参考:来自JCG合作伙伴 Frank Appel的JMeter和Yourkit对REST / HTTP服务的性能分析,位于Code Affine博客上。