周 志远 (zzyuan@cn.ibm.com), 软件工程师, IBM 中国开发试验室
2008 年 7 月 03 日 事务 (transaction) 支持对于构建可靠的分布式应用至关重要。面向服务的组件体系结构 (SCA) 为我们提供了一个与实现无关的开发架构,SCA 组件是构建 SOA 应用的基本单位,本文将介绍 SCA 对事务的支持,以及如何使用 WebSphere Integration Developer (WID) 开发与配置支持全局事务的 SCA 应用。
引言
本文将讲述如何用 WID 开发运行于 WebSphere Process Server(WPS) 上的支持全局事务的 SCA 应用。首先将介绍事务相关概念,接着讲述 SCA 对事务的支持,最后我们通过一个示例演示如何在组件层次制定事务属性,开发支持 WS-AT 全局事务的 SCA 应用,。
概念介绍
-
事务 :保证一组对于资源的更新活动以原子的方式执行。也就是说,要么事务中所有的资源更新都被执行,其结果被永久保存;要么所有活动都不执行。
-
Resource Manager Local Transaction (RMLT):RMLT 是从资源管理器角度来看,通过单一连接使用资源的局部事务。支持 RMLT 的资源管理器,包括通过资源适配器 (Resource Adapter) 进行访问的 EIS,通过 JDBC DataSource 访问的关系数据库,JMS 队列,等等。
-
Global Transaction(
全局事务
):当一个应用使用到多个资源时,需要一个外部的事务管理器通过全局事务来协调对各个资源的更新。在 J2EE 中,使用 BMT(Bean Managed Transaction) 的 EJB 组件,应用程序客户端组件,web 组件等可以通过 Java Transaction API(JTA) 的 userTransaction 接口来创建 / 参与到全局事务中;采用 CMT(Bean Managed Transaction) 的 EJB 组件则由容器负责划分事务。
-
WS-AT: Web service atomic transaction (WS-AT) 是 IBM,Microsoft, BEA 等提出的在 web service 应用之间使用分布式全局事务的协议,全局事务的参与者通过两阶段提交协议来协调事务状态,确保所有事务参与者能够达到一致的状态 , 即所有参与者都回滚或者提交。WS-AT 本身并没有定义新的事务接口,全局事务的划分仍然通过标准的 JTA 接口来定义。当运行于全局事务的 J2EE 应用程序发起 web service 请求时,事务管理器将在 SOAP header 中自动插入对应的 WS-AT CoordinationContext。web service 目标组件接受到请求时,其事务管理器将根据发送过来的 WS-AT CoordinationContext 创建从属于此全局事务的 JTA 事务,从而 web service 的各个参与者都运行于同一个全局事务上下文中。
-
WPS
对事务的支持 :WPS 从 version 6.0 开始实现了对 WS-AT 的支持。WPS 可以作为事务管理器来协调全局事务,也可以作为全局事务参与者参与到全局事务中,同时也为 RMLT 提供运行环境。

 |

|
SCA 中的事务的支持
Service Component Architect (SCA) 中,组件拥有标准的接口 (WSDL,java interface 等 ),组件的实现形式可以是 BPEL,POJO,business rule 等等。这些 SCA 组件可以导出为外部应用程序所使用,也可以导入外部组件为自身使用。SCA 组件对于事务的支持通过 QoS 来指定。开发者在 SCA 组装的时候可以为其指定事务相关的 Qualifier,SCA runtime 将根据事务 QoS 属性在运行时提供相应的事务支持。WID 为 SCA 组件提供了强大的开发工具。对于 SCA 组件的事务属性定制,WID 同样提供了方便的编辑工具。
具体来说,在开发和组装 SCA 的时候可以在 Interface(接口)和 implementation(实现)两个方面为组件指定事务支持属性:
-
Component Implementation Qualifier for transaction:
组件实现的 Qualifier 属性指定了组件在运行时对于安全性,事务等 QoS 的要求。其中 "Transaction' 属性表明了组件执行时对活动的逻辑划分,其取值与意义如下:
Global:若客户请求传播了全局事务上下文,则组件运行于此全局事务中;若不存在已有的全局事务,则会新建一个全局事务。
Local:组件运行于一个局部事务中,该事务不能跨域容器边界。
Any:若存在全局事务上下文,则组件将运行于此全局事务中;否则组件将运行于局部事务中。
-
Component interface Qualifier for transaction:
组件接口的 Qualifier 是组件对外部的 QoS 声明,是组件与其客户端交互的协约。其中的 "Join Transaction" 属性声明了组件与客户端参与事务的特性,其取值与意义如下:
True:组件运行时容器将参与到任何客户端传播过来的事物中。
False:组件运行时容器不参与到客户端传播的事务中。
接口和实现的事务属性一起决定了组件运行时的事务特征:
|
Interface - Join Transaction qualifier
|
Implementation - Transaction Value qualifier
|
组件事务行为
|
|---|
|
True
|
Global
|
组件将运行于传播来的全局事务中,否则将新建一个全局事务
| |
True
|
Local
|
不兼容,在 WID 中将导致检查错误
| |
True
|
Any
|
组件将运行于传播来的全局事务中,否则将运行于本地事务中
| |
False
|
Global
|
组件将运行于新建的全局事务中
| |
False
|
Local
|
组件将运行于本地事务中
| |
False
|
Any
|
组件将运行于本地事务中
|
在 SCA 应用中应用 WS-AT
本小节通过一个示例介绍如何为 SCA 组件定制全局事务支持。
场景说明
示例模拟这样一个简单的银行转帐流程,包括取款 (withdraw balance) 和存款 (add balance) 两个阶段,这两个阶段的功能分别由两个 SCA 组件实现,如【图一】所示。AccountTransfer 组件由 BPEL 流程实现,组装取款与存款组件以完成转帐功能,如【图二】所示。我们将通过配置 SCA 事务支持,以保证存款与取款两组件的操作结果一致,即两者都提交或者两者都回滚。
图一 Account Transfer 服务组装图
图二 Account Transfer BPEL 流程图
应用实现
-
存款组件由 java 实现,当交易额大于 1000 时抛出 Exception。其主要代码为:
public String add(String account, Integer amount) throws Exception {
//TODO Needs to be implemented.
if(amount.intValue()>1000){
throw new Exception("tranfer amount"+amount.intValue()+"
is too large at a time");
}
return "OK";
}
|
-
取款组件由 java 实现,并导出为 web service,为 AccountTransfer 流程所调用。取款组件从数据库中对应的帐号减去取款数额,其主要代码为:
public String withdraw(String account, Integer amount) throws
NamingException, SQLException {
//TODO Needs to be implemented.
Connection con = this.getConnection_DS();
Statement st = con.createStatement();
String sql="update account set amount=amount-"+amount.intValue()+"
where accid='"+account+"'";
st.executeUpdate(sql);
st.close();
con.close();
return "SUCC";
}
private Connection getConnection() throws NamingException, SQLException {
Connection con = null;
DataSource ds = null;
ds = (DataSource) new InitialContext().lookup("jdbc/account");
con = ds.getConnection();
return con;
}
|
SCA 事务配置
本部分介绍如何对 SCA 进行全局事务配置。我们的应用包括三个 SCA 组件:转帐组件,由 BPEL 实现;取款组件和存款组件均由 Java 实现。我们将根据前面介绍的概念为组件配置事务 QoS 属性。显然,要让三个组件参与到全局事务中,组件实现的 Transaction 属性应该设为”Global”, 组件接口的”Join Transaction”属性应该设为”True”。
-
在 WID 中导入项目交换文件 accounttransfer.zip , 切换至 Business Integration 视图,如【图三】所示。
图三 AccountTransfer 项目视图
-
在 Assembly Diagram 中打开 AccountTransferModule,选择”addBalanceComp”组件,在”Properties”页中依次选择”Details”=>”Interfaces”=>”addBalance”=>”Qualifiers”, 为其添加”Join transaction” QoS 属性,其值设为”true”,如【图四】所示。
图四配置存款组件接口事务属性
选择”Implementation”=>”Qualifiers”, 为其添加”Transaction” QoS 属性 , 其值设为”Global”,如【图五】所示。
图五配置存款组件实现事务属性
3. 按照与步骤 2)同样的方式,为”withdrawBalanceWSImport”导入添加”Join Transaction”QoS 属性,其值设为”true”; 在 Assembly Diagram 中打开 DelBalanceModule, 为”withdrawComp”组件在 Interface 上添加”Join transaction” QoS 属性,其值设为”true”,在 implementation 上添加”Transaction” QoS 属性 , 其值设为”Global”。
4. 对于实现为 BPEL 的组件,我们可以看到全局事务属性默认已经被选上
5. 保存设置,重新 build 项目。
WS-AT 事务配置
本部分介绍如何在 WID 中为应用配置 WS-AT 全局事务支持,关于 WS-AT 更详细的介绍,请参考【文献 1】。
在 WID 中切换至”J2EE”视图,在”Project Explorer”中选择“Dynamic Web Projects” => ”AccountTransferModuleWeb”, 双击”Deployment Descriptor”, 在”Servlets” Tab 中选中” AccountTransferServiceExport1_AccountTransferServiceHttpPort”,在”Global Transaction”栏中选上”Send Web Service Atomic Transaction on requests”, 如【图六】所示。这表明 AccountTransfer 应用将在调用 Web Service 的请求中发送 WS-At Context。
图六为 AccountTransfer 应用配置 WS-AT
创建数据源
1. 在 DB2 中创建数据库 account。在 account 数据库中按照如下 SQL 创建 account 表:
CREATE TABLE "ADMINISTRATOR"."ACCOUNT" (
"ACCID" VARCHAR(10) NOT NULL ,
"AMOUNT" INTEGER NOT NULL );
ALTER TABLE "ADMINISTRATOR"."ACCOUNT"
ADD CONSTRAINT "PK_ACCID" PRIMARY KEY
("ACCID");
|
2. 向 account 表中添加测试用数据:
Insert into account values (‘acc001’,10000);
Insert into account values (‘acc002’,10000);
|
3. 在 WPS 管理控制台上为 account 数据库创建 jdbc 数据源,其 JNDI 为”jdbc/account”, 在这里我们使用”DB2 Universal JDBC Driver Provider(XA)”类型数据源。
部署与测试
1. 将 AccountTransferModule 和 DelAccountModule 导出为 EAR,部署到 WPS 上。
2. 在 WID 中用 Web Service Explorer 测试 AccountTransfer 服务如下:
测试一:从账号 acc001 转帐 500,服务返回成功。输入与结果如【图七】所示:
图七测试成功 case
测试二:从帐号 acc002 转帐 2000,服务返回异常,在数据库中打开 account 表,可以发现账户余额没有发生变化。这是因为交易额大于 1000,存款组件抛出异常,处于 WS-AT 全局事务的取款组件所作操作被回滚。服务返回消息为:
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Server.generalException</faultcode>
<faultstring>
<![CDATA[
javax.transaction.TransactionRolledbackException: ; nested exception is:
com.ibm.websphere.csi.CSITransactionRolledbackException: Transaction marked
rollbackonly
]]>
</faultstring>
</soapenv:Fault>
</soapenv:Body>
|
注意事项
-
参与 WS-AT 全局事务的数据源必须支持两阶段提交协议,例如支持 XAResource 接口的数据源。
-
参与全局事务的组件,其事务的提交由全局事务管理器来决定,不能在组件中使用 Connection.commit 方法,这样将导致运行时错误。
-
WS-AT 适用于 short duration 的流程。由于两阶段提交协议通过对资源加事务锁来实现,不适合 long running 的流程,而通常采用 compensation 的方式
-
对于应用了全局安全的 WPS,您需要进行额外的配置以支持 WS-AT, 请参考【文献 2】中对于 WS-AT 部分的说明
总结
本文介绍了 SCA 中的事务相关概念,通过本文的例子可以看到,SCA 提供了一个与实现无关的开发架构,不论其实现为 BPEL 还是 POJO,我们都可以方便的通过其事务 QoS 属性,声明式地配置组件的事务行为。
参考资料 学习
获得产品和技术
讨论
关于作者  | |  | 周志远,工作在 IBM 中国软件开发实验室 - 中国新技术中心(China Emerging Technology Institute),从事 Incubator 及 SOA 相关项目的工作。他的邮件是zzyuan@cn.ibm.com。 |
对本文的评价
|