一种正规的性能调优方法:基于等待的调优(2)

发表于:2013-04-23来源:InfoQ作者:Steven Haines,点击数: 标签:性能调优
不管用户行为是如何获得的,它都是开始任何性能调优实践之前的关键先决条件。 全新应用 由于全新的应用没有任何最终用户行为可以分析,所以对我们

  不管用户行为是如何获得的,它都是开始任何性能调优实践之前的关键先决条件。

  全新应用

  由于全新的应用没有任何最终用户行为可以分析,所以对我们提出了一个独特的挑战。定位新应用的用户行为需要三个步骤,如图1所示。

  图 1 评估新应用的最终用户行为

  第一步,评估最终用户应该会做什么。这步是“猜一猜”的正式说法,但应该是一个经过培训的猜测过程。评估结果应该来自于以下双方的讨论:应用业务负责人和应用技术负责人。应用业务负责人,通常是一个产品经理,负责细化最终用户应该如何使用该应用程序——例如,他可能报告说最终用户会登陆、处理五个索赔请求、过期、处理五个索赔请求、生成两个报告、然后注销。应用技术负责人,一般是架构师或是技术lead,负责把业务交互的抽象列表翻译成用于生成负载测试的技术步骤——例如,他可能报告说登陆通过“/login.do” URI完成而处理一个索赔请求需要五个URI。这些人(或者小组或者一些大型项目里的委员会)应该一起提供足够的信息来建立一个基准负载测试。

  我们建立负载测试,用其调整应用和容器,应用程序部署到生产环境中,这一切做完之后,调优工作并没有结束。下一步是验证负载测试集。这通常是一个多阶段的活动:

  冒烟测试验证:在实际运行的一两周之内验证原先的评估值是否符合真正生产环境下最终用户的行为。这步验证是为了确认在评估过程中没有明显的错误。

  生产验证:一些应用需要用户花时间才能形成统一的使用方式。这个适应的时间长短因应用而异,可能是一个月或者一个季度,不过一旦用户的行为稳定下来,就需要验证最终用户行为是否与评估一致。

  回归验证:最好在应用的生产周期中阶段性的验证用户行为,以防止用户行为改变或者引入新的功能或工作流导致用户行为改变。

  最后一步,也是经常被忽视的一步,就是反思。根据实际用户行为来反思评估的精确性是很重要的,因为只有通过反思,用户行为才能更便于理解,评估在以后的应用中才能得到提高。没有反思,相同的错误会一犯再犯,最终会增加调优的工作量。

  基于等待的调优方法

  建好了负载测试,接下来就是决定把调优精力放在何处。大多数调优指南都会提到“性能比率”或者度量之间的关系。例如,某调优指南可能强调说缓存命中率应该达到80%或者更高,因此负载测试应用时调整缓存大小直到命中率达到80%。然后处理列表上的下一个度量值,但是不要忘记验证调整新的参数不会影响之前已经调好的其他参数。

  这项工作不仅困难而且效率很低。例如,调整缓存命中率到80%可能是件好事,但是存在一些更重要的问题:

  该应用如何依赖于缓存(与该缓存交互的请求比例是多少)?

  这些请求对应用中的其他请求有多大影响力?

  被缓存的条目的本质是什么?它们真的需要缓存吗?

  基于等待的调优方法提出了一个新的思想,即分析应用的业务交互和实现这些交互的底层结构,然后优化这些业务交互的处理。第一步是分析应用的架构以定位实现业务请求的相关技术。每一个技术代表一个“等待点”,或者说在应用的这个地方,请求可能需要等待一些事情才能继续处理。例如,如果一个请求执行了一次数据库查询,则它必须从连接池中获取一个数据库连接—如果连接池里没有可用的连接,则该请求必须等待直到有连接可用。同样,如果某请求调用了一个web服务,而那个web服务维护了一个请求队列(对应一个线程池处理请求),这会潜在的导致请求等待直到一个处理线程可用。

  从以上称之为等待点分析的方法中,可以定位两种类型的等待点:

  基于层次的等待点

  基于技术的等待点

  本节首先概述了基于等待点的架构分析方法,然后分别研究了不同类型的等待点。

  等待点架构分析

  从上面讨论中得出的最重要的结论就是性能调优必须在应用架构的环境中执行。这也是调优性能比率为何如此低效的一个原因:主观的调整一个性能参数到一个最佳值,对应用可能是好事也可能是坏事——因为这可能会也可能不会有利于最终用户体验。

  基于等待点的分析是一个分析应用中主要请求处理路径的过程,借此定位潜在影响该请求可能会等待的资源。在等待点分析实践中最有效的办法是定位并标出应用中核心处理路径。这包括一个请求可能访问的所有层次、请求交互的所有外部服务、被做成池的所有对象和全部缓存对象。

  基于层次的等待点

  一个请求穿过一个物理层,比如在web层和业务层之间切换,或者调用外部服务器,比如web服务,每当这种时候,都意味着伴随着转换,这里存在一个隐式等待点。如果服务器在某一时刻只处理单个请求是没有效率的,因此他们使用了多线程方法。典型的,一个服务器在某个socket端口监听访问的请求——每当收到一个请求它就会把请求放在队列中,然后返回监听其他请求。请求队列对应着一个线程池,负责从队列中提取请求并处理。图2描述了对于三层结构(web服务器、动态web层和业务层)的执行过程。

  图 2 基于层次的等待点

  因为请求穿越层次的动作会引起请求队列,由相应的线程池处理,这种线程池也是一个潜在的等待点。每一个线程池的大小的调优必须基于以下考虑:

  池必须足够大使得访问的请求无需不必要的等待一个线程。

  池不能大到耗尽服务器。过多的线程会迫使服务器花费大量时间用于在不同的线程上下文中切换,真正处理请求的时间反而更少了。这种情况通常表现为CPU使用率很高,但是处理请求的吞吐量却降低了。

  池的大小不能透支与之交互的后台资源。例如,如果数据库对于单个服务器只支持50个请求,那么服务器不应该向数据库发送超过50个数量的请求。

原文转自:http://www.infoq.com/cn/articles/Wait-Based-Tuning-Steven-Haines