性能剖析
blog应用程序实现的另一个关注点是性能剖析。在许多项目中,应用程序要先进行性能剖析才能投入生产,而且只有当用户抱怨时才重做剖析。有了Atlassian剖析器,我们可以采取更为前摄的方法,跟踪每种方法所花的时间,并在每个请求的结尾报告结果。如果有哪一个请求花的时间比预期的多,我们就把剖析信息记录为ERROR以引起注意。将Log4J配置为有任何记录错误就向开发团队发送电子邮件,如果应用程序运行过慢的话我们立刻就能知道。凭我的经验,这在查找应用程序的程序设计问题(如死锁和编写得糟糕的事务)方面非常有价值。为了实现剖析器,我们可以沿用跟踪代码所使用的方法。在每一种方法中,代码中都有很多调用来启动和停止剖析器。把这与跟踪代码相结合,就得到了下面的代码:
public Entry[] findAll() {
log.enter("findAll");
Profiler.push("findAll");
List entries =
getHibernateTemplate().find("from Entry");
Profiler.pop("findAll");
log.exit("findAll");
return (Entry[])
entries.toArray(new Entry[] {});
}
这就对完全的用户请求产生了下面的日志输出:
com.tss.common.Profiler INFO :
[2373ms] - service: '/blog'
[150ms] - findAllEntries
[150ms] - findAll
尽管该信息非常有用,但您可以看到代码变得多么冗长。剖析器是如此易于侵入,以至于几乎不可能让整个开发团队都使用它。因为我们还没有使用AOP,我们别无选择只有引进一些hack——把对剖析器的调用结合在日志记录代码中。现在下面的日志方法会处理性能剖析:
public void enter(Object method) {
if (!l.isInfoEnabled()) return;
l.info(">" + method);
Profiler.push(method.toString());
}
public void exit(Object method) {
if (!l.isInfoEnabled()) return;
l.info("<" + method);
Profiler.pop(method.toString());
}
当这发挥作用时,开发人员现在只需要调用日志记录代码,我们把性能剖析关注点和日志记录关注点紧密联系起来了。这种紧密联系降低了灵活性。
示例应用程序实现中的问题
我已经暗示过blog应用程序实现的一些问题,但我想再次强调一下,并解释一下为什么这些是问题:
* 过于冗长
为了使记录器和剖析器能正常工作,必须在每个方法中包含enter/exit代码。在某些情况下,需要改变代码风格以允许在从方法返回之前调用一个exit。例如,想要剖析器正确记录方法的时间,必须在抛出异常或从方法返回之前调用exit。这给开发人员造成了负担,并使代码变得膨胀。
* 不易重构代码
重构代码以使它变得更为自然是一个最佳实践,任何使重构变得更复杂的事情,开发人员都会尽力避免。更改方法名称的事情经常发生,这使静态声明的 enter/exit方法名称不再正确。情况好的话,只需要手动更改;情况糟糕的话,不正确的名称会遗留在代码中,并将在调试过程中引起混乱。
* 不经代码审查无法坚持正确的用法
除了进行常规代码审查,没有其他方法可确保团队中的每个人都遵循项目的日志记录指导原则。如果不一致遵循指导原则的话,性能剖析和跟踪信息就失去意义。
* 日志记录关注点和性能剖析关注点的紧密联系
把性能剖析关注点融入到日志记录代码中会使剖析某些特定的代码路径非常困难。例如,分析所有的ServletRequest并且在任一个请求所花费的时间超过5秒时记录一个错误,这是我们想要的结果。其他的任务,如初始化Application servlet,应该允许花费超过5秒的时间而不触发错误,但是出于信息目的,我们仍然想把它记录下来。如果在初始化过程中产生错误,那么很可能把日志记录代码注释掉或者把最大花费时间增为10秒。(非常不幸,这是我的一个非AOP项目中真实发生的事情,那次初始化花了8秒钟。)
上面1,2,3点可能成为开发人员真正的负担。除了对日志记录和性能剖析的额外关注外,开发人员还有很多要担心的。当然了,开发人员会定义模板来自动完成enter/exit方法调用,但是他们必须记得始终要这样做而且还要修复重构引起的错误。
为了阐明这种实现将成为多大的负担,考虑一下在项目的整个生存期为实现记录在每个类上花费多长时间。在最近的一次会议上,Adrian Colyer,AspectJ项目的主要开发人员,估测了一下在大中型项目上实现不间断日志记录所投入的开发时间。他估计在整个项目中,开发人员对每个类要花15分钟来实现日志记录。(即使用代码完成,也必须考虑重构造成的影响。)在一个相对小一些、具有500个类的项目中,项目进度表中居然有3周半是花在实现日志记录上!下面通过引入一些方面来实现日志记录以减少花费的时间。
进行重构以使用AOP
在使用AOP之前先决定要使用哪种框架。当前有好几种可选择的AOP框架,我认为,AspectWerkz和AspectJ,基于它们的功能性和全面普及,是最引人注意的。本文将演示如何使用AspectWerkz框架,使用的是2.0版本。
在AspectWerkz和AspectJ之间做出选择是很困难的。幸好,最近AspectWerkz和AspectJ同意联合起来并合作发布 AspectJ 5。当前的AspectWerkz 2.0版本将是最后版本,所有的新发展都将在AspectJ分支中进行。AOP领域的强强联合对开发人员来说最终还是一件好事,因为当前平台的多样性会使初学者望而生畏。此外,将两个强大的开发团队统一成一个更强的统一体将会有助于推广AOP。
在深入研究把方面应用于代码基之前,需要在如何使用AspectWerkz上做一些决定。
