1. 介绍
企业的业务依赖于J2EE应用服务器以提供高可靠性的关键业务应用。这些应用包括自助分类服务, 实时证券管理,和24 x 7 客户服务。
如果这些系统一旦不可用, 将带来客户和资金方面的损失。
J2EE应用监测解决方案需要汇总从众多来源收集的数据, 提供一个用户友好的数据接口, 并且在用户感受到不良响应时间或崩溃之前识别出性能问题。
本文描述了J2EE、如何实现应用管理,识别常用监测热点,和通过向用户提示潜在问题而提供一个应用可用性最大化的解决方案。
2. J2EE的应用环境
J2EE可为企业的关键业务应用提供一个具有扩充性的平台。N层J2EE应用的开发提供了所需的扩展性,冗余, 和应用的客户界面与业务逻辑的分离机制。然而这些应用环境也带来了额外的复杂性和新的可用性和性能风险。
各软件开发团体处于J2EE 集成技术的不同阶段。客户关系管理(CRM) 公司正通过增加基于HTML的接口来扩展他们的应用。J2EE
应用服务器经常在这个时候被推荐用于运行Java Servlets和JSPs。
当产品架构师确定一个应用的确需要更高级的管理时,他可以利用由J2EE应用服务器提供的持久性和事务框架等优势设计应用。服务器负责管理J2EE应用使用的资源,包括:内存,
数据库连接,线程池和缓冲等
J2EE 应用是由多个层次组成的。客户使用Web浏览器访问Web服务器。Web服务器可以独立处理请求,或可以把请求传回到应用服务器。如果一些必要的数据被本地缓冲,
应用服务器将通过Web服务器向客户端返回一个响应。否则, 应用服务器访问远程的数据库和旧的系统,把形成的请求结果发给客户。
3. 数据采集
数据是所有监测解决方案的基础。没有可用的高质量的系统信息, 就无法产生警告和警报。J2EE 应用的性能监测需要汇总从众多来源收集的数据。
第一个来源就是应用服务器自己。除了为应用提供平台之外, 服务器也提供框架的管理。 近来所有主要供应商的应用服务器, 包括BEA,
IBM, Oracle, 和SUN, 都实现了被称为Java Management (JMX) 的标准框架。
JMX 框架由三个层次组成。 第一层是MBeans 。 MBeans 提供配置数据和改变配置值的方法. MBeans 也提供当前资源的使用情况。
例如, MBean 能告诉您EJB 缓存大小的最大值,当前值以及如何改变其最大值。
位于JMX框架的第二层是MBean Server,它负责管理MBeans。 MBean Server包含MBeans 的注册服务并且提供处理MBean的服务。
在上述例子中, 远程应用通过MBean Server检查和管理EJB缓冲的大小。
最后一层包括JMX适配器,用来帮助外部应用访问MBeans。这层是JMX 标准的一部分,但它并不要求一定实现。应用服务器和软件供应商都编写适配器以满足他们各自不同的需要。例如,
基于Web的控制台通过HTTP适配器访问MBean Server来监测EJB缓冲大小。
通过JMX 框架, 应用服务器供应商提供方法可以访问当前使用资源的访问情况,及EJB 和Servlet 容器的配置信息。 虽然这是一个标准的框架,应用服务器供应商可以任意选择提供哪些他们的应用服务器中的属性。一般通过JMX监测的资源包括:EJB
的使用情况,事务,线程池,servlet 池, JMS阈值和存储器大小。
除应用服务器之间的差异,另外还有很多关于应用的信息通常不能通过JMX得到。这种处理,称为instrumentation(注入测量方式),在获取非供应商提供的信息方面是很必要的,包括应用中单个方法的详细情况。有两种测量方式被用于获得方法级数据。第一种是由应用架构师设计并由程序员在应用开发期间实现的。一旦编译,
应用就会提供由架构师指定的性能信息。
在设计阶段,软件监测工具通常不能访问应用的源代码。一般是在部署前的压力测试阶段采用监测解决方案。这时候,使用工具监测源代码将给项目带来风险;对代码的改动和测试将大大地推迟产品发布日期。不修改源代码,
监测工具通常对已编译应用的字节代码采用第二种测量方式。通过在已编译方法的周围增加少量的额外字节代码, 所需的性能信息就可以提供给监测系统。
虽然JMX 经常用来测量EJBs 的许多属性, 但它无法提供EJB 里的所有信息。字节代码测量方式的运用,可以比单独使用JMX获得更深层的运行时数据。
一旦使用这种方式, 就可以获得类和方法的数据,包括响应时间的分布, 使用数量和抛出的异常等。
特别地, JMX 和字节代码测量方式可以对J2EE应用服务器和其中部署的应用提供有价值的观测信息。使用这两个来源的数据, 监测工具就能够精确地报告J2EE应用的有效性和性能,并且向管理员警告潜在的问题。
4. 常见优化热点
4.1. 垃圾回收
应用使用内存的方式会极大地影响其性能;举一个简单的示例,例如创建一个事件对象的新实例,并且在Web层和EJB层之间传递,这在开发和单元测试阶段是看不出问题的,但在压力测试下就会对内存产生极大的影响。例如,
如果各个请求需要使用10K内存, 平均每5秒500 个并发用户的请求数,在运行5 分钟以后, 内存分配给这个10K对象的空间是300MB。把这些和你创建的所有其它对象综合在一起,突发的垃圾回收就会变成一个主要的问题。
Java的一个好处是由虚拟机为你管理所有的内存:这一点有利有弊,虽然你不必承担管理内存的任务,但是你同样不能精确地使用和管理内存。因此JVM有一个线程观察内存并在需要时回收它们。现在有很多版本的虚拟机,但考虑到本文的目的,我们主要集中讨论的是Sun
1.3.1版本的JVM(大部分生产中的应用服务器仍然使用它)。
Sun JVM通过维护两个独立generational(世代)空间来管理内存:young generation和old generation,对象在这两个空间中被分配。通过管理young
generation,JVM能很好处理大部分存活时间非常短的对象,这些对象在创建后很快就符合垃圾回收条件;young generation运行的非常快并且非常有效率,因为它或者回收不再使用的内存或者使用Copying机制把old对象转移到old
generation。它支持三种垃圾回收类型:
Copying(或者scavenge):在generations之间高效率地移动对象;默认为minor回收
Mark-compact:原地回收内存,但明显比Copying慢;默认为major回收
Incremental(或者train):持续回收内存,用来最小化单次回收花费的时间;你必须明确指定"-Xincgc"命令行参数。
默认的垃圾回收行为是,回收在generation之间采用Copying回收类型可以回收的内存,并且当内存使用达到配置的最大值时,将执行mark-compact操作(major回收)。在单一的应用环境中,一次major回收将使应用响应变慢,但这种回收很少发生;然而在企业级应用中,我们看到一个简单请求所执行的操作每5分钟就使用300MB内存。一次major回收对应用服务器的性能的影响是非常大的。一些major回收可能需要花费好几分钟的时间运行,并且在这段时间中你的服务器将没有响应,并且拒绝新的连接请求。
那么你将如何避免major回收呢?或者如果你不能避免,那么你又如何使它对系统的影响最小呢?
JVM调优设计包括以下两个步骤:
1. 设置执行事务的堆大小可以支持在用户压力下运行事务。
2. 设置generation大小用于最大化minor回收。
默认的JVM行为可以很好地 支持独立(Stand-alone)应用的运行,但对于企业级应用就很难确定;对于不同的操作系统,默认的大小也有所不同,但都没有针对具体的企业应用做性能优化。考虑运行在Solaris上的JVM,它的堆大小最大值为64MB,其中给young
generation分配了32MB。假如你的请求在5分钟内分配300MB,或者每分钟60MB,而整个堆是64M,并且想想在虚拟机中并不只运行着你的请求,所以这个堆实在太小了。首要原则是把尽可能多的内存分配给虚拟机。
当设置generation大小时,我们必须仔细看看young generation的结构:它有一个用于创建对象的区域叫Eden,并且定义了两个survivor
空间;其中一个survivor空间一直为空并且是后续复制操作的目的空间,在两个survivor空间之间复制对象,直到这些对象的age(年龄)符合old
generation的条件。正确地设置survivor空间大小非常重要,因为如果设置的太小,minor回收将不能从Eden中一次性把所有需要的对象复制到survivor空间中,这样就得频繁运行mimor回收,并且强迫它过早地将对象移动到old
generation中。在Solaris下,survivor 空间的默认大小是整个young generation大小的1/27,这对于大多数的企业级应用可能太小。
下一个问题是如何设置young generation本身。如果你不存在频繁major回收的问题,最佳实践是分配给young
generation的内存,达到整个堆的一半。
做了这些改变之后,在系统处于压力作用下观察堆以及垃圾回收的行为,并根据具体情况调整它的大小。
4.2. 实体Bean缓冲
实体Bean定义了运行在应用和持久数据之间的对象模型;实体Beans通常跟数据库通信,尽管它们并非只局限与数据库通信。考虑到我们这里的讨论目的,我们假设实体Beans与数据库通信,但是本讨论对于以前的旧系统或者其它的实现也是一样的。
在一个拥有正确实现的Java企业应用中,你将用实体Beans代表数据持久化,这不仅是J2EE初始的目标,而且也因为应用服务器提供的缓冲机制,为你管理实体Beans。一个数据请求的生命周期如下:
1. 一个组件请求和实体Bean
2. 使用其代表的持久信息加载和初始化实体Bean
3. 为了以后的引用,实体Bean存储在实体Bean缓冲池中(激活到缓冲中)
4. 将实体Bean的远程(或本地)接口返回给请求的组件
后续的请求(对数据库的查询)将由实体Bean缓冲直接提供服务,避免了对象的创建和初始化;因此减少对数据库的访问,极大地提高了性能。缓冲的特性是预先定义好大小,指定能够持有的对象数量,并且使用基于内部的算法管理这些对象的生命周期。例如,它可能把最近经常访问的对象放在缓冲中,并且把很少访问的对象移走,用来为新的对象腾出空间。因为缓冲预先设定了大小,缓冲的大小对性能影响会很大。
当谈到实体Bean 缓冲时,我们把对象从持久性存储加载到缓冲池的过程称为激活,而把对象从缓冲中存储到持久性存储介质的过程称为钝化。实体Beans被激活放在缓冲中,直到缓冲满了并且有新的实体Bean被请求时,一些现有的bean必须被钝化,这样才能激活新的bean。激活和钝化bean会增加系统的负载,如果执行过多的激活和钝化,实际上就抵消了缓冲的作用。我们把这种频繁激活和钝化的状态成为"颠簸"。
当调优实体Bean缓冲的大小时,我们目的是调整其大小使缓冲能够为大部分请求服务,从而减少"颠簸"。对每个可调节参数,都需要权衡:大的缓冲比小的缓冲需要更多的系统资源(内存)。所以你的目的另一方面就是确保缓冲足够用但又不能太大。
调优的过程是使用典型的事务对应用进行负载测试,并使用监测工具观察实体Bean 缓冲的行为,密切注意:
请求数量
缓冲所服务的请求数量(命中率)
钝化的数量
激活的数量
如果你看到了过多的激活和钝化,这时需要增加缓冲大小,直到很少发生激活和钝化或者不发生为止。这样就缩短了请求的响应时间并减少了数据库访问。
4.3. 分割(Segmentation)
资源的分割是指:为特定的应用组件分配特定的资源集合。分割能应用于线程池和JDBC连接池。
所有应用服务器都有线程池,用于服务进入的请求;在WebLogic中,这些线程池被称为执行线程,并且包含在一个或更多的执行队列中;而在WebSphere中则称为线程池。不管它们是如何实现的,请求到达应用服务器后,或者进入队列等待,或者直接分配到一个线程去处理。当该线程可用时,它处理该请求,并执行一些业务逻辑并把响应结果返回给调用者。在大多数的应用服务器中,默认的行为是把所有的应用和组件分配到同一个的线程池。如果应用中的所有组件的业务价值是相同的,并且允许一个业务过程在队列中等待,且由于另一个业务过程正在运行,等待时间是不确定的,那么这是可接受的模型。尽管如此,在实际的应用中,某些业务过程比其它的更具有价值,不管怎样,一个业务过程不应该限制另一个业务过程的性能。考虑部署以下组件:
E-Commerce Store Front-End(电子商务前端店面)
E-Commerce Checkout 和Billing Component(电子商务结算和支付组件)
Administration Component(管理组件)
这三个中的每一个组件对于企业都有特定的目的和价值:前端店面允许客户浏览公司的产品,比较价格,并添加产品到他的购物车中;结算和支付组件负责收集客户属性和信用卡信息,连接信用卡支付服务器获得购买的确认信息和把购买金额记入客户的信用卡中,并且储存记录到数据库中用于以后引用;管理组件是管理你的电子商店,跟踪订单和管理存货等的入口。因此每一个组件对于企业都有特定的目的和价值,它们每一个必须能够高效率地执行。例如,当一个客户在浏览商店时不应该限制其它客户下订单,同样客户下订单的时候也不应该使其它客户浏览商店的速度减慢下来;浏览者一定可成为顾客,并且正在购买商品的客户必须能够完成交易,这才能是公司获得收入。最后,万一发生问题时,管理组件必须能够与其它两个组件一起同时运行;例如,如果信用卡支付公司改变了它的在线地址或者如果公司的帐户更新了,这些改变的更新对于客户必须是无缝的。
你如何才能保证每个组件都有访问系统资源的公平权力呢?因为只有这样才能完成任务。
答案是为每个组件创建单独的线程池,使得在运行时只有它有权访问。为满足系统的需要和用户负载,需要调整每个线程池大小;访问组件越频繁,需要的线程越多,例如前端店面,就比结算和支付需要更多的线程,而结算和支付需要的线程又比管理组件需要的多。如果系统在高的负载下,前端店面满负荷运行,则客户仍可以下订单并且商店管理员也能够连接到管理组件。
这个概念可以进一步推广到数据库连接池层。即使你的所有底层持久性数据都存在一个数据库中,也可以考虑创建多个连接池连接到该数据库,这些连接池有各自的业务功能。浏览商店的组件应该由其中的一个连接池提供服务,而支持客户下订单的组件由另一个连接池提供服务。这将可以避免两个组件之间由于共享一个连接池所造成的数据库资源竞争。
按照业务逻辑功能分割线程和数据库连接池可以保证应用在压力下能够响应请求,并且确保关键过程的良好运行。其结果是调优成本费用增加了,这是因为有很多地方需要调优,但是最终用户体验将极大地提高,这才是性能调优的真正目标。
5. 解决方案(Segmentation)
应用管理员负责管理和看管J2EE应用服务器,当出现问题时,作为管理员需要做初始选择,确定解决问题的优先次序。通常当最终用户不能完成一个交易时,就会报告问题,管理员则需要分析该问题并且推动解决问题的过程。
在该做问题的初始选择阶段时,需要快速发现性能瓶颈的位置。要找到问题所在,管理员开始从整个应用环境查看。该环境是由一些节点组成的,包括Web服务器,应用服务器,数据库和各种各样的旧系统。
5.1. 直观的警告
监测工具应提供24×7无人守护的监测解决方案。客户只需为J2EE应用制定一些J2EE应用规则,过滤通过JMX,测量和日志文件收集来的数据,当发现问题时,可根据这些规则就可以向应用管理员发出警告。规则编辑器是基于性能的变量集合,这些变量将满足客户的需求,并且可以在多个规则中重用这些变量。规则触发器警告可以有多个条件。根据警告的不同严重级别,可以向一个或多个管理员发送电子邮件。每一个警告信息含有问题的严重级别和问题描述。从警告中,管理员能够分析到警告的细节,并且访问帮助系统以获得更深入的信息。
标准规则可用于监测J2EE应用和应用服务器的可用性和性能。规则能够监视阈值的超过情况,比如活动的线程数达到100或者可用的应用服务器从10个下降到4个的时间。不必是固定的值,例如当90%的JDBC连接在使用或者当可用的服务器数下降到40%时,规则能够触发警告。第三种规则能够把当前的资源使用情况跟平均的资源使用情况作比较,必要时它会触发警告。例如,当servlet平均响应时间大于平均运行时间59%时,将能够发送电子邮件警告。这种规则可以知道应用的一般行为,并且当应用响应不正常时就会给管理员发出警告。
5.2. 丰富的图表和数据视图
对于J2EE应用,用户需要使用易读的图表快速访问资源使用情况,可用性,EJB和Servlet的性能。也可以创建,存储和共享额外的图表。
以下是值得关注的20个常见问题:
1. 上周我的服务器可用性怎样?
2. 集群(工作组)中有多少个服务器是可用的?
3. 每一个应用中有多少个Entity Beans是活动的(在缓冲中)?
4. 每个应用中有多少个Session Bean是活动的?
5. 每个执行队列有多少个线程正在使用?
6. 垃圾回收期间有多少内存被回收了?
7. 当前正在使用多少JDBC连接?
8. JMS服务器的总体使用情况是什么样的?
9. 所有的JMS Topics和Queues的消息和字节使用情况是什么样的?
10. 对于每个应用当前有多少个HTTP Session是处于活动状态的?
11. 过去24小时中可用的JVM堆是多少?
12. 系统已经执行了多少个事务?
13. 今天应用回滚的事务很多吗?
14. 有多少回滚是由JTA Resource引起的?
15. 每个被监视的类的平均响应时间是多少?
16. 每个被监视的方法的平均响应时间是多少?
17. 哪个方法引起我的类响应不正常?
18. 对于给定的方法,怎么比较该方法的最近响应时间和平均响应时间?
19. 每个被监视的Servlet的平均响应时间是多少?
20. 每个被监视的Servlet被调用了多少次?
6. 结论
随着供应商不断地把自己的应用迁移到J2EE平台,同时IT部门采用Java作为它们首选开发环境的标准,J2EE应用的数量在不断增加。作为程序员和架构师,以前还可以充当应用管理员,但是现在已经没有时间照看生产系统了。自定义脚本和单点监测产品已经不足以确保客户当前所需要的高可用性。每个公司必须具有一个能分析覆盖应用环境所有层关键数据的全面的解决方案。
|