在 UNIX 操作系统上优化 N 层 J2EE 应用程序,分析、优化并采用最佳实践,提升基于 Web 的应用程序的性能

UNIX® 和类 UNIX 操作系统通过 Internet 托管大部分 Web 浏览器和基于 Web 的应用程序。尽管这些操作系统主要针对多处理、网络和性能进行设计,但是如今的分布式 n 层 Web 应用程序引入了其他很多可影响应用程序性能的因素。

常用缩写词
  • API:应用程序编程接口(Application programming interface)
  • FTP:文件传输协议(File Transfer Protocol)
  • HTML:超文本标记语言(Hypertext Markup Language)
  • I/O:输入/输出(Input/output)
  • IT:信息技术(Information technology)
  • SDK:软件开发工具箱(Software development kit)
  • SQL:结构化查询语言(Structured Query Language)
  • XML:可扩展标记语言(Extensible Markup Language)

在 Web 的早期,Web 服务器使用 HTML 以及位于给定 Web 服务器的本地图形文件交付静态数据。尽管这种方法适用于静态类内容,但是,企业应用程序需要访问动态的内容,包括客户信息、库存和定价信息,以及其他频繁变化、保存在数据库而非 Web 服务器内容目录(通常保存在数据库之类的外部资源中)的信息。

通过允许 Web 服务器直接访问包含动态内容的其他数据资源(比如数据库),2 层 Web 架构可以满足这些需求。2 层 Web 应用程序 是一种标准的客户机服务器系统,通过使用诸如 Common Gateway Interface (CGI) 之类的数据接口协议和各种 Web 脚本语言实现 Web 服务器和外部数据资源之间的直接通信。面向 Web 应用程序和相关数据源的所有连接和业务逻辑都在 Web 应用程序内完成编码,并且不管数据是本地存储还是远程存储,该应用程序都可以直接访问所需数据。UNIX 和类 UNIX 操作系统是 2 层 Web 应用程序的理想选择,因为它们的设计目标就是实现多处理、支持大量物理内存,并且包括高性能虚拟内存功能来解决应用程序内存需求超出物理内存容量的问题。

然而,不管底层操作系统和硬件有多么强大,2 层应用程序在处理大量请求或维护大量 Web 客户机的状态信息时变得有些低效,因为应用程序的所有组件通常都运行在同一台机器上。使用数据库和 Web 服务器上的本地数据源将增加复制和备份的难度,并且加重了 Web 服务器的负荷。同样,如果系统被侵入并受到破坏,那么将数据库或数据库访问信息存储在正在运行 Web 服务器的机器上会引发安全性问题。

n 层 Web 应用程序架构解决了这些问题,它在不同的机器上运行不同的服务,并使用称为应用服务器 的中间件充当 Web 应用程序逻辑和远程数据需求之间的中介层。N 层架构提供了大量其他简单架构无法比拟的优势,其中包括:

  • 提升高通信量 Web 服务器的性能,因为 Web 应用程序要访问的数据存储在其他系统中。使用除 Web 服务器或应用服务器以外的系统运行数据库(或其他数据)服务器可以降低这些系统上的整体负载,提供更多可用于这些服务器的资源。类似地,诸如对远程数据库、内容管理系统(CMS)和其他数据源执行备份之类的管理任务并不会影响 Web 服务器或应用服务器的性能。
  • 通过实现可伸缩性改善服务器和数据资源管理。Web 服务器和应用服务器可以轻松地执行复制,从而实现负载平衡。通过在多个 Web 服务器和应用服务器之间实现自动故障转移,3 层架构可以提高 Web 应用程序的可用性。与远程资源的连接可以实现智能管理。
  • 提高远程数据的安全性,因为远程数据始终存储在单独的系统中,而不是存储在正在运行 Web 服务器(以及应用服务器)的系统中。对 Web 服务器的入侵和破坏并不会直接暴露私有数据和业务关键数据。

N 层架构依赖于多个计算机系统的相互操作,根据所提供的服务的类型,每个计算机系统具有不同的性能考虑因素。这些计算机系统之间的互操作常常需要对 IT 基础设施进行额外的投入,这是因为对网络负载和应用程序响应性的需求更加迫切,包括与监视和同步多个系统来支持冗余和故障转移所需的额外通信量,以及与集中化网络附加存储(Network Attached Storage,NAS)和存储区域网络(Storage Area Network,SAN)数据存储资源相关的网络通信量。

UNIX 和类 UNIX 操作系统一直在高性能联网、分布式应用程序和分布式存储需求方面保持领先,这些需求是实现健壮的、高性能的 n 层 Web 应用程序的基础。然而,除了使应用程序更加易于理解、更加容易维护、更具可伸缩性外,对 n 层应用程序本身实现的优化还可以大幅提升这些应用程序的性能。

面向 n 层应用程序的编码标准的最佳实践

优秀的应用程序设计和编码实践是实现高性能、可维护的应用程序的基础。对应用程序组件采用模块化设计并理解组件之间的交互,将简化性能问题的调试、测试、跟踪和应用程序更新。

后面的小节将着重介绍一些面向 n 层应用程序的标准的最佳设计和编码实践。以下用于实现优秀 n 层应用程序设计的 “五大规则” 将为基于 Web 的应用程序提供一个坚实的基础,但是在处理遭遇性能和可靠性问题的现有 n 层应用程序时可能不太管用。分析并优化 n 层应用程序 这一节将讨论对现有应用程序进行检查并提升其性能的各种方法。

尽量使用 MVC 方法

面向应用程序设计的模型-视图-控制器(Model-View-Controller,MVC)框架最早出现于 20 世纪 80 年代,时至今日,它仍然是一种十分有用的框架。除了协调应用程序的表示(视图)和业务逻辑(模型)外,还将这两者分离开来,并始终坚持这种分离,简化了设计、调试、测试和维护。

可测试性设计

高级的设计原则,比如 MVC 和模块化应用程序设计,将功能具体化为更加易于测试的代码,因为每个类和方法都实现一个具体的目的。在编写代码时,不需要在测试期间为了查看预期的优势而去掌握诸如敏捷编程、极限编程或测试优先设计等方法。在编写应用程序时,最简单也最有效的方法就是包含测试点,即挂钩(hook),因为这使测试点成为设计流程的一个健壮部分,而无需在事后将测试点接入到现有应用程序中。

安全性设计

安全性对于与业务有关的基于 Web 的应用程序至关重要。然而,由于安全性对于开发人员来说是个令人头痛的问题,因此安全性经常是在开发流程的后期添加或激活的。不管安全性是由 IBM® WebSphere® Application Server 之类的应用服务器提供,还是由某个外部服务提供,都应当在开发过程中支持安全性。性能影响和比较复杂的调试等常见问题常常被过分夸大,但是如果这些问题确实会对应用程序产生影响,那么最好在部署应用程序之前尽早了解到这一点。

构建日志记录功能

在实际实现应用程序组件时,每个组件的逻辑都比较陌生。诸如 Log4J 和 Java™ 软件开发工具(JDK)Trace APIs(参见 参考资料)之类的开放框架不仅易于使用,而且很容易在编写代码时并入到其中。将日志和跟踪功能集成到应用程序中,不仅可以简化调试,同时可以使其他人在完成部署后更加方便地监视和调试应用程序。

日志记录的一个缺点就是,每次记录日志都需要执行 I/O,这将降低应用程序的性能。Log4J 之类的软件包(参见 参考资料)提供了不同级别的日志记录来最小化 I/O。您可以使用 API 调用(比如 Log4J 的 org.apache.log4j.PropertyConfigurator.configureAndWatch() 方法)监视额外的属性文件,可以使用这些文件动态激活不同日志记录级别,或是禁用运行中的应用程序的日志记录功能。

避免混淆

与复杂的、经过大量优化的代码相比,易于理解的简单代码更加易于调试、优化和维护。现在很少需要哄骗您的 Java Virtual Machine (JVM) 或其编译器。针对具体环境或平台进行优化的代码可能不适用于其他情况,并且常常难以扩展现有的功能。

分析并优化 n 层应用程序

n 层 Web 应用程序的典型问题与其他应用程序无异:启动时间过长、整体性能较差、无法解决某些具体问题以及应用程序故障。尽管引起其中任何一种问题的原因有很多,但是下面的小节将介绍一些比较常见的原因,帮助了解如何改善现有应用程序的性能。

引起性能问题的原因有很多种,比如应用程序的实现方式、应用程序的操作系统、系统使用的远程资源,或者所有这些因素的结合。Heisenberg 的测不准原理(uncertainty principle)告诉我们,对系统执行检查会改变系统的行为,但这并不表示不应使用手头的所有工具解决性能问题,或是想尽办法提升和优化性能(即使当前性能足够满足需要)。

对架构和代码同时执行检查

很多性能问题常常和应用程序架构有关,而不是由具体的代码问题引起的。大量调用外部资源会浪费应用程序的时间,同时影响其他使用相同资源的应用程序的性能。类似地,使用 XML 之类的标准格式对数据交换非常有益,但是如果应用程序执行重复的或过量的转换,那么这样做将对性能造成负面影响。

检查使用 EJB 技术的方式

Enterprise JavaBean (EJB) 技术是 Java 2 Platform, Enterprise Edition (J2EE) 容器内的一个托管组件。该容器为管理资源、事务和并行性之类的内容提供了自动支持,但是也会为应用程序引入大量负载负荷。

为了缩短系统启动时间并降低整体负荷,应尽可能使用非持久性会话 bean,而不应使用实体 bean(将自动维护状态信息)。一般而言,应当尽量使用无状态会话 bean 而不是有状态会话 bean(参见 参考资料 中的 “Java EE 最佳实践”,了解如何使用各种类型的 bean 以及其他内容)。

问题识别

如果应用程序的性能从整体上来看比较差,那么应激活日志记录功能来通过日志消息时间戳识别发生问题的位置。日志记录功能肯定会影响性能,但是如果通过一致的方式记录日志,那么这种影响将均匀分布到整个应用程序中。还可以使用性能监视软件(将在 后面的小节 中讨论)识别问题所在,或使用标准的系统监视软件识别与系统资源限制相关的性能问题。

解决与资源相关的瓶颈问题

对于 J2EE 应用程序来说,数据库访问和响应性是一个常见的问题来源。本节将识别您所使用的数据库驱动程序,并了解是否可以使用其他性能更好的备选方法。

在数据库一端,检查 Java Database Connectivity (JDBC) 驱动程序正在发出的查询,以了解当前发出的调用是否过多,或者调用本身是否为最佳调用。对数据库执行检查,看看是否可以通过调优提高正在发出的 SQL 语句的性能。简单的数据库端问题,比如磁盘变慢或索引丢失,常常是引起问题的真正原因。

使用性能监视工具

大多数应用服务器提供了内部性能管理软件,可以使用它获得关于整个应用程序的详细信息,并且它对性能的影响也非常小。例如,WebSphere Application Server 提供了一个 Performance and Diagnostic Advisor,该工具在应用服务器的 JVM 进程上运行,对整个系统的影响也很有限。类似地,IBM Tivoli® Performance Viewer 提供的性能顾问程序运行在应用服务器的 JVM 中并收集 Performance Monitoring Infrastructure (PMI) 数据,这些数据可以帮助识别非最佳应用服务器设置和特定的应用程序问题。

使用可用的系统监视工具

所有 UNIX 系统都提供了 psvmstat 之类的工具,通过它们提供进程和虚拟内存统计信息。可用于识别进程所用资源的开源工具,也可用于所有 UNIX 系统。这类工具可同时用于运行应用服务器的系统和数据库之类的远程资源。比如,高虚拟内存使用率表示系统仅需增加一些内存来提升性能。类似地,持续升高的内存使用率表示应当解决内存泄漏问题。

利用标准 Java、JVM 和系统优化

每发布一个新的 JVM 版本都将改善性能和可靠性。对 Java 代码进行优化要比对使用传统语言编写的代码进行优化困难得多,因为大部分 JVM 都可以响应不断变化的条件和配置反馈。但是,您仍然可以使用大量针对 J2EE、Java 技术和 JVM 优化的标准方法来提升应用程序性能。

下面的小节将着重介绍一些常见的优化,可以提供有限的应用程序性能提升。利用标准 J2EE、Java 编码和 JVM 优化可以提供基本的改进,从而提升整个应用程序并实现显著的性能改进。

基准对象分配和对象重用

J2EE 应用程序和 Java 技术的对象分配较慢这一缺点一直都为人诟病。在过去确实如此,然而最新一代的 JVM 在这方面有所改善。但是,研究一下 JVM 在特定环境中的执行情况仍然不失为一个好主意。

分配开销具体取决于可用的资源和特定 JVM 的性能以及 J2EE 实现。最好编写一些简单的基准测试来比较一下在 J2EE/JVM 环境中分配新对象的成本和重用现有对象的成本。除非对现有应用程序执行更新,否则这些信息没有什么用处,但是对于编写新应用程序十分有帮助。

释放内存

内存泄漏导致应用程序的大小随时间不断增长,该问题在 Java 应用程序中尤为突出,因为消耗完堆空间后将触发 JVM 的垃圾收集。使用上文讨论的系统监视工具或性能监视工具监视应用程序的内存使用情况,并检查代码以确保在处理完分配的对象后总是能够释放它们。一些内存泄漏问题常常不易发觉,比如 HashtableVector 之类的 Java Collections 类中的内存泄漏,这些类在删除了所有其他引用后仍然会保持对对象的引用。

减少显式垃圾收集的次数

如今的 JVM 在何时需要执行垃圾收集以及收回哪些对象方面表现得十分智能。同时,垃圾收集是一项 JVM 操作,对性能会产生很大的影响。如果需要执行显式的垃圾收集来降低应用程序内存使用,那么尝试在低负载情况下或在非繁忙时刻实现垃圾收集。

优化系统垃圾收集

大多数现代 JVM,比如 IBM Java 版本 5.0 及更高版本,都支持多种垃圾收集策略。检查您的 JVM 所支持的策略,看看使用不同的策略是否会改善吞吐量与垃圾收集引入的暂停之间的平衡关系。

研究 JVM 堆调优

堆调优 控制单独 JVM 和其中运行的应用服务器实例可以使用的内存量。堆调优涉及两个参数:堆的初始大小和最大大小。最大堆大小应当始终小于物理内存 — 当堆开始换入磁盘后,Java 性能将迅速降低。它必须足够低,以在物理内存的范围内包含堆。一条通用规则是将初始堆大小设为最大堆大小的 25%。

最大化系统内存

大多数 UNIX 系统都支持大量内存。Java 应用程序和 JVM 本身都需要使用大量内存。最大化系统内存能够增加 Java 堆的大小,同时仍然确保它小于物理内存。最大化系统内存通常会通过减少内存交换来提升系统系能。

减少运行的进程

将关键系统上运行的进程的数量减到最少应当成为部署流程的一个标准组成部分,但是这一点常常被忽视。尽管在 Linux® 系统上更加常见,但是许多 UNIX 系统都会启动或提供实际上不会在这些系统上使用的服务器进程或服务。常见的、经常被忽视的例子包括 FTP 之类的服务和 getty 进程,后者将等待目前也许永远不会发生的串口登录(serial login)。

检查您的系统以确保没有运行多余的进程,并且终止实际上没有必要运行的任何进程,从而为业务关键型应用程序释放处理器和内存资源。减少运行进程的数量还有另外一个好处,就是通过减少或去除系统访问点来提高安全性。

结束语

优化常常被看作是一种事后行为,就是说在应用程序已经编写完并且可以正常工作之后再执行优化。这种方法通常不太高明。在应用程序设计和实现过程中应用良好的设计决策和最佳实践可以防止许多可能需要优化的编码问题,从而能够对应用程序执行预先优化。

Donald Knuth 曾经指出,“提前优化是万恶之源”。尽管这句话通常是正确的,但是性能分析和优化机会分析应当成为每个应用程序的部署和维护流程的一部分。本文首先讨论了一些可以在创建应用程序之前决定的设计决策,从而获得易于维护、易于测试、可良好执行的应用程序,并提供了挂钩以便在出现问题时简化调试工作。后续的小节讨论了用于在现有应用程序中识别问题根源并加以解决的技术,并着重介绍了标准 J2EE、Java 编码和 JVM 优化方法,这些方法可以提升新应用程序和现有应用程序的性能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值