服务组件体系结构(Service
Component Architecture,SCA)是下一代编程模型,此编程模型提供了三种异步调用模式。您可以使用那些模式异步地调用目标SCA服务,而不需要知道请求和响应消息是如何“魔法般地”进行处理的。本文阐述在发出异步请求时所发生的情况,以及SCA运行时如何处理消息系统中的异步消息。了解如何开发中介处理程序来监视SCA异步消息,以及如何使用中介处理程序来分析异步调用。
引言
IBM WebSphere Integration Developer和IBM
WebSphere Process Server是用于组装和执行SCA应用程序的功能强大的平台。SCA的一个重要特性在于,它与传输协议无关,这意味着业务应用程序开发人员可以集中精力处理业务逻辑。作为开发人员,您唯一需要知道的事情在于,您能够以同步或异步的方式向服务发送请求,而不需要知道请求消息是如何进行处理的。服务也决不需要知道请求来自于何处。请求可能来自于Java
Message Service(JMS)队列、HTTP请求,或者甚至来自于套接字请求。所有这些传输信息对服务提供者和请求者是完全透明的。
但是系统架构师、服务维护团队和技术专家需要知道幕后发生的事情。本文将阐述那些过程。
图1所示的拓扑可以帮助您发现SCA运行时异步行为。该拓扑包括两个模块:Caller和Callee。Caller模块包含两个实现类型为Java的SCA组件。第一个Java组件(Caller)异步地调用同一个模块或另一个模块中的Callee组件。最终用户确定该调用是否以跨模块的方式发生,以及所使用的是哪一种调用类型。
图1. 示例拓扑
在WebSphere Integration Developer中构建Caller和Callee模块以后,WebSphere
Integration Developer将为每个模块生成相关的Java 2 Platform, Enterprise
Edition(J2EE)构件。这些构件是了解SCA运行时行为的关键。例如,对于Caller模块,您将获取如图2所示的构件。
图2. WebSphere Integration
Developer中的Caller模块的构件
在将应用程序部署到WebSphere Process Server以后,您可以在SCA系统总线中看到以下目的地已自动地生成(请参见图3)。
图3. SCA系统总线中的目的地
WebSphere Integration Developer生成的构件和应用程序部署期间生成的目的地一起来完成同步和异步调用。
WebSphere Integration Developer为每个模块生成一个Enterprise
JavaBeans(EJB)项目。在所生成的EJB项目中,WebSphere Integration Developer为该模块生成一个会话Bean,用于为服务请求者提供同步调用接口。所生成的另一个重要构件是消息驱动Bean(message-driven
bean,MDB),用于处理所有的异步请求。MDB从JMS消息正文获取请求消息,并从消息标头中获取目标服务名称。然后它将请求分派给某个SCA运行时。SCA运行时处理消息并调用目标服务。如果存在任何响应,SCA运行时将响应消息写回到JMS目的地中。最后,MDB再次拾取响应消息,将消息分派给SCA运行时,并让SCA运行时将消息发送给调用者。
一般来讲,那些目的地和MDB是SCA异步处理的关键。当SCA检测到存在异步请求(调用方法为async_XXX)时,它将请求放入预定义的目的地中。MDB将被触发,并从消息正文获取消息,从消息标头获取目标服务名称。然后它将请求分派给SCA运行时,后者处理消息,调用目标服务,如果存在任何响应,则将响应消息写回到JMS目的地中。最后,MDB再次拾取响应消息,将消息分派给SCA运行时,后者然后将消息发送给调用者。
因此,关键是了解这些目的地如何协同工作以完成异步调用。以下几个部分将解释所有的详细信息。
消息中介处理程序
前一部分向您介绍了SCA异步处理机制。务必要知道那些目的地如何协同工作以完成异步调用。下面您将了解如何利用消息中介处理程序功能来揭开秘密。
IBM WebSphere Application Server V6.1信息中心将中介处理程序定义为“充当中介中的部署和管理单元的Java程序。它具有控制其行为的中介参数。”
如图4所示,预配置的中介处理程序在消息到达目的地之前被触发。中介处理程序可以:
- 将消息从一种格式转换到另一种格式。
- 将消息路由到一个或多个不是由发送应用程序指定的目标目的地。
- 通过添加来自数据源的数据来扩充消息。
图4. 中介处理程序
本文中的方法是使用中介处理程序来监视所有的SCA目的地,并获取消息路由路径。然后您就可以明白SCA运行时是如何工作的。
首先您将开发一个中介处理程序:
·在WebSphere Integration Developer中创建一个EJB项目,然后创建一个Java类,该类实现一个MediationHandler接口。清单1显示了当前的目的地名称。有关开发自定义中介处理程序的完整描述,请参考IBM
WebSphere Application Server V6.1信息中心。
清单1. 中介处理程序实现
public class SCAMessageHandler implements MediationHandler {
public boolean handle(MessageContext context) throws MessageContextException {
try {
SIMessageContext ctx =(SIMessageContext)context;
System.out.println("=====>destination name:" +
ctx.getSession().getDestinationName());
}
catch(Exception e) {
e.printStackTrace();
}
return true;
} |
打开此EJB项目的部署描述符(Deployment Descriptor,DD),并将该类添加到中介处理程序列表。如图5所示,务必要记住的关键信息是中介处理程序名称,在将应用程序部署到WebSphere
Process Server以后将会使用到该名称。
图5. 定义中介处理程序
- 完成向导之后,DD源文件中将为该处理程序生成一个会话Bean,如清单2所示。
清单2. 示例DD源文件
<session id="SCAMessageHandler">
<ejb-name>SCAMessageHandler</ejb-name>
<local-home>com.ibm.websphere.sib.mediation.handler.ejb.
GenericEJBMediationHandlerLocalHome</local-home>
<local>com.ibm.websphere.sib.mediation.handler.ejb.
GenericEJBMediationHandlerLocal</local>
<ejb-class>com.ibm.websphere.sib.mediation.handler.ejb.
GenericEJBMediationHandlerBean</ejb-class>
<session-type>Stateless</session-type>
<transaction-type>Container</transaction-type>
<env-entry>
<env-entry-name>mediation/MediationHandlerClass</env-entry-name>
<env-entry-type>java.lang.String</env-entry-type>
<env-entry-value>test.SCAMessageHandler</env-entry-value>
</env-entry>
</session> |
- 在EAR文件中公开该EJB项目,并将其部署到WebSphere Process Server。
- 在将中介处理程序应用程序和两个SCA示例应用程序部署到WebSphere Process Server以后,登录到集成的解决方案控制台,以创建中介并在SCA系统目的地上中转该中介。
- 导航到Service Integration>Buses>SCA.SYSTEM.您的单元名称>Bus>Mediations>New。
- 在Handler list name字段中输入SCAMessageHandler(请参见图6)。
图6. 创建中介
- 现在您需要在为Caller和Callee模块创建的所有目的地上中转该中介处理程序。可以将该中介中转到所选的目的地,如图7所示。
- 保存更改,然后对所有的目的地重复上述步骤。
图7. 中转目的地
注意:如果存在许多需要中转的目的地,逐个地中转它们是非常耗时的。一种方法是编写脚本来自动化该过程。您可以使用清单3中的代码将中介处理程序中转到总线中的所有目的地。
清单3. 用于将中介处理程序中转到总线中的所有目的地的代码片段
set listdest "-bus $busname -type Queue"
foreach destination [$AdminTask listSIBDestinations $listdest] {
set destination_name [$AdminConfig showAttribute $destination identifier]
set query "WebSphere:*,type=SIBQueuePoint,cluster=$cluster_Name,
SIBus=$busname,name=$destination_name"
set queuePoints [$AdminControl queryNames $query]
set med2dest "-bus $busname -destinationName $destination_name
-mediationName $mediation_Name -node $node_Name -server $server "
$AdminTask mediateSIBDestination $med2dest
} |
在目的地上中转中介处理程序以后,重新启动服务器以使更改生效。
发现SCA异步调用模式
在将该中介连接到所有的相关目的地以后,您可以访问JSP页面以向SCA运行时发送异步请求。该JSP页面应该与图8所示类似。
图8. JSP发送异步请求
这里,您可以选择一些参数:异步调用类型和调用是否跨越模块边界。
在调用JSP文件以后,JSP页面上选择的参数被传递给Caller组件的Java实现(请参见清单4)。该实现将根据参数发出特定的异步调用。
清单4. Caller组件的实现
public void makeAsyncCall(Boolean isCrossModuleInvocation,
Integer asyncInvocationType) {
System.out.println("=========> into caller's makeAsyncCall method,
parameters are: isCrossModule:" + isCrossModuleInvocation +
" asyncInvocationType:" + asyncInvocationType);
Service callee = null;
if (isCrossModuleInvocation) {
//cross module invocation.
callee = this.locateService_HelloSCAIntfPartner1();
} else {
callee = this.locateService_HelloSCAIntfPartner();
}
switch(asyncInvocationType) {
case 1:
callee.invokeAsync("oneWayCall", "hello");
break;
case 2:
AsyncTicket ticket = (AsyncTicket)callee.invokeAsync("twoWayCall", "hello");
System.out.println("===========>Invocation Type is two way deferred response,
ticket:" + ticket.getId());
try {
Thread.currentThread().sleep(2*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
DataObject result = (DataObject) callee.invokeResponse(ticket, Service.WAIT);
System.out.println(result.get("output"));
break;
case 3:
ticket = (AsyncTicket)callee.invokeAsyncWithCallback("twoWayCall", "hello");
System.out.println("===========> Invocation Type is two way call back, ticket:"
+ ticket.getId());
break;
default:
System.out.println("wrong type:" + asyncInvocationType);
}
} |
总共存在六种组合,接下来的几个部分将对此进行详细的介绍。
单模块异步调用:单向
- 在JSP页面上,确保选择SingleModuleInvocation和asyncOneWay,然后单击Submit按钮。
- 在调用完成以后,打开SystemOut日志文件。您应该看到已生成了如清单5所示的信息。
清单5. 单模块中的单向异步调用的SystemOut日志
AsyncTicket ticket = (AsyncTicket)callee.invokeAsync("twoWayCall", "hello");
//do something
//query result
DataObject result = (DataObject) callee.invokeResponse(ticket, Service.WAIT); |
可以看到,此调用中涉及到两个目的地:sca/Caller/component/Callee和sca/Caller。正如目的地名称所指示的,sca/Caller/component/Callee表示Callee组件,sca/Caller表示该模块本身。关键配置:在将消息路由到sca/Caller/component/Callee以后,消息会自动重新路由到sca/Caller目的地。这是通过在目的地sca/Caller/component/Callee上定义缺省转发路由路径来实现的,如图9所示。
图9. 缺省转发路由路径
基于SystemOut日志文件中生成的日志,您可以创建消息流序列关系图,如图10所示。Caller组件发出异步调用,然后请求将由SCA运行时进行处理。SCA运行时将请求和目标服务信息放入消息中,然后SCA运行时将消息放入sca/Caller/component/Callee目的地中。下一步,根据缺省转发路由路径,消息将路由到sca/Caller目的地。MDB被触发以处理该消息;它只是拾取该消息,从消息正文中获取请求数据,从消息标头中获取目标服务信息,然后将信息发送给SCA运行时。基于此信息,SCA运行时可以知道应该将请求路由到哪一个组件。
图10. 单模块异步调用的消息流序列关系图:单向
单模块异步调用:延迟响应
第二种场景是异步延迟响应。在清单6中,您可以看到您首先发出了一个异步请求。在调用完成以后,您可以获取一个票据(ticket)对象。稍后您可以使用此票据来查询结果
清单6. 延迟响应调用
AsyncTicket ticket = (AsyncTicket)callee.invokeAsync("twoWayCall", "hello");
//do something
//query result
DataObject result = (DataObject) callee.invokeResponse(ticket, Service.WAIT); |
- 在JSP页面上,确保选择SingleModuleInvocation和asyncDeferredResponse,然后单击Submit按钮。
- 在调用完成以后,打开SystemOut日志文件。您应该看到已生成了如清单7所示的信息。
清单7. 单模块中的延迟响应异步调用的SystemOut日志
[6/2/08 13:38:30:942 CST] 00000067 SystemOut O =====>
destination name:sca/Caller/component/Callee
[6/2/08 13:38:30:942 CST] 00000067 SystemOut O =====> destination name:sca/Caller
[6/2/08 13:38:31:036 CST] 00000067 SystemOut O =====>
destination name:sca/Caller/component/Callee
[6/2/08 13:38:31:083 CST] 00000067 SystemOut O =====> destination name:sca/Caller |
在SystemOut日志中,您可以看到除了sca/Caller/component/Callee和sca/Caller以外,还使用了另一个目的地sca/Caller/component/Caller。SCA运行时使用此目的地来传送响应消息。其机制如下:在调用目标服务并生成响应以后,SCA运行时知道该消息是响应消息。然后SCA运行时将消息放入此目的地中(SCA运行时维护有每个调用的消息标头,因此它知道请求来自于何处,以及应该将响应发送到何处)。同样,sca/Caller/component/Caller中定义了缺省的转发路由路径,然后消息将路由到sca/Caller模块目的地。消息一直保留在此目的地中,直到您发出invokeResponse请求来使用它为止。
图11. 单模块异步调用的消息流序列关系图:延迟响应
单模块异步调用:回调
此场景与延迟响应场景相同,只不过结果将由SCA运行时自动发回给调用者。幕后的“魔法”在于,caller类具有一个接收响应的方法。因此如清单8所示,当SCA运行时取回响应消息时,它将调用Caller的特定方法来将响应传递给Caller。
清单8. 回调调用
public void onTwoWayCallResponse(Ticket __ticket, String returnValue,
Exception exception) {
//proces response message.
} |
清单9显示了在使用SingleModuleInvocation和asyncDeferredResponse参数提交请求之后,SystemOut日志文件中生成的日志信息
清单9. 单模块中的回调异步调用的SystemOut日志
[6/2/08 13:39:59:385 CST] 00000067 SystemOut O =====>
destination name:sca/Caller/component/Callee
[6/2/08 13:39:59:416 CST] 0000006f SystemOut O =====> destination name:sca/Caller
[6/2/08 13:39:59:588 CST] 00000067 SystemOut O =====>
destination name:sca/Caller/component/Caller
[6/2/08 13:39:59:603 CST] 0000006f SystemOut O =====> destination name:sca/Caller |
图12. 单模块异步调用的消息流序列关系图:回调
跨模块异步调用:单向
转向跨模块的场景时,情况变得稍微有点复杂,因为必须正确地将请求消息和响应消息传递到正确的目的地,而这些目的地位于不同的模块中。
- 在JSP页面上,确保选择IsCrossModuleInvocation和asyncDeferredResponse。然后单击Submit按钮。
- 在调用完成以后,打开SystemOut日志文件。您应该看到已生成了如清单10所示的信息。
清单10. 跨模块单向异步调用的SystemOut日志
[6/2/08 13:55:31:782 CST] 0000006f SystemOut O =====>
destination name:sca/Caller/importlink/CalleeImport
[6/2/08 13:55:31:829 CST] 0000006f SystemOut O =====>
destination name:sca/Callee/exportlink/CalleeExport
[6/2/08 13:55:31:876 CST] 0000006f SystemOut O =====> destination name:sca/Callee |
根据生成的日志,您可以绘制消息路由序列关系图(如图13所示)。
图13. 跨模块异步调用的消息流序列关系图:单向
这里的要点在于,在部署期间,将会在sca/Caller/importlink/CalleeImport上创建缺省转发路由路径。然后在消息到达此目的地时,消息将被自动转发到sca/Callee/exportlink/CalleeExport,后者驻留在Callee模块中。同样,sca/Callee/exportlink/CalleeExport上定义了缺省转发路由路径,因此在消息到达此目的地时,消息将被转发到sca/Callee目的地。正如前面所讨论的,生成的MDB将发挥作用以使用该请求消息
跨模块异步调用:延迟响应
清单11显示了为延迟响应调用生成的日志。
清单11. 跨模块延迟响应异步调用的SystemOut日志
[6/2/08 13:57:08:491 CST] 0000006f SystemOut O =====>
destination name:sca/Caller/importlink/CalleeImport
[6/2/08 13:57:08:506 CST] 0000006f SystemOut O =====>
destination name:sca/Callee/exportlink/CalleeExport
[6/2/08 13:57:08:506 CST] 0000006f SystemOut O =====> destination name:sca/Callee
[6/2/08 13:57:08:569 CST] 0000006f SystemOut O =====>
destination name:sca/Caller/import/CalleeImport
[6/2/08 13:57:08:569 CST] 0000006f SystemOut O =====> destination name:sca/Caller |
在消息路由序列关系图中,可以看到在结果可用之后,SCA运行时可以直接将响应消息发回到Caller模块的目的地。其基本原理在于,当在Caller的模块中发出延迟响应调用时,SCA知道该调用是跨模块调用,并将响应目的地名称放入请求消息标头中。然后在目标端,当服务调用返回时,SCA运行时将向从请求消息标头中检索到的目的地发送响应。
您可以使用清单12中的示例代码来从消息标头中获取响应目的地名称。消息路由序列关系图如图14所示。
清单12. 用于获取响应目的地的示例代码
SIMessageContext ctx =(SIMessageContext)context;
String responseDestination = ctx.getSIMessage().getUserProperty ("scaTargetDestination")); |
图14. 跨模块异步调用的消息流序列关系图:延迟响应
跨模块异步调用:回调
回调调用类似于延迟响应调用,只不过结果将自动传递给调用者。清单13显示了所生成的日志。
清单13. 跨模块回调异步调用的SystemOut日志
[6/2/08 14:01:07:143 CST] 0000006f SystemOut O =====>
destination name:sca/Caller/importlink/CalleeImport
[6/2/08 14:01:07:159 CST] 0000006f SystemOut O =====>
destination name:sca/Callee/exportlink/CalleeExport
[6/2/08 14:01:07:581 CST] 00000077 SystemOut O =====> destination name:sca/Callee
[6/2/08 14:01:07:659 CST] 0000006f SystemOut O =====>
destination name:sca/Caller/component/Caller
[6/2/08 14:01:07:690 CST] 00000077 SystemOut O =====> destination name:sca/Caller |
消息路由序列关系图如图15所示。
图15. 跨模块异步调用的消息流序列关系图:回调
结束语
在本文中,您了解了SCA运行时如何处理异步调用,以及SCA运行时如何利用基础的消息系统(WebSphere
Process Server)来实现异步调用。架构师可以使用此信息作为参考,从而设计大规模、基于消息的面向服务的体系结构(Service-Oriented
Architecture,SOA)。
|