http://www-128.ibm.com/developerworks/cn/java/j-jca1/
連接改善,資源適配器煥發(fā)生機(jī)
David Currie
Staff Software Engineer, IBM UK Ltd.
2005 年 4 月 21 日
本系列共三部分,在第一部分中,Java 開發(fā)人員 David Currie 介紹了 Java 2 Enterprise Edition (J2EE) Connector Architecture (JCA) 1.5 中所作的一些優(yōu)化,它們可以加快現(xiàn)有或新外部資源適配器的運(yùn)行速度。他還分析了一些使資源適配器煥發(fā)生機(jī)的新功能。若想提高現(xiàn)有資源適配器的性能或要加入新的功能,或者正在考慮編寫新的 JCA 1.5 資源適配器,那么本系列是必不可少的。若要編寫使用資源適配器的應(yīng)用程序,并想知道更多的幕后情況,那么本系列也值得一讀。
JCA 1.5 是 J2EE Connector Architecture 的最新版本,它包含了許多重要的增強(qiáng)和新增功能。討論這些變化的系列文章分為三個(gè)部分,本文是第一部分,將介紹該版本中所作的一些優(yōu)化,它們可以加快那些使用出站資源適配器的應(yīng)用程序,特別是那些使用事務(wù)的應(yīng)用程序的運(yùn)行速度。然后介紹對(duì)生命周期管理契約所作的擴(kuò)展,它們可以防止應(yīng)用程序的連接斷開,并使資源適配器煥發(fā)新生。這將為第 2、3 部分奠定基礎(chǔ)。第 2 部分討論新的工作管理和事務(wù)流入契約,第 3 部分探討期待已久的消息流入契約,其更常見的叫法是消息驅(qū)動(dòng)的 bean (MDB) 支持。
本文假定讀者對(duì) JCA 連接管理契約有一定的了解。原來 1.0 規(guī)范中的所有內(nèi)容仍然適用,因此如果剛剛接觸 JCA,建議先看一看 Willy Farrell 的入門教程(請(qǐng)參閱 參考資料)。
連接加快
JCA 規(guī)范的新版本不會(huì)使應(yīng)用程序與后端系統(tǒng)之間的連接變得更快,但 JCA 1.5 引入了兩組接口,可以加快使用連接的應(yīng)用程序的運(yùn)行。第一組接口解除了以前 JCA 版本中應(yīng)用服務(wù)器管理連接句柄方式的限制。
許多讀者都知道,J2EE 支持兩種連接使用模式,本文稱之為 get-use-close 和 cached-handle。對(duì)這些模式的進(jìn)一步分析有助于理解 JCA 1.5 在這方面所帶來的性能改善。
get-use-close
在 get-use-close 模式中,應(yīng)用程序在需要新連接時(shí),總是先獲取新連接,然后使用,然后再關(guān)閉它,如清單 1 所示。(為清楚起見,本文沒有在示例清單中加入異常處理邏輯。)
清單 1. 使用 get-use-close 模式的一個(gè) Enterprise JavaBean (EJB)
public class GetUseCloseEJB implements SessionBean {
...
public void businessMethod() {
InitialContext context = new InitialContext();
DataSource dataSource = (DataSource)context.lookup("java:comp/env/jdbc/mydatasource");
Connection connection = datasource.getConnection();
...
connection.close();
}
}
|
get-use-close 模式看起來效率不高,但是應(yīng)用服務(wù)器實(shí)現(xiàn)的連接池可降低“獲得”操作的成本。此外,因?yàn)閼?yīng)用程序只在需要時(shí)才保留連接,所以應(yīng)用程序的不同實(shí)例或者部分可以重復(fù)使用該連接,從而降低了總的資源占用,如圖 1 所示。
圖 1. 使用中的 get-use-close 模式

如圖 1 所示,每當(dāng) bean 方法調(diào)用 getConnection
時(shí),連接管理器都會(huì)重復(fù)使用池中托管的連接,并只創(chuàng)建一個(gè)新連接句柄。當(dāng)連接句柄通知連接管理器它已關(guān)閉時(shí),托管的連接就被清除并返回池中。圖 1 中的綠色線條表示托管連接與應(yīng)用程序的這個(gè)實(shí)例相關(guān)聯(lián)的時(shí)間。
cached-handle
在 cached-handle 模式中,應(yīng)用程序在一開始是獲得連接,然后在一個(gè)實(shí)例字段中緩存對(duì)它的引用,如清單 2 所示。
清單 2. cached-handle 模式
public class CachedHandleEJB implements SessionBean {
private Connection _connection;
...
public void ejbCreate() {
InitialContext context = new InitialContext();
DataSource dataSource = (DataSource)context.lookup("java:comp/env/jdbc/mydatasource");
_connection = datasource.getConnection();
}
public void businessMethod() {
...
}
}
|
應(yīng)用程序開發(fā)人員通常都使用 cached-handle 方法,因?yàn)樗麄冋J(rèn)為這樣應(yīng)用程序的性能會(huì)更好。但是因?yàn)?get-use-close 模式中連接池的作用,這兩種使用模式的性能差別一般來說是很小的。盡管 cached-handle 模式可使業(yè)務(wù)方法中的邏輯更簡(jiǎn)單,但是需要額外的邏輯以便在鈍化(passivation)時(shí)關(guān)閉連接,并在激活時(shí)重建連接。當(dāng)容器毀壞 bean 時(shí)(如由于方法生成了一個(gè)運(yùn)行時(shí)異常),還有可能留下打開的連接。
cached-handle 模式的最大問題是當(dāng) bean 或者 servlet 的一個(gè)實(shí)例使用該連接時(shí),其他實(shí)例就不能使用它——因此連接數(shù)最多只會(huì)有實(shí)例那么多。如圖 2 所示。
圖 2. 使用中的 cached-handle 模式

從圖 2 中可以看到,創(chuàng)建 EJB 時(shí)(從綠色線條開始),托管的連接與連接句柄關(guān)聯(lián),并且這個(gè) bean 實(shí)例以外的任何其他對(duì)象都不能使用這個(gè)連接。(綠色線條無限延伸。)
針對(duì)這種情況,JCA 1.5 規(guī)范引入了兩個(gè)新的接口,如清單 3 所示。
清單 3. 解除關(guān)聯(lián)(dissociation)和惰性關(guān)聯(lián)(lazy association)接口
public interface DissociatableManagedConnection {
void dissociateConnections() throws ResourceException;
}
public interface LazyAssociatableConnectionManager {
void associateConnection(Object connection,
ManagedConnectionFactory mcf,
ConnectionRequestInfo cxReqInfo)
throws ResourceException
}
|
解除關(guān)聯(lián)
資源適配器的托管連接實(shí)現(xiàn)第一個(gè)接口—— 解除關(guān)聯(lián) 接口——以向連接管理器表明適配器支持這種優(yōu)化。當(dāng)連接暫時(shí)超出范圍時(shí)(即當(dāng) bean 或者 servlet 方法退出時(shí)),如果連接管理器支持這種優(yōu)化的話,它就可以解除由應(yīng)用程序使用的連接句柄與表示物理資源的托管連接的關(guān)聯(lián)。這使托管連接可以返回連接池,為應(yīng)用程序的其他部分所使用。
惰性關(guān)聯(lián)
這種優(yōu)化使用 惰性關(guān)聯(lián),而不是在下次調(diào)用方法時(shí)重新將連接句柄與托管連接關(guān)聯(lián)。如果方法沒有使用連接,或者它只調(diào)用連接句柄上不需要訪問后端的簡(jiǎn)單方法,那么托管連接未必會(huì)從池中移出。相反,當(dāng)連接句柄確定它不需要與一個(gè)托管連接重新關(guān)聯(lián)時(shí),它就可以將連接管理器強(qiáng)制轉(zhuǎn)換為 LazyAssociatableConnectionManager
并調(diào)用 associateConnection
方法。該方法以連接句柄為第一個(gè)參數(shù),然后是托管連接廠,以及對(duì) allocateConnection
的第一次調(diào)用時(shí)傳遞的請(qǐng)求信息。然后連接管理器從池中找到另一個(gè)合適的托管連接,并使用這個(gè)托管連接的 associateConnection
方法將它與連接句柄關(guān)聯(lián)。
圖 3 顯示了這種優(yōu)化對(duì) 圖 2 中的 cached-handle 用法的效果。
圖 3. 惰性關(guān)聯(lián)降低資源用量

圖 3 中虛線箭頭表示方法完成時(shí) EJB 容器對(duì)連接管理器的通知。這時(shí),托管的連接與連接句柄解除關(guān)聯(lián),只有當(dāng)方法試圖使用這個(gè)句柄時(shí),托管連接才會(huì)重新關(guān)聯(lián)。短的綠色線條顯示托管連接現(xiàn)在綁定到 EJB 上,以縮短時(shí)間并可以在別的地方重新使用。
征募,還是不征募
大家都知道事務(wù)代價(jià)很高——特別是 XA(全局)事務(wù)。這使得讓事務(wù)不執(zhí)行非必需的工作變得很重要。JCA 1.5 中的另外兩個(gè)新接口防止了 XAResource
對(duì)象的不必要征募。
我們現(xiàn)在來更詳細(xì)地分析一下這種增強(qiáng)所針對(duì)的問題。假定取用 清單 1 中的 EJB,并用容器托管的事務(wù)部署它,將業(yè)務(wù)方法的事務(wù)屬性設(shè)定為 RequiresNew
。調(diào)用這個(gè)方法時(shí)便開始一個(gè)新的事務(wù)。創(chuàng)建連接時(shí),連接管理器不知道如何使用它,因此它必須從關(guān)聯(lián)的托管連接獲得一個(gè) XAResource
,并在事務(wù)中征募它。連接可能只用于查詢數(shù)據(jù)庫(kù)或者根本不被使用。但是連接管理器必須征募連接,以備插入或者更新操作。這意味著,資源適配器至少必須進(jìn)行開始、提交或者回滾,并結(jié)束流程返回后端。圖 4 展示了這樣的事務(wù)流程。
圖 4. 急切事務(wù)征募

在圖 4 中可以看到,獲得連接后,XAResource
立即征募到了事務(wù)中(即接收一個(gè)開始流程)。這意味著當(dāng)事務(wù)方法結(jié)束時(shí),流程需要在資源處結(jié)束,即使方法沒有在事務(wù)中使用連接。
如果在事務(wù)中涉及另一項(xiàng)資源,就會(huì)強(qiáng)制進(jìn)行不必要的兩階段提交,導(dǎo)致額外的準(zhǔn)備流程。這里的惟一可取之處是資源管理器仍然可以從準(zhǔn)備調(diào)用中返回 XA_RDONLY
(用于“read only”)以表明它實(shí)際沒有做任何工作。事務(wù)管理器不需要在那個(gè)資源管理器處完成調(diào)用,并且如果在事務(wù)中只有一個(gè)資源管理器真正做了工作,那么事務(wù)管理器也許可以避免日志文件中的惰性寫操作。
惰性征募
現(xiàn)在您應(yīng)當(dāng)認(rèn)識(shí)到除非絕對(duì)需要,否則不要在事務(wù)中征募。JCA 1.5 規(guī)范提供了一個(gè)解決方案:惰性征募。清單 4 顯示了支持這各種優(yōu)化所引入的兩個(gè)接口。
WebSphere 為 JCA 1.5 打下基礎(chǔ) IBM WebSphere 應(yīng)用服務(wù)器 V5 用 JCA 1.0 的一個(gè)擴(kuò)展克服了 cached-handle 連接模式的缺點(diǎn),它稱為 智能連接句柄(smart connection handle)。JCA 1.5 規(guī)范通過對(duì)清單 3 中的兩個(gè)接口的修改吸取了這種擴(kuò)展。
JCA 1.5 在其惰性征募支持中還采用了 WebSphere Application Server V5 的 延遲事務(wù)征募 擴(kuò)展。
請(qǐng)閱讀 developerWorks 中 Kevin Kelle 等人撰寫的文章以了解更多的 WebSphere 擴(kuò)展(請(qǐng)參閱 參考資料)。 |
清單 4. 惰性征募的接口
public interface LazyEnlistableManagedConnection {
}
public interface LazyEnlistableConnectionManager {
void lazyEnlist(ManagedConnection mc)
throws ResourceException;
}
|
LazyEnlistableManagedConnection
接口是由托管連接實(shí)現(xiàn)的標(biāo)記接口,用以向連接管理器表明在事務(wù)中創(chuàng)建新的連接或者在連接已經(jīng)存在的情況下開始一個(gè)新的事務(wù)時(shí),它不需要將托管連接急切征募到現(xiàn)有事務(wù)中。如果連接句柄準(zhǔn)備執(zhí)行應(yīng)當(dāng)是任一事務(wù)中的一部分的某些工作,并且它的托管連接還沒有征募,那么應(yīng)當(dāng)確定連接管理器是否實(shí)施了 LazyEnlistableConnectionManager
接口。如果實(shí)施了,那么它應(yīng)當(dāng)調(diào)用傳遞托管連接的 lazyEnlist
方法。這個(gè)方法不返回任何結(jié)果,但是如果調(diào)用線程關(guān)聯(lián)了一個(gè)事務(wù),那么這時(shí)就征募托管連接的 XAResource
。如果連接沒有被征募,那么它需要在后面每一項(xiàng)工作之前再次調(diào)用 lazyEnlist
,以檢查在上次調(diào)用這個(gè)方法之后,是不是沒有啟動(dòng)過事務(wù)。
圖 5 顯示了這個(gè)新的事件序列。
圖 5. 惰性事務(wù)征募

在圖 5 中可以看到,在獲得連接時(shí),XAResource
不再急切征募。相反,連接管理器會(huì)等待,直到連接用 lazyEnlist
調(diào)用表明它要完成一些事務(wù)工作時(shí),才會(huì)征募 XAResource
。
什么時(shí)候出現(xiàn)錯(cuò)誤
JCA 規(guī)范的第一個(gè)版本提供了一種讓資源適配器在連接出現(xiàn)嚴(yán)重錯(cuò)誤時(shí)通知連接管理器的機(jī)制。這是 ConnectionEventListener
接口的 connectionErrorOccurred
方法。收到這個(gè)通知后,連接管理器就會(huì)毀環(huán)發(fā)送事件的托管連接,這樣就不會(huì)再使用它。這些都是不錯(cuò)的。但是,如果到后端的連接丟失了,那么池中的許多托管連接也很有可能不能再使用。
針對(duì)這個(gè)問題,JCA 1.5 以 ValidatingManagedConnectionFactory
接口的方式引入了一種雅潔的解決方案,如清單 5 所示。
清單 5. 用于確定無效連接的接口
public interface ValidatingManagedConnectionFactory {
Set getInvalidConnections(Set connectionSet)
throws ResourceException;
}
|
由托管連接廠實(shí)現(xiàn)的 ValidatingManagedConnectionFactory
接口包含一個(gè)方法——getInvalidConnections
——它以一組托管連接為參數(shù),返回資源適配器認(rèn)為無效的一個(gè)子集。資源適配器的驗(yàn)證可以采取任何形式,不過通常涉及到對(duì)后端的某種“ping”操作,以測(cè)試連接。然后連接管理器在資源適配器指示連接錯(cuò)誤時(shí)調(diào)用這個(gè)方法,甚至定期調(diào)用該方法,以便從池中排除壞掉的連接。
開始和結(jié)束
最初的 JCA 版本為托管連接及其相關(guān)聯(lián)的連接句柄提供了詳細(xì)的生命周期模式,但是它沒有為資源適配器提供這種概念。只有當(dāng)創(chuàng)建了托管連接廠后,部署的資源適配器才知道它的存在。JCA 1.5 中引入的 ResourceAdapter
接口對(duì)此進(jìn)行了糾正,如清單 6 所示。
清單 6. 新資源適配器接口的生命周期方法
public interface ResourceAdapter {
void start(BootstrapContext ctx)
throws ResourceAdapterInternalException;
void stop();
...
}
|
資源適配器可以在其部署描述符(ra.xml)的 resourceadapter-class
元素中給出實(shí)現(xiàn)這個(gè)接口的類的名稱。除了實(shí)現(xiàn) ResourceAdapter
接口,這個(gè)類可以通過 JavaBean 樣式支持某些屬性。與托管連接廠一樣,在部署描述符中可以聲明這些屬性及其默認(rèn)值,如清單 7 所示。在安裝了資源適配器后,應(yīng)用服務(wù)器將允許管理員覆蓋這些默認(rèn)值。
清單 7. 資源適配器部署描述符
<connector>
...
<resourceadapter>
<resourceadapter-class>
example.ExampleResourceAdapterImpl
</resourceadapter-class>
<config-property>
<config-property-name>ServerName</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>MyServer</config-property-value>
</config-property>
<config-property>
<config-property-name>PortNumber</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>1976</config-property-value>
</config-property>
</resourceadapter>
...
</connector>
|
在啟動(dòng)時(shí),應(yīng)用服務(wù)器會(huì)創(chuàng)建在部署描述符中指定的類的一個(gè)實(shí)例,并設(shè)置管理員提供的屬性。這個(gè)類必須根據(jù)這些屬性實(shí)現(xiàn)一個(gè) equals
方法,這樣應(yīng)用服務(wù)器就可以保證它不會(huì)創(chuàng)建一個(gè)以上同樣的實(shí)例。然后會(huì)調(diào)用 start
方法,向它傳遞一個(gè)實(shí)現(xiàn)了 BootstrapContext
接口的對(duì)象。可以用這個(gè)對(duì)象創(chuàng)建計(jì)時(shí)器、調(diào)度其他線程上的工作和控制導(dǎo)入的事務(wù),在本系列的第 2 部分中將更詳細(xì)地討論所有這些內(nèi)容。這個(gè)方法將不會(huì)堵塞并會(huì)及時(shí)返回。
應(yīng)用服務(wù)器通常會(huì)在關(guān)閉或者解除部署資源適配器之前,對(duì)資源適配器調(diào)用 stop
方法。JCA 1.5 規(guī)范描述了這個(gè)過程的兩個(gè)階段。首先,應(yīng)用服務(wù)器保證依賴資源適配器的所有應(yīng)用程序都已停止。這可保證程序線程不再使用資源適配器對(duì)象,并且所有事務(wù)都已完成。然后應(yīng)用服務(wù)器調(diào)用 stop
方法。這時(shí),資源適配器將執(zhí)行一個(gè)有序的關(guān)閉(例如,釋放網(wǎng)絡(luò)和應(yīng)用服務(wù)器資源,并將所有緩存的數(shù)據(jù)強(qiáng)行送回后端)。調(diào)用 stop
后,應(yīng)用服務(wù)器將不會(huì)重新使用資源適配器實(shí)例。
為了保留向后兼容性,ManagedConnectionFactory
沒有改變,但是如果希望外部資源可以利用資源適配器具有的功能,那么還要實(shí)現(xiàn)清單 8 所示的新的 ResourceAdapterAssociation
接口。
清單 8. ResourceAdapterAssociation
接口
public interface ResourceAdapterAssociation {
ResourceAdapter getResourceAdapter();
void setResourceAdapter(ResourceAdapter ra)
throws ResourceException;
}
|
構(gòu)建了托管連接廠之后,應(yīng)用服務(wù)器將調(diào)用 setResourceAdapter
方法以便將它與其資源適配器關(guān)聯(lián)在一起。在托管連接廠的生命周期內(nèi)這種關(guān)聯(lián)將會(huì)固定下來。這種方法只調(diào)用一次。
結(jié)束語(yǔ)
本文展示了 JCA 1.5 為現(xiàn)有出站契約帶來的四項(xiàng)增強(qiáng)功能。惰性關(guān)聯(lián)和征募優(yōu)化會(huì)提高使用連接的應(yīng)用程序的性能,驗(yàn)證托管連接廠會(huì)改善對(duì)故障情況的處理。在資源適配器級(jí)別引入生命周期管理為資源適配器提供了多種有趣的新機(jī)會(huì)。本系列的第 2 部分將分析如何在這個(gè)基礎(chǔ)上建立新的工作管理和事務(wù)流入契約。
參考資料
關(guān)于作者
David Currie 最近加入了位于英國(guó) Hursley 的 IBM Software Services for WebSphere 組織。在這之前,他從事 WebSphere Application Server 的開發(fā),最初是在事務(wù)處理領(lǐng)域,最近負(fù)責(zé)設(shè)計(jì)和實(shí)施 JCA 資源適配器以提供消息傳遞支持。可以通過 david_currie@uk.ibm.com 與他聯(lián)系。 |