程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 提升EJB性能的12招

提升EJB性能的12招

編輯:關於JAVA

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)

在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的方法可以讀取操作數據的中間狀態,但是時他們無法得知是否增加了新的記錄。

隔離級別越高性能上開銷也越大。通常情況下,較低的隔離級別允許更多的並發操作 ,代價是需要更加復雜的邏輯去處理潛在的數據不一致性的問題。一條有用的准則是在該企業信息系統可接受的性能范圍內采用盡可能高的隔離級別。

設計和開發後期

在一個企業級應用的開發後期階段我們必須關注與性能相關的部分對整個系統進行調整。余下的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.調整系統參數

應用程序中的許多參數是是可調的。例如,在操作系統中,我們可以調整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對象將所有的數據打包成單個調用。

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;
}
...
}

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved