ws-atomic transaction 2阶段提交 转

转帖http://www.ibm.com/developerworks/cn/webservices/ws-wsat/index.html

事务是构建分布式应用程序的关键,J2EE事务广泛使用了JTA事务。随着Web服务技术逐渐走向成熟并深入发展,事务技术在Web服务中的重要性也日益彰显。WS-AT (WS- Atomic Transaction) 定义了Web服务原子事务的协调类型,主要使用两阶段提交做为协调协议。目前IBM WAS 6.0(WebSphere Application Server 6.0)已经支持WS-AT事务(Web Services Atomic Transaction for WebSphere Application Server , WS-AT for WAS)。在这篇文章中我们将讲述WS-AT规范,WAS6.0对WS-AT事务的支持,并用RAD6.0(Rational Application Developer 6.0) 开发一个具体的WS-AT事务例子来加以说明。

<!--START RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--END RESERVED FOR FUTURE USE INCLUDE FILES-->

1. 引言

事务技术很早就已经出现,从最初的数据库事务、分布式事务以及到现在的Web服务事务,事务技术已经被广泛使用,在分布式应用程序中,事务已经成为构造可靠的应用程序的关键。

事务具有原子性、一致性、隔离性和持久性四个特性,也就是常说的ACID特性。参与事务的所有操作或者全部成功,如果有一个操作失败那么其它的操作结果需要取消,也就是回滚事务,从而保证事务的特性。

JTA/JTS是J2EE事务的解决方案,它使用两阶段提交协议来保证事务的特性。JTA是提供给客户端的接口,客户使用JTA可以很方便地发起一个J2EE事务,划分事务边界,提交或者取消事务;JTS是由J2EE平台提供的标准的事务处理服务。为了参与J2EE事务的两阶段提交过程,资源的实现者,例如数据库驱动程序,JMS消息队列…需要实现两阶段提交的XA Resource接口,从而保证分布式资源可以和事务管理器交互完成事务。事务的绝大部分工作由JTA/JTS、 XAResource供应商实现,对于应用程序开发者来说仅仅需要通过配置或者少量的编程就可以完成事务的开发,从而减轻了开发工作。

虽然JTA/JTS事务具有很好的优点,并且大部分J2EE应用服务器已经提供了相应的支持,但JTA/JTS事务还有一些限制,例如实际环境中,常常需要其它的事务模型交互或者非J2EE环境中的参与者卷入事务当中,或者当前的通信协议不支持JTA/JTS事务的传播。在这些方面,Web服务具有明显的优势,JTA/JTS事务的这些限制正好可以由Web服务解决。

Web服务是基于标准协议的自包含的模块化组件,是实现SOA架构的基础。使用WSDL可以描述Web服务,使用SOAP消息可以在网络上调用Web服务,使用UDDI可以发布或者查找Web服务。由于Web服务使用了业界广泛接受的协议,并且独立于平台和编程环境,通过将企业遗留程序包装成Web服务,SOAP消息可以基于HTTP进行传输从而跨越防火墙,从而特别适合于企业应用程序的集成。

通过将J2EE事务和Web服务结合起来,使得Web服务在J2EE事务基础上跨越异构环境参与到全局事务中,这样在底层上借助于成熟的J2EE事务处理技术,高层上借助于Web服务跨越不同的通信协议或者异构环境传播事务,从而成为Web服务事务解决方案的一种很好的实现方式。

目前针对Web服务中的事务处理,已经制定了一些规范,例如OASIS组织的BTP, IBM和BEA、Microsoft等联合提交的WS-Coordination 、WS-AtomicTransaction 和 WS-BusinessActivity规范等。下面来介绍一下WS-Coordination 、WS-AtomicTransaction和 WS-BusinessActivity规范。

 




回页首

 

2. WS-AT规范介绍

IBM、Microsoft和BEA等联合提交的Web服务事务规范包括以下几部分:

1) WS-Coordination: Coordination定义了一种可扩展的协调框架,该框架提供了三种服务:激活服务,注册服务和协调服务,从而支持不同的协调协议进行交互达到一致的结果。WS-Coordinatiom定义了用来传播上下文的Coordination Context的消息格式。在WS-Coordination之上定义了Web Service事务的两种协调类型,原子事务和商业活动。

2) WS-AtomicTransaction: 定义了Web服务原子事务 (Web Service Atom Transaction简称WS-AT或者AT)的协调类型。WS-AT类似于传统的分布式或者数据库事务,一般使用两阶段提交协议做为协调协议,参与事务的Web服务的执行结果或者全部成功或者由于一个失败其它执行能够全部取消。这样的事务一般持续时间比较短,经常发生在一个企业内部。WS-AT事务可以保证短时间的分布式活动获得一致的结果,从而具有all-or-nothing的特性。

3) WS-BusinessActivity: 定义了Web服务商业活动 (Web Service Businsess Activity简称WS-BA或者BA)。这样的事务持续时间比较长,而且通常跨越不同的领域和组织,参与事务的各部分一般不允许采用锁定资源而采用补偿处理的方式,常常应用在长事务或者商业流程中。

目前WAS v6实现了WS-Coordination和WS-AtomicTransaction规范(以下简称WS-AT for WAS),WAS 6.0允许用户在J2EE事务之上方便快速地开发WS-AT事务,从而使分布式的 Web 服务应用程序及它们使用的资源引入全局事务中。

 




回页首

 

3. WS-AT for WAS

WS-AT for WAS为部署在WAS中的Web服务提供了事务特性。在WAS 6.0中,WS-AT事务是建立在J2EE事务之上的,事务的底层实现,例如XAResource资源的操作是由JTA事务实现的。WS-AT但事务上下文的传播以及协调器和事务参与者的交互是建立在Web服务之上的。

WAS 完成了绝大部分的WS-AT实现,对于开发者来说,为了支持WS-AT,不需要在应用程序中实现新的接口或者做其它的编码工作,所做的工作仅仅是在配置Web服务的时候指定一些选项就可以了。因此使用RAD开发一个WS-AT事务是非常简单的。

WS-AT for WAS主要的过程和原理如图1所示:


1) 在WS-AT事务的声明和普通的JTA事务是一样的,在Web服务客户端中使用UserTransaction接口发起一个事务,并创建一个事务上下文Context,在调用其它的Web服务应用后,WAS可以将该Context转化为符合WS-Coordination标准的Coordination Context并放在Business request消息头部从而将事务传播到其它的Web服务。

2) 目标WAS在接受到该Coordination Context后,隐式地在运行环境内确定一个从属JTA事务。首先对Context进行解析并得到注册服务的地址,并向宿主WAS发送注册该Web服务的请求,这时Web服务应用变为事务参与者,其中的的XA资源已经处于全局事务控制之下。

3) 当Web服务Client调用UserTransaction.commit接口时候, 宿主WAS能够和注册到该Server的参与者Web服务进行两阶段提交的工作,从而完成事务。

说明:

1) Web服务Client内部也可以执行J2EE操作,从而将自身也做为事务的参与者,但对自身XA资源的处理是本地的J2EE操作而不是Web服务调用。

2) 从本质来讲,WS-AT事务只不过是J2EE事务的扩展,Web服务操作的调用,参与者的注册以及两阶段提交操作由以前的J2EE调用变成了SOAP消息的传递。

3) 对于容器管理的EJB事务来说,开发者不需要显示地使用UserTransaction发起事务, 容器会隐式地执行事务性操作。

 




回页首

 

4. 示例

本文以一个具体的例子来逐步说明如何用WAS6.0和RAD6.0开发、部署和测试WS-AT事务,这里我们使用WAS 6.0部署和发布WS-AT应用,使用RAD6.0(或者内嵌RAD6.0的Rational Software Architect 6.0和WebSphere Integration Developer 6.0)开发和测试WS-AT应用。该例子是常见的银行转账业务,其中用到的应用关系如图2所示。其中AccountManager是一个无状态会话Bean,内部使用CMP读写数据库,提供存款 (Deposit)和取款(WithDraw)操作。WebBankManager是一个Web项目,使用普通的Java类调用AccountManager实现转账功能,两者都发布为Web服务。而WebBankManagerClient是一个普通的Web项目,用户可以使用里面的JSP页面填写表单发送转账请求。


图2 示例中各应用之间的关系
图2 示例中各应用之间的关系

下面就让我们一步一步的来完成这个例子。

4.1 创建数据库插入测试数据

该例子使用WAS自带的CloudScape数据库,首先创建WSAT数据库以及Account表,并插入测试数据。用户可以直接运行示例包中的CloudCreate.bat来完成以上操作。

创建完成后运行CloudList.bat可以看到以下数据,如图3所示:


图3 ACCOUNT表的数据
图3 ACCOUNT表的数据

启动WAS Server后,运行管理控制台,创建CloudScape JDBC Provider, 将WSAT数据库注册为Datasource,JNDI名字是jdbc/wsat,如图4所示,后面的AccountManager EJB将会用到这个数据源。


图4 在WAS 管理控制台中创建数据源
图4 在WAS 管理控制台中创建数据源

4.2 开发AccountManager

数据库建好后,我们采用自底向上的方式开发AccountManager EJB。首先在RAD中新建一个数据库连接,导入新建的WSAT数据库,在Account表中右键单击选择根据表创建EJB,如图5所示。在后面的对话框中指定导入的AccountManager项目以及Account表,就可以创建一个Account 实体Bean 了,RAD会自动创建CMP和表的映射关系。


图5 根据表创建Account实体EJB
图5 根据表创建Account实体EJB

在J2EE透视图中,在新建的项目中新建一个会话Bean,名字是AccountManager,在AccountManager EJB中增加对Account的EJB引用,同时设定Account CMP的connection为jdbc/wsat。

然后在会话Bean接口中增加如下两个方法,用来实现转账功能中的取款和存款功能,如清单 1 所示:


清单 1: AccountManager接口

public void withDraw(String accountNo, int amount)   throws Exception,
java.rmi.RemoteException;	
public void deposite(String accountNo, int amount)    throws Exception,
java.rmi.RemoteException;

在会话Bean实现类中实现这两个接口,如清单 2 ,清单 3 所示:


清单 2: AccountManagerBean的withDraw方法实现

public void withDraw(String accountNo, int amount) throws Exception {
	AccountLocalHome home = this.findAccountHome();
	System.out.println(accountNo + " withDraw begin to find account local");
	AccountLocal accountLocal = home.findByPrimaryKey(new
	AccountKey(accountNo));		
	int balance = accountLocal.getBalance().intValue();
	System.out.println(accountNo + " original balance = " + balance);
	if (amount > balance)
	{
		throw new Exception("you cannot withdraw " + amount + " money, 
		exceed your banlance");
	}
	accountLocal.setBalance(new Integer(balance - amount));		
	System.out.println(accountNo + " withDraw finish, balance = " +
	accountLocal.getBalance());
}



清单 3:AccountManagerBean的deposit方法实现

public void deposite(String accountNo, int amount) throws Exception {
	AccountLocalHome home = this.findAccountHome();
	System.out.println(accountNo + " deposite begin to find account local
	accountNo = " + accountNo);
	AccountLocal accountLocal = home.findByPrimaryKey(new
	AccountKey(accountNo));
	int balance = accountLocal.getBalance().intValue();	
	System.out.println(accountNo + " original balance = " + balance);
	accountLocal.setBalance(new Integer(balance + amount));	
	System.out.println(accountNo + " deposite finish, balance = " +
	accountLocal.getBalance());
}

以上代码比较简单,在此我们就不具体解释了。完成以上操作后,将AccountManager EJB发布为Web Service,生成WSDL文件。

具体步骤是:新建一个Web Service项目,选择Web Service type为EJB Web Service,选择正确的EJB project和EAR project(如图6),输入Router Project的名字,这里指定名字AccountManagerRouter。点击Next后确定发布的Web Service的WSDL文件和两个方法(如图7)。


图6 选择EAR Project和EJB project
图6 选择EAR Project和EJB project

图7 确定要发布的Web Service的WSDL文件和两个方法
图7 确定要发布的Web Service的WSDL文件和两个方法

4.3 开发WebBankManager

新建一个动态Web项目,名字是WebBankManager,我们将在这个项目中调用AccountManager Web Service 实现转账功能。

首先在该项目内新建一个Web Service 客户机,指定上面生成的AccountManager.wsdl,这样可以使用生成的Web Service Proxy调用AccountManager Web Service。

新建一个Java类,名字是WebBankManager,增加一个transfer方法,如下所示。该方法首先发起一个UserTransaction事务,然后调用AccountManager Service取款和存款,实现转账功能,最后提交事务。如清单 4 所示。


清单 4: WebBankManager的transfer方法实现

	
public void transfer(String fromAccountNo, String toAccountNo, int amount)
			throws Exception {
		UserTransaction userTransaction = null;
		try {
			System.out.println("begin to call transfer amount " + amount);
			InitialContext context = new InitialContext();
			userTransaction = (UserTransaction) context
					.lookup("java:comp/UserTransaction");
			userTransaction.begin();
			AccountManagerProxy accountManager = new AccountManagerProxy();
			accountManager.withDraw(fromAccountNo, amount);
			accountManager.deposite(toAccountNo, amount);
			userTransaction.commit();
			System.out.println("transaction committed");
		} catch (Exception e) {
			userTransaction.rollback();
			System.out.println("transaction cancelled");
		}
	}
	

最后需要将该Web project发布为Web Service,在该项目内新建一个Java Bean Web Service,指定上面创建的WebBankManager Java类,生成WebBankManager.wsdl文件。

4.4 开发WebBankManagerClient,部署测试

主要的功能已经实现了,需要建一个Test Client来测试我们的工作。新建一个WebBankManagerClient Web 项目,然后在该项目中建立一个Web Service 客户机,注意选中 测试Web Service选项,如图8所示,指定上面生成的WebBankManager.wsdl文件后,就创建了包括WebBankManager Web Service Proxy以及测试JSP页面的相关代码。


图8 创建WebBankManager的WebService 客户机
图8 创建WebBankManager的WebService 客户机

目前,例子中所有的项目已经创建完成,J2EE透视图中的项目如图9所示,所做的工作和开发普通的EJB、Web Service没有什么区别。


图9 创建好的示例应用
图9  创建好的示例应用

为了支持WS-AT,我们需要额外设置一下Web项目的事务选项,如图10所示。对于WebBankManager Web Service来说,它接受请求后创建UserTransaction全局事务,并将事务上下文发送出去。而AccountManager WebService接受到事务后需要参与到全局事务并执行操作。


图10 设置应用的事务选项
图10  设置应用的事务选项

图10  设置应用的事务选项

以上设置完成后,部署发布应用程序,就可以测试我们的工作了。启动WAS Server后, 发布导出的WebBankManagerEAR和AccountManagerEAR,运行WebBankManagerClient中的TestClient JSP测试页面,输入转账的两个账号和金额,点击invoke就OK了,如图11所示。


图11 测试页面
图11  测试页面

我们看一下后台的输出,如清单 5 所示:


清单 5: 例子的后台输出

SystemOut     O begin to call transfer amount 200
ServletWrappe A   SRVE0242I: [AccountManager]:初始化成功。
SystemOut     O begin to find account home....
SystemOut     O account home found
SystemOut     O A00001 withDraw begin to find account local
WSRdbDataSour I   DSRA8203I: Database 产品名:DBMS:db2j
WSRdbDataSour I   DSRA8204I: Database 产品版本:5.1.60.17
WSRdbDataSour I   DSRA8205I: JDBC driver 名:Cloudscape Embedded JDBC Driver
WSRdbDataSour I   DSRA8206I: JDBC driver 版本:5.1.60.17
SystemOut     O A00001 original balance = 7000
SystemOut     O A00001 withDraw finish, balance = 6800
SystemOut     O begin to find account home....
SystemOut     O account home found
SystemOut     O A00002 deposite begin to find account local accountNo = A00002
SystemOut     O A00002 original balance = 3000
SystemOut     O A00002 deposite finish, balance = 3200
SystemOut     O transaction committed

查看数据库中的数据,如图12所示,我们可以看到账号A00001少了200,而A00002多了200,从而说明转账已经成功。


图12 事务完成后的Account数据
图12 事务完成后的Account数据

4.5 进一步工作

针对例子可以进一步的测试和验证,读者可以自行验证。

1. 上述例子是一个成功的事务,读者可以试一下事务的回滚。例如:

a) 用户输入的取款金额大于7000会在withDraw方法内抛出异常从而导致事务的回滚。

b) 在deposit方法内写代码抛出异常。在执行WithDraw时候虽然Banlance减少了金额,但由于deposit抛出的异常导致了事务的回滚,从而使得先前的withDraw操作结果被取消。

c) 将WebBankManager的transfer方法 的transaction.commit改为transaction.cancel,测试后可以发现withDraw和deposit方法都已经执行,Server端的控制台输出上显示金额已经改变,但由于最后事务的回滚导致两个方法操作的结果都被取消。

查看数据库的Account表会发现,无论事务的成功或者某个操作的失败导致事务的回滚都不会影响两个账号余额的总和。

2. 该例子中不同的应用程序可以运行在不同的机器上,例如取款的AccountManager应用部署在机器A上,存款的AccountManager应用部署在机器B上,WebBankManager部署在机器C上,从而测试跨越多台机器的WS-AT事务。修改WebBankManager的transfer方法,指定新的地址即可。设置地址代码如下:

 

AccountManagerProxy accountManager = new AccountManagerProxy();
accountManager.setEndpoint("http://yr:9080/AccountManagerRouter/
services/AccountManager"); 

3. 我们可以用TCPMonitor查看发送和接受的SOAP消息,将WebBankManager的transfer方法中的accountManagerPxoxy的endpoint端口改为localhost:9081,然后启用TcpMonitor侦听9081端口,可以看到发出去的SOAP请求消息头部带有符合WS-Coor规范的CoordinationContext,其中包含了注册服务地址、协调类型以及一些扩展信息,如清单 6 所示:


清单6 :头部带有CoordinationContext的SOAP消息

   
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wscoor="http://schemas.xmlsoap.org/ws/2004/10/wscoor"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
   <soapenv:Header>
      <wscoor:CoordinationContextType soapenv:mustUnderstand="1">
         <wscoor:Expires>Never</wscoor:Expires>         
		 <wscoor:Identifier>com.ibm.ws.wstx:000001080454b39
		 00000000100000004d862bc9149d8d8a47c20f3bd31a2a8d60142573f
		 </wscoor:Identifier>
         <wscoor:CoordinationType>
		 http://schemas.xmlsoap.org/ws/2004/10/wsat
		 </wscoor:CoordinationType>
         <wscoor:RegistrationService xmlns:wscoor="
		 http://schemas.xmlsoap.org/ws/2004/10/wscoor">
            <wsa:Address xmlns:wsa="
			http://schemas.xmlsoap.org/ws/2004/08/addressing">
			http://9.181.86.202:9080/_IBMSYSAPP/wscoor/services/
			RegistrationCoordinatorPort</wsa:Address>
            <wsa:ReferenceProperties xmlns:wsa="
			http://schemas.xmlsoap.org/ws/2004/08/addressing">
               <websphere-wsat:txID xmlns:websphere-wsat="
			   http://wstx.Transaction.ws.ibm.com/extension">
			   com.ibm.ws.wstx:000001080454b3900000000100000004d862b
			   c9149d8d8a47c20f3bd31a2a8d60142573f
			   </websphere-wsat:txID>
               <websphere-wsat:instanceID xmlns:websphere-wsat="
			   http://wstx.Transaction.ws.ibm.com/extension">
			   com.ibm.ws.wstx:000001080454b3900000000100000004d862
			   bc9149d8d8a47c20f3bd31a2a8d60142573f
			   </websphere-wsat:instanceID>
            </wsa:ReferenceProperties>
         </wscoor:RegistrationService>
      </wscoor:CoordinationContextType>
   </soapenv:Header>
   <soapenv:Body>
      <p702:withDraw xmlns:p702="http://wsat.ibm.com">
         <accountNo>A00001</accountNo>
         <amount>100</amount>
      </p702:withDraw>
   </soapenv:Body>
   





回页首

 

5. 总结和展望

从上面例子可以看出,使用WAS6.0和RAD6.0开发一个WS-AT事务是非常容易的。由于WAS支持标准的WS-AT规范,在事务服务上可以和其它实现了WS-AT规范的产品进行集成,例如.NET上的非J2EE事务,由于使用了标准的符合WS-AT规范的SOAP消息,WAS中的事务Context可以传递到.NET的Web service中,双方可以进行互操作从而完成一个跨平台的全局事务。

同时,我们也应该看到,目前WAS仅仅支持AT事务,不支持BA事务。由于AT事务主要用于企业内的事务持续时间比较短的环境下,而这通常由普通的J2EE事务就可以完成了。在Web Service的环境下,事务更多的是以BA事务的形式出现的,跨企业多方协作,并常与BPEL流程等结合在一起。对于如何对事务进行补偿,如何处理长时间运行下可能出现的网络故障甚至系统崩溃,如何对非信任域的多方进行协调等问题,目前这些还不成熟,在Web服务事务的进一步发展过程中这些问题会逐步解决,WAS的未来版本肯定也会支持,让我们拭目以待。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值