晋升EJB机能的12招
副标题#e#
Enterprise JavaBeans(EJB)是一种被遍及回收的基于J2EE平台上的处事端组件体系架构,它可以用来快速开拓机动的、可重用的、可移植到各其中间件平台上的以任务为要害的企业级应用。同时,由于回收开放的协作拟定类型,EJB架构能掩护IT投资,淘汰对供给商的依赖性,制止范围在某一个供给商的技能实现上。个中,机能是EJB应用措施成败的要害。
为了开拓高机能的企业级应用,我们必需意识到精采的设计和编程类型是机能晋升的基本。抱负状态是在开拓前我们必需先行把握优秀的编程类型。而到了开拓后期,再将重点转移到整个应用措施机能的调解:包罗Web处事器, 应用处事器业以及数据库。计策是找出懦弱的一环,然后举办改造。为到达最佳机能,本文将接头一些通用的能力,辅佐我们设计和实现一个高机能的基于EJB的企业级应用。
EJB的开拓
我们前八个EJB机能方面的能力是关于在应用系统设计和开拓阶段的。因为在处事器情况下,大都需要优化的代码,更多地是在布署阶段之前。以便在问题不行收拾前把问题找出并办理。
1. 设计粗粒度(coarse-grained) 的EJB 长途(remote) 接口
由于每个通过长途或当地(home)接口挪用EJB的要领都属于长途挪用,对付大大都细粒度(fine-grained)的工具交互来说挪用中间件的开销大得的确让人无法遭受。为了制止这类问题, 每个EJB应代表一个独立的业务工具,有独立的特性和生命周期。具有依赖性的工具决不能用EJB暗示。相反,它应该作为一个EJB内部的Java类来实现。譬喻,一张购物单可用一个EJB来实现,而每张购物单上的子项就不该该用EJB,可以回收helper类的形式实现。
2. 设计粗粒度的EJB长途接口要领
正如上面所提到的,每个客户端对EJB的挪用都是长途挪用,别的还要举办一些查抄操纵,好比存取节制,事务处理惩罚以及激活/休眠等。 因此,EJB挪用比长途挪用要慢上好几倍,与挪用当处所法的时间相差就更大了。淘汰要领的挪用次数可以提高机能。设计粗线优化的要领中的一个能力,就是在单个要领挪用进程中只管增加数据往返传输的数量,把多个要领归并以淘汰要领的个数。好比,代码段一显示的是一个设计欠佳的长途接口,因为客户端必需多次挪用来取得Person的数据。对比之下代码段二改造了机能表示,因为它在一次要领挪用中利用了粗粒度的要领,要领的个数变少了,从而淘汰了挪用次数(尽量这些要领传输的数据更多)。
3. 淘汰JNDI 查找次数
由于各类应用处事器的定名和目次处事在详细实现要领上的差别,JNDI 的查找进程大概会较量费时。 (注:JNDI对EJB资源,好比数据源、bean引用,以致情况项(environment entry)的查找大概耗费庞大,且制止反复查找并非易事。)某些应用处事器在差异的呆板上回收单独的历程来实现目次信息。在这种环境下缓存home handle是一个晋升机能的好步伐。(见代码段3). facade是一个工具,它为多个工具提供一个的统一接口。一个处事端的facade能简化处理惩罚,使得客户端不需相识每个处事器端的EJB(见图一)。在这种方法下所有客户端对处事器的挪用都经过工具ServerFacade。如代码段3所示,ServerFacade是一个会话 EJB,缓存了所有需要的EJB的home handle.而客户也可以在客户端用singleton工具来缓存ServerFacade的home handle。
图 1.客户端通过ServerFacade会见处事器。ServerFacade是一个会话 EJB,缓存了所有需要的EJB的home handle.
4. 操作会话 bean的包从一个实体 bean中返回多行数据
凡是,在我们的应用措施中一个bean暗示数据库的一个逻辑行。实体 bean的当地接口中界说了一个或多个finder要领。每个要领用于查找一个实体工具或多个实体工具的荟萃。 在客户端通过挪用实体bean的finder要领来取得实体工具的荟萃。 然后客户端从该荟萃依次取得数据。假如是长途客户端,每次远进程挪用取得一行数据,资源开销太大了。我们可以回收一个在处事端的会话bean把所有的行打包,作为一个实体当地工具的finder要领的返回功效。代码段4 显示了一个会话 bean工具如何通过一次挪用把多行数据的向量/荟萃发送到客户端。该要领可用在当GUI数据(如列表框或电子表格的数据)保持静态稳定的环境。通过扩充这种步伐也可以支持客户端的数据修改。
5.回收会话 bean来处理惩罚大量的数据库操纵
实体bean适于一次处理惩罚数据库的一个逻辑行,可是在数据库的批量操纵中会激发机能方面的许多特别开销。好比,一个返回1000行的实体bean的finder要领需要执行1001次数据库的select操纵,导致机能急剧下降。假如用会话 bean来处理惩罚数据库的批量操纵可以显著的提高机能(见代码段5),因为它淘汰了挪用数据库的次数。
6.尽大概回收CMP(container-managed persistence)容器打点bean取代 BMP(bean-managed persistence bean)
#p#分页标题#e#
在BMP方法中, 编写bean的人员必需本身在bean的要领顶用JDBC,SQLJ等提供数据库会见代码。而在CMP方法中,由容器在EJB陈设是自动生成数据库会见代码。编写bean的人员只需在陈设描写器中指定要自动生成会见代码的实例的字段清单。 CMP的利益是使bean类能独立于数据源。别的的长处是应用处事器的厂家为CMP生成优化代码,以晋升数据库会识趣能。
7.得当的利用实体bean
在某些特定的场所下适合利用实体bean。他们合用于只从数据库中取出少量数据行供多个用户同时会见。好比,在Person中,当小我私家登录系统后,他的记录可以缓存起来并让多个EJB会见,如SecurityBean, BankAccountBean等等。因此Person可以实体bean的形式存在。实体bean还合用于那些数据行需要在多个事务中被频繁更新的场所。
由于机能方面的特别开销,实体bean 不符合大量数据行在数据库处理惩罚中的选取、插入或更新。这种环境下可思量回收会话 bean,别的也可以在实体 bean中设计粗线优化的数据库会见操纵。
8.回收得当的断绝级别
断绝条理是指多个瓜代举办的事务处理惩罚之间制止彼此滋扰的水平。TRANSACTION_SERIALIZABLE是最高断绝条理。回收这个断绝级别会使整个进程相当迟钝,因为所有的事务处理惩罚(既即是简朴的读操纵)必需排成一队依次执行。执行的要领取得独有写入数据的锁定,制止其它事务处理惩罚对数据举办读取、更新或插入行动,直到该事务乐成完成并提交事情。在TRANSACTION_REPEATABLE_READ级别,事务处理惩罚无法修改那些正在被其它事务处理惩罚读取的数据。写入锁定用来防备其它事务处理惩罚对数据的修改。回收 TRANSACTION_READ_COMMITED 级别时,当前事务不能其他事务操纵数据的中间状态。因为它无法对正在被其它事务处理惩罚修改的数据加上写入锁定,所以也就无法读取修改一半的数据了。TRANSACTION_READ_UNCOM- MITED是最低的断绝级别。在这种级别中Bean的要领可以读取操纵数据的中间状态,可是时他们无法得知是否增加了新的记录。
断绝级别越高机能上开销也越大。凡是环境下,较低的断绝级别答允更多的并发操纵 ,价钱是需要越发巨大的逻辑去处理惩罚潜在的数据纷歧致性的问题。一条有用的准则是在该企业信息系统可接管的机能范畴内回收尽大概高的断绝级别。
#p#副标题#e#
设计和开拓后期
在一个企业级应用的开拓后期阶段我们必需存眷与机能相关的部门对整个系统举办调解。余下的4点就是针对工程的这个阶段。
9.模仿实际运行情况举办调解
这里的计策是查察整个系统,包罗Web处事器,应用处事器和数据库处事器。可以思量执行以下任务:
运行整个应用措施
利用厂家推荐的版本和补丁
模仿实际运行的数据
利用实际运行的资源(好比设备)
操作负载测试东西模仿用户负载
10. 操作东西识别机能瓶颈
在这个要领中,我们使系统高负载并监督所有呆板的CPU,内存 , I/O和网络的利用环境。识别机能瓶颈的处所后,重复测试,查察详细的问题地址。代码段1列出了各类用于测试措施的各方面机能的东西。表1列出了各类用于测试措施的各方面机能的东西。
应用范畴 | 东西举例 |
CPU 利用 | Unix中的"top"呼吁 (www.groupsys.com/topinfo) |
内存利用 | Unix中的"top"呼吁 |
I/O 利用 | Unix中的"iostat"呼吁 |
网络利用 | "netstat" 呼吁 |
Java 代码阐明 | 仓库阐明东西Heap Analysis Tool (java.sun.com/people/billf/heap)
Optimizeit (www.optimizeit.com) jProf (http://starship.python.net/crew/garyp/jProf.html) JProbe(www.sitraka.com/software/jprobe) PerfAnal (http://developer.java.sun.com/developer/technicalArticles/Programming/perfanal) |
负载测试 | LoadRunner (www-svca.mercuryinteractive.com/products/loadrunner)
e-LOAD (www.rswsoftware.com/products/eload_index.shtml) Bean-test (www.testmybeans.com) Web Bench (www.webbench.com) Microsoft Web Application Stress tool (http://webtool.rte.microsoft.com) |
SQL 跟踪 | Oracle 中的Facility 与 TKPROF |
表1利便的东西. 要使EJB 应用措施取得高机能,重要的一点是识别机能瓶颈,可利用以上这些东西。
11.调解系统参数
#p#分页标题#e#
应用措施中的很多参数是是可调的。譬喻,在操纵系统中,我们可以调解TCP/IP参数,文件限制数,历程限制数和IPC限制数。在应用处事器上则可以调解毗连池参数,EJB池巨细,线程数,JVM个数以及JVM仓库巨细。在数据库处事器上,我们可以调解毗连和历程的个数,共享池的巨细,缓冲区巨细,索引和SQL查询提示。在Web处事器上,可以调解保持毗连的参数,线程/历程的个数和毗连的backlog.
12.回收群集来满意高负载或思量进级硬件
有时到达了某种水平时调解代码以致整个系统会比增加新硬件的开销更大。这种环境下群集有显著的优势。大大都应用处事器都提供了群集的特性。一个EJB本质上就是在多台呆板上运行的一组EJB处事器,用于增加系统的马力。一般群会合的每台应用处事器都含有沟通的EJB。处事器群集回收各类差异的算法来实现负载平衡,譬喻round robin, random, sticky bit, server load等。可以在多个条理上回收群集,如图2所示。
图2: 群集的群集:在差异条理的群集增加了系统的马力
以上12点可以辅佐我们设计和实现一个高机能的基于EJB的企业级应用。综合利用这些能力以使我们的应用措施到达最优化。
代码段 1. 一个设计欠佳的长途接口
若利用以下接口,客户端必需多次挪用才气获取数据。我们可以通过利用粗粒度的要领加快这个进程。
public interface Person extends EJBObject
{
public String getFirstName()
throws RemoteException;
public void setFirstName(String firstName)
throws RemoteException;
public String getLastName()
throws RemoteException;
public void setLastName(String lastName)
throws RemoteException;
public int getPersonId()
throws RemoteException;
public void setPersonId(int personId)
throws RemoteException;
}
代码段2. 一个更好的要领
这里我们对代码段1所展示的接口举办改造。只不外这些接口要领一次可以通报更多的数据。
public interface Person extends EJBObject
{
public PersonData getPersonData()
throws RemoteException;
public void setPersonData(
PersonData personData)
throws RemoteException;
}
/* 这是一个value工具. value工具可以取得 细线优化的要领*/
public class PersonData implements Serializable
{
PersonData(String firstName, String lastName,
int personId)
{
this. firstName = firstName;
this. lastName = lastName;
this. personId = personId;
}
public String getFirstName() {
return firstName ; }
public String getLastName() {
return lastName; }
public int getPersonId(){ return personId; }
private String firstName;
private String lastName;
private int personId;
}
代码段3. 成立一个Facade。
处事器端的facade为多个工具提供统一的接口。在这个例子中,ServerFacade为措施要用到的所有EJB的home handle提供缓存。
/* ServerFacade 为处事端的EJB 提供一个统一的接口。
所有客户端的类都通过ServerFacade会见处事器
*/
public class ServerFacadeBean implements SessionBean
{
file://缓存所有需要的EJB的home handle.
/* Person EJB 的home handle*/
PersonHome personHome = null;
/* Bank EJB的home handle*/
BankHome bankHome = null;
...
public ServerFacadeBean()
{
initialize();
}
public void initialize()
{
try
{ /* 初始化所有的Home handle.
We could also do lazy initialization
i.e., initialize as And when a home
handle is needed.
/* get initial context */
Context ctx = ...
if ( personHome == null )
personHome = (PersonHome)
ctx.lookup(“PersonHome”);
if ( bankHome == null )
bankHome = ( BankHome)
ctx.lookup(“BankHome”);
}
catch(Exception e)
{
/* 异常:查找失败*/
}
}
public PersonData getPersonData(int personId)
throws RemoteException
{
/*利用personHome缓存的副本*/
try
{
Person person =
personHome.findByPrimaryKey(personId);
return person.getPersonData();
}
catch(Exception e)
{
/*异常处理惩罚*/
}
}
public BankData getBankData(int bankId)
throws RemoteException
{
/* 利用bankHome handle 缓存的副本*/
try
{
Bank bank =
bankHome.findByPrimaryKey(bankId);
return bank.getBankData();
}
catch(Exception e)
{
/*异常处理惩罚*/
}
}
...
}
代码段4.一个挪用
为了制止一次长途挪用返回数据库的一行,可以用会话EJB工具将所有的数据打包成单个挪用。
#p#分页标题#e#
public class ServerFacadeBean implements SessionBean
{
...
>// 将行向量返回给客户端
public Vector findPersonsWithFirstName(
String firstName) throws RemoteException
{
Vector vector = new Vector();
Enumeration enumeration = null;
try
{
enumeration =
personHome.findPersonsWithFirstName (
firstName);
PersonData personData = null;
if ( enumeration != null )
{
while ( enumeration.hasMoreElements() )
{
Person person =
(Person)enumeration.nextElement();
personData = person.getPersonData();
vector.addElement(personData);
}
}
}
catch(Exception e)
{
>/* 异常处理惩罚 */
}
return vector;
}
...
}