异步处理通信是面向服务架构(SOA)的重要部分,因为企业中的许多系统通信,尤其是跟外部系统通信本来就是异步的。java消息服务(JMS)就是用来编写异步消息J2EE应用的API。使用JMS API的传统消息实现涉及到象这样的一些步骤:查找对列连接工厂、队列资源以及在实际发送和接受消息前,创建JMS会话(JMS session)。
SPRing framework简化了用JEE组件(包括JMS)开发JMS应用的工作。它提供了一个模板机制来隐藏典型的JMS实现细节,所以开发者可以专注于消息处理任务而不用担心怎样创建、访问和释放JMS资源。
本文用一个运行在JBoss MQ server上的简单Web应用概述了Spring JMS API和怎样使用它异步处理(发送和接受)消息。我将对比JMS实现的传统方法和Spring JMS实现方法,以显示使用Spring JMS来处理消息是多么的简单和灵活。
异步消息和SOA
现实世界中,大多数Web请求是同步处理的。例如,当用户登陆一个站点,他或她输入用户名和口令以及服务器识别登陆凭证。假如身份验证成功,程序让用户进入站点。这里,登陆请求从客户端被接受后,立即被处理。信用卡授权也是一个同步处理的例子;仅当服务器核实了发送进来的信用卡号是有效并且该客户的帐号有足够的信用额度后,才答应客户继续进行下一步动作。让我们来考察一下订单处理系统中的支付结算步骤。一旦系统核实了那个用户的信用卡信息是正确的,而且帐户上有足够的资金,那么不需要等到支付细节和转帐最终完成。支付结算用异步方式处理,如此客户便可以继续进行结帐处理。
与典型的同步请求相比,异步处理用于需要长时间来处理的请求。异步处理的另外一个例子是住房贷款处理应用中,处理提交到AUS(Automated Underwriting System)的贷款请求。贷款人提交贷款申请后,抵押公司发送请求到AUS取得信用历史信息。因为该请求要取得综合具体的信用报告如贷款人当前和过去的信用帐户,最近的支付以及其它金融具体信息,所以从请求到获得响应经常需要很长时间。对客户端程序来说开一个到服务器的连接并且长时间等待响应是没有意义的。于是就有了异步通信;也就是,一旦请求被提交,它就被放入队列里面并且客户断开服务器连接。然后,AUS服务从特定队列摘取请求,处理它,把结果消息放入另外一个消息队列。最后客户程序从消息队列摘取响应结果继续处理信用历史结果信息。
JMS
假如用过JMS的话,会发现它类似写JDBC或JCA代码。它有创建或检索JMS资源的样板代码,每当你需要编写一个新类来发送或接受消息时,都得重复编写那个样本代码。下面列出了传统JMS实现涉及的步骤:
1、创建JNDI初始上下文context;
2、从JNDI上下文获得队列连接工厂;
3、从队列连接工厂取得队列Queue;
4、创建一个Session对象;
5、创建一个发送或接受对象;
6、利用第5部创建的发送或接受对象发送或接受消息;
7、处理完消息后,关闭所有JMS资源。
如你所见,只有第6步是处理消息的步骤。其他步骤都只是治理JMS资源,与实际业务需求无关,但开发者不得不编写和维护那些附加步骤代码。
Spring JMS
Spring框架提供一个模板机制来隐藏Java API细节。JEE开发者可用JDBCTemplate 和JNDITemplate类来分别访问后端数据库和JEE资源(数据源,连接池)。JMS没有异常。Spring提供了JMSTemplate类,所以开发者不必为JMS实现编写样本代码。当开发JMS应用时,Spring提供了一下一些优势:
1、提供了一个JMS的抽象API,简化了JMS的使用。如:访问目的地(队列或主体)和出版消息到特定目的地。
2、JEE开发者不必关心JMS不同版本之间的差异(如JMS 1.0.2 同 JMS 1.1);
3、开发者不必特定地处理JMS异常,因为Spring为JMS代码抛出的任何JMS异常提供了一个unchecked异常。
一旦你在JMS应用中开始使用Spring,你将会欣赏到异步消息处理的简易性。Spring JMS框架提供了各种java类使JMS开发变得简单。
点击查看大图表1,Spring JMS类
随后的部分,我将具体解释表1中的类(如JmsTemplate, DestinationResolver,和 MessageConverter)。
JMSTemplate
JmsTemplate提供了几个helper方法来执行基本操作。开始使用JmsTemplate前,有必要知道JMS提供者支持哪种JMS规范。JBoss AS 4.0.2 和 WebLogic 8.1服务器支持JMS1.0.2规范。WebLogic 服务器 9.0包含JMS1.1支持。JMS1.1统一了PTP和Pub/Sub编程接口。有了这个改变,开发者可以创建一个事务会话,然后在同一个JMS事务中,从Queue(PTP)接受消息和发送一个消息到Topic(Pub/Sub)。JMS1.1向后兼容JMS1.0,因此基于JMS1.0编写的代码仍然能跟JMS1.1工作。
JmsTemplate提供各种方法来接收和发送消息。表2是方法列表。
点击查看大图表2。JMS模板方法
使用JNDI上下文存储和检索目的地。当配置Spring应用上下文时,我们用JndiObjectFactoryBean获得JMS目的地引用。DestinationResolver用来解析目的地名称到一个JMS目的地,当应用有许多目的地时,那是很有帮助的。DynamicDestinationResolver(缺省DestinationResolver实现)用于解析动态目的地。
MessageConverter接口定义了java对象和JMS消息之间转换的契约。使用转换器,应用代码可以专注于业务对象,不用操心它是如何代表JMS消息的。SimpleMessageConverter(和SimpleMessageConverter102)是缺省MessageConverter实现。它们用于将String、字节数组((byte[])、Map、Serializable对象分别转换成JMS TextMessage、JMS BytesMessage,JMS MapMessage,JMS ObjectMessage。你可以编写MessageConverter接口的定制实现并结合xml绑定框架如JAXB, Castor, Commons Digester, XMLBeans, 或 XStream来转换XML文档到TextMessage。
样本应用
我将用一个样本贷款应用处理系统(叫LoanProc)来说明怎样在JMS应用中使用Spring。作为贷款处理的一部分,LoanProc发送贷款具体资料(loan ID, borrower name, borrower's SSN, loan eXPiration date, and loan amount)从AUS系统请求信贷历史。为让例子简单一点,我们将基于两个参数:信用评分和贷款数量来获得信贷历史具体资料。让我们假定处理信用检查请求的业务规则如下:
1、假如贷款数量等于或小于$500,000,那么贷款人必须至少有一个“good”信用(例如,贷款人的信用评分在680到699之间);
2、假如贷款数量超过$500,000,那么贷款人必须至少要有一个“very good”信用,这意味他/她的信用评分超过700。
贷款应用Use Case
贷款请求处理Use Case由下列步骤组成:
1、用户在贷款申请web页面输入贷款具体资料并提交贷款申请;
2、然后程序发送贷款具体资料到AUS系统取得信用历史具体资料。用发送请求到名叫CreditRequestSendQueue的消息队列来完成。
3、AUS系统从队列摘取贷款具体资料并用贷款参数来从数据库检索信用历史信息;
4、然后AUS系统用找到的贷款人信用历史信息创建一个新的消息并发送到名叫CreditRequestReceiveQueue的消息队列;
5、最后LoanProc从接收消息队列摘取响应消息并处理贷款申请,决定申请是被核准还是拒绝。
应用中,在同样的JBoss MQ server中配置了两个消息队列。Use Case用序列图1表示如下:
点击查看大图图1:贷款处理应用的序列图