作者 Boris Lublinsky 译者 陈义 发布于 2009年7月26日 下午11时9分
社区 SOA 主题 工作流/业务流程管理, 编制 标签 JBoss垂直市场解决方案(VMS)是NAVTEQ公司中的一个机构,负责为客户提供定制的解决方案,包括移动门户和导航系统。这些解决方案中包含了NAVTEQ 公司提供的服务以及第三方服务,以客户要求的方式交付组合服务和内容,这些方式包括Web services,WAP,portals等。
VMS在响应庞大的销售机会和预见未来客户需求方面面临着一些挑战。为了解决这些挑战就需要开发复合服务(composable services),这些服务可以快速地、方便地集成到整个客户解决方案中。用于服务集成的中间件基本上能解决这些问题。
在本文中,我将讨论如何使用JBoss中间件平台来构建这样的系统,尤其是使用JBoss ESB和jBPM(JBoss Business Process Management)。
总体解决方案
在深入了解JBoss中间件产品的细节之前,我将快速地描述构建VMS解决方案的基本方式。
目前, NAVTEQ公司提供了这些解决方案所需的所有功能,包括功能强大的基于位置的服务,比如地理编码 (Geocoding)和路由,交通相关服务,比如交通事故信息,交通堵塞因子计算和所指定线路的交通报警,以及一般的电子商务功能,包括采购,授权和权限管理。
给客户直接暴露这些服务存在多种问题:
- 每个服务只提供特定的功能,而NAVTEQ公司的客户希望有一个组合服务。例如,客户可能需要相关路线的交通信息和事件信息。这可以通过几个步骤实现—— 首先是地理编码,接着是标点,再通过标点计算路线,最后获得该线路的事件信息以及交通信息。根据客户的需求,可以通过这一系列的步骤构建新的组合服务。
- 现有功能正在不断的涌现,结果是它们使用不同的数据模型/定义,这导致服务的使用和编排更加复杂。
- 不同的客户可能需要通过不同的机制传递内容/服务。有些客户可能希望能够使用SOAP或REST直接访问服务,有些客户可能希望将HTML/JavaScript片段传输给他们的网页/门户网站,另外一些客户可能需要WAP接口连接到服务等等。
- 现有实现有自己的安全,用户管理,监控和管理架构/实现。
最后,我们采用如图1所示的总体架构来实现。
图 1 总体解决方案架构图
架构图的中间部分是VMS平台,它通过编排现有的后端功能实现了特定客户所需的服务。这个平台主要由三层组成:
- 后端适配器(Back end adapters)—— 这一层负责访问现有的后端(和/或者第三方)功能,并将执行的结果(以标准域模型的形式)返回到平台中。
- 服务实现层(Service implementation layer)—— 负责将现有的(可通过后端适配器访问的)服务编排成客户所需的服务。另外,还要实现一些尚未实现的定制功能。
- 定制适配器(Consumer adapters)—— 这一层负责通过不同的协议/传输将服务执行的结果传输到不同类型的客户端。
下面我将介绍如何使用JBoss中间件平台实现这个架构。
JBoss ESB
JBoss ESB是一个开源的ESB,它基于違規廣告 ESB,支持服务的创建、部署和整合。
从架构上而言,可以将Jboss ESB中的一切都看作是服务。这些服务并非Web Services,而是ESB服务,这些ESB服务可以通过多种传输暴露出来。所有的ESB服务都有一个方法(doWork),可以通过下面的接口(由所有的服务共享)描述:
Message utput = service.doWork(Message input)
在JBoss ESB中,ESB消息和SOAP消息类似,都由几个部分组成,包括标头(header),消息体(body),错误(fault),附件(attachments),等等。每个部分包括一个可序列化的java对象集合(map),通过集合中定义的name进行访问。这就意味着JBoss ESB消息并不是强类型的,在访问消息时需要注意(类型转换)。
服务定义的形式:
- 服务目录/名称(Service Category/Name)——Jboss ESB有一个内部的服务注册机制(部署服务时,服务会自动注册),其内部为访问服务提供优化机制。
- 输入消息组件 —— 包括名称(names)(在入站消息体中)和类型(type)
- 输出消息组件 —— 包括名称(names)(在出站消息体中)和类型(type)
传统ESB是使用一系列拦截器(interceptor)构建服务,允许在服务调用管道(pipeline)中注入额外处理。不同的是,JBoss ESB将服务作为一个显式的管道(pipeline)来构建,管道包含一系列的动作(action),如图2所示,每个action实现了(潜在部分的)服务业务功能或者基础功能,比如在消息存储中存储服务消息。
图2 JBoss ESB服务
与基于拦截器的传统ESB不同的是,JBoss ESB可以很好地隔离“业务”(服务实现)和相关基础设施(拦截器)。JBoss ESB在可执行的管道中将二者连接起来。然而这并非毫无价值,在一个服务管道中包含所有action都可以简化服务处理的理解。
JBoss ESB中的服务action是一个java类,它必须实现接口类org.jboss.soa.esb.actions.ActionLifecycle。服务的实现往往将服务功能分解成一系列的action,然后分别实现每个action。这类服务实现组织为JBoss ESB开发者提供了两个级别的复用——服务本身和可以在多个服务中复用的action。JBoss同样也提供了一个可复用action库。该库中包括如下类型的action1:
- 转换器(Transformers & Converters)
- 支持JBPM
- 脚本(Scripting)
- 路由(Routing)
- 支持Web Service
- Misc
服务请求可通过多种传输传递。服务定义包括一系列传输/端点的引用。服务在端点(每个端点都有一个唯一指定的服务)上监听消息。服务定义中也可以通过指定(在传输上)处理服务请求的线程数来配置服务的吞吐量。
服务action调用的设置和顺序以及服务监听的端点都是在jboss-esb.xml文件中配置。可以给指定的服务配置一系列action(它们由JBoss或者特定的公司或者服务所提供)。这种支持action的外部XML配置,极大地提高了actions的复用。
JBoss ESB中的服务调用
正如上文中提到, JBoss ESB中的一切都服务,异步2调用是默认的调用模式。JBoss ESB同样也支持同步调用,同步调用可以使用相关性实现。ESB服务的同步和异步调用可以通过下面两种方式进行控制:
- 服务实现通过使用消息交换模式(MEP)参数定义服务行为(service behavior)。Request-Response是默认的MEP,也就是说服务必须返回一个响应(response)。如果将MEP设置成为one-way,那么服务无需返回响应信息。
- 服务消费者可以自己定义它是否需要回复。如果需要,request-response MEP服务将返回一个响应信息3。
为了简化服务调用,Jboss ESB提供了org.jboss.soa.esb.client.ServiceInvoker类,该类为服务调用提供了一个非常简单的接口,调用时只需要提供一个目录/服务名称作为参数。
JBoss ESB的本地服务调用
除了远程传输外,JBoss还提供了高效的本地(在同一个JVM中)调用机制,它是通过一个内存队列来实现的。当在服务调用中使用 org.jboss.soa.esb.client.ServiceInvoker时,如果该服务采用本地调用且采用本地部署,那么内存传输总会被选择用来优化性能。
支持Web Services
如今,当人们谈论ESB时,经常会强调Web Services的处理。技术上而言,JBoss ESB中包含很多基于Web Services的组件,这些组件可以暴露和调用Web Services端点(比如,总线上SOAP的开启和关闭):
SOAPProcessor通过JBossESB托管的监听器支持调用JBossWS托管的Web Service端点。这意味着可以通过ESB为其它的ESB服务暴露Web service端点。它是基于一个轻量级的服务包装器(Service Wrapper)Web service(比如,JSR 181规范4的实现)而实现的,通过它可以调用目标服务(target Service)。这也意味着这些服务可以通过ESB所支持的传输通道(http, ftp, jms等)进行调用。在Web Service端点(使用JAXWS注解的java类)上配置的SOAP处理器(processor),实际上由Web Service端点处理SOAP请求。
SOAPClient使用WISE5客户端服务生成JAXWS客户端代码,并调用目标服务。通常是使用Web Service的WSDL的URL动态生成JAXWS客户端,以调用web service。
在开发的过程中,我们发现采用这种方式使用web service是很笨重的,所以需要采用如下策略:
- 要是消费web service,我们直接通过可用的WSDL生成web service客户端(可以采用Axis 1,Axis 2 或者JBoss WS)。
- 要是暴露ESB服务,我们可以为SOAP采用JBoss WS ,为REST采用RestEasy来暴露org.jboss.soa.esb.client.ServiceInvoker,并调用所需的服务。这在架构上和使用SOAPProcessor是一样的,但更通用一些,这允许服务功能可以暴露成SOAP、REST或者其它所需接口。
JBoss ESB工具
通过XML文件可以配置服务监听器和服务执行管道。该XML配置文件由3个文件组成,这些是配置ESB服务6不可缺少的文件:
- jbm-queue-service.xml中定义了服务7所用的每个传输(通常是队列)相关的MBean。
- deployment.xml定义了服务依赖的所有传输。部署ESB服务时会验证该文件中的依赖关系是否正确。
- jboss-esb.xml中定义了监听器和服务管道。
虽然jbm-queue-service.xml和deployment.xml文件可以直接通过XML进行编辑,但JBoss提供了工具简化ESB项目8的创建、部署和维护。图3 显示了jboss-esb.xml的编辑器。
图3 JBoss ESB工具
该工具以图形化的方式添加和配置监听器、服务和动作(包括动作的参数)。
使用Smooks实现数据转换
最重要的服务动作是数据转换。JBoss ESB使用Smooks9实现数据转换。Smooks的基本原理是使用多种类型的数据源,并从数据源中生成事件流。然后对事件流采用访问者模式(Visitor pattern)生成不同类型的数据。10它支持多种不同的数据源和数据类型,这意味着支持多种转换类型,包括(但不仅限这些):
- XML to XML
- XML to Java
- Java to XML
- Java to Java
- EDI to XML
- EDI to Java
- Java to EDI
- CSV to XML
- CSV to ...
为了简化Smooks的使用,JBoss ESB提供了特定的action11,可以通过配置直接调用Smooks——并将转换作为服务管道的一部分。
为什么是Smooks? 技术上而言,可以直接通过java代码实现数据转换,那么为什么要使用Smooks呢?(记住:Smooks是基于XML配置文件的,编写XML文件往往比直接通过Java代码实现还要慢12……) 数据转换中使用Smooks的好处:
|
Smooks工具
JBoss ESB工具为可视化定义Smooks转换提供了图形化编辑器。
图 4 Smooks图形编辑器
数据转换创建完后,可以通过Smooks的执行报告(图 5)以视图的形式展现该过程中的所有执行步骤。该视图极大程度上简化了Smooks转换的debug过程。
图 5 Smooks执行报告
常见的Smooks转换设计错误 设计Smooks转换时,最重要的是要记住转换不是上下文敏感的。也就是说,比如,有如下一个XML文档: 12345 12345 当使用“c”作为选择器时,它会被调用两次。第一次是“b”元素里面的“c”,第二次是“d”元素中的“c”,即使是使用选择器"b"也会调用两次。 为了避免出现这些问题,需要使用源文档中具有唯一名称的选择器。 |
JBPM
JBoss jBPM是一个灵活的,可扩展的流程语言框架13。jPDL是建立在该公共框架上的一种流程语言,它将业务流程图形化的表示成任务(tasks),异步通信的等待状态(wait states),计时器(timers),自动化动作(automated actions)和其它组件。
jPDL最大程度地减少了对其它lib库的依赖,可以像使用java库一样简单地使用。另外,也可以用在对吞吐量非常重要的J2EE集群应用服务器的环境中。它还支持多种数据库,可部署在任何应用服务器上。
与BPEL不同的是,BPEL与Web services是紧耦合的(在BPEL中,每个活动都必须作为Web service14而实现),而JBPM更像是一个组件框架15,允许直接调用Java处理器(类似于ESB服务调用管道)。
如果已经存在一个服务执行管道,那为什么还需要额外的服务编排机制呢? 看起来,服务管道和服务编排之间存在很多重叠。本质上,服务编排是一个有序的action编排。但它不支持最常见的编排功能,比如,决定(decisions),条件转换(conditional transitions)和并行执行(parallel execution)。虽然在技术上可以实现这些功能,并作为管道定义中的一部分,但实现起来并不容易。我们更倾向于将基本的业务功能和额外的基础设施(包括,数据转换,执行监控等)结合在一起时使用服务管道,而编排服务时使用jBPM。 部署所需要考虑的问题。如果在很多用例中都用到了同一个动作,那么可以将该动作分离出来,作为一个单独的服务,因此部署时只用部署一次,并可以用于jBPM的服务编排中。 |
JPDL主要由节点和执行上下文组成。JPDL定义了如下类型的节点:
- Start Node —— 启动流程
- Task —— 人工活动
- State —— 等待状态
- Node —— 自定义的执行代码
- Decision —— 基于流程变量的决定
- Fork —— 在多个路径中分离执行
- Join —— 汇合多个执行路径
- Transitions —— 节点之间的连线
- SuperState —— 聚合多个节点
- End Node —— 流程结束
执行上下文(execution context)和HTTP的session有些类似,它包含一系列的命名对象——它与先前介绍的ESB消息体有些类似。任何节点都可以访问执行上下文,可以读、写上下文的变量。执行状态被持久化到数据库中——当JPDL流程处于等待状态时,它的上下文(上下文变量)被存储到数据库中。当流程再次被激活时,从数据库中读取上下文变量的值,重新创建上下文。
节点中装载JPDL流程(在服务编排中)。节点只是执行一个动作处理器——一个实现了org.jbpm.graph.def.ActionHandler 接口的类。节点动作处理器和服务动作处理器相似,都是可配置的,只是使用不同的配置方法。任何定义在动作处理器中的public/private变量都可以通过流程定义进行配置。
异步执行 通过配置节点的节点类型来指定异步执行。注意,JPDL中的异步执行并不是使用线程实现的,而采用的是队列机制。如果节点执行采用异步调用,那么流程的当前状态会被持久化到数据库中。通过org.jbpm.job.executor.JobExecutor类可以继续执行流程。运行这个类的标准配置方法是在单独的WAR中配置org.jbpm.job.executor.JobExecutorServlet servlet。在本文中,我们不使用这种方法。而是通过从JPDL中调用ESB服务来实现异步执行(详细内容如下)。 |
Decision节点是另一种节点,它支持自定义实现。只需要实现接口org.jbpm.graph.node.DecisionHandler即可。当实现Node功能时,可以像配置Action处理器一样配置Decision处理器。
在JPDL中使用Fork/Join节点可以实现并行执行。与Fork和Join节点相连接的路径可以并行实现。
Fork/Join执行 JPDL中的Fork并不是基于线程实现的。也就是说当transitions离开Fork节点时,Fork的实现并不为它们创建线程。只为每个transition创建令牌。因为流程执行是单线程的,所有的令牌将顺序执行。实现Fork/Join并行执行的一种方法是通过从JPDL中调用ESB服务(详细内容如下)。 |
循环在服务编排中使用得非常广泛,但JPDL却不支持它。顺序的循环可以通过使用Decision节点和计算循环变量实现,更为复杂(在服务编排中非常普遍)的场景是并行循环——运行时计算transition路径数量的Fork/Join。在JPDL中并不支持这种模式,但是定义两个自定义的处理器就可以很简单地实现这个功能(见附录中的列表1,列表2)。
这些处理器的实现都是基于JPDL Fork/Join而实现的。Start处理器创建多个令牌,并启动它们。End处理器等待所有的子令牌完成,然后transition到下个节点。
异常处理
流程执行时会产生异常。JPDL中可以为每个流程节点定义异常处理器。异常处理器也是一种action处理器,当节点执行时出现异常,它将会被调用。附录中列举了一个简单的异常处理器的例子。该异常处理器输出异常信息,然后在目的变量中将异常信息过渡到特定的节点。
部署流程
JPDL的流程定义并不是作为JBoss应用部署的,而是保存在流程数据库中。
JBPM/ESB整合
JBoss ESB和JBPM都是非常强大的软件平台,但是如果将ESB/JBPM整合起来,那么功能更强大16。
两种ESB/JBPM整合类型:
- 将业务流程暴露为服务
- 从业务流程中调用服务
JBoss/JBPM整合提供了一个特殊的action处理器——BpmProcessor,它通过调用jBPM的命令API与jBPM交互。它以一个流程定义名称作为参数,就可以创建和启动一个流程实例。此时,流程在单线程中异步执行,也就是说当流程运行时,服务会返回一个应答给服务调用者。
这种整合也实现了两个jBPM action处理器类——EsbActionHandler和EsbNotifier。EsbActionHandler是一种request- reply类型的action,它将消息发送到服务总线上,然后等待响应。其架构如图6所示。EsbActionHandler将请求消息发送给用户服务,并将流程变为等待状态。当用户服务执行完后,它会调用一个特殊的JBPM服务,该服务通知等待流程继续执行。
图 6 JBPM/ESB整合架构
每种集成类型都为流程中的多个提供了异步执行(比如,fork/join或者并行循环的执行见上文)。
而EsbNotifier只需要将消息发送给服务,可以继续自己的流程。它与JBossESB的交互本质上是异步的,当服务执行时,不会阻塞流程实例的运行。
流程的同步调用
如上所述,BpmProcessor异步的调用业务流程,这在服务编排时,并不总是可取的;流程执行生成的内容会用于服务响应。另外,ESB/JBPM集成还支持如下场景17。
BpmProcessor支持一个额外的配置参数——reply-to-originator。当这个参数为true时,BPMProcessor 会将服务调用的ReplyTo信息保存在新创建的流程实例的JBPM执行上下文中。 EsbNotifier将reply-to-originator作为notifier的目的地,使用ReplyTo传递流程执行的结果。为了达到这种效果,服务的调用流程必须定义为MEP的Oneway方式。
异常处理
当在业务流程中调用服务时,异常并不仅仅出现在节点执行(服务调用)中,而且会出现在被调用服务的执行中。这就需要一种特殊的异常处理器。该处理器作为 BpmProcessor的一部分而定义,它可以控制服务调用的transition——在成功或者异常时的不同transition。
JBPM工具
JPDL是一种基于XML的语言,可以通过XML表示。为了简化业务流程的创建和分析,JBoss提供了一个Eclipse插件,通过它可以可视化的创建和维护JPDL流程(如图7所示)。
该编辑器即支持图形化的业务流程视图,也支持XML格式的业务流程视图。也可以直接在Eclipse中将流程部署到JBoss服务器。
图 7 JPDL编辑器
整体评估
JBoss ESB/jBPM整合为基于已有企业资源创建面向服务的解决方案提供了一个非常强大的、可扩展的和灵活的平台。它为这些解决方案的实现提供了所有主要组件,包括:
- 灵活的ESB平台,可以很容易地将现有功能和其他业务以及服务实现(服务管道)所需的基础设施流程相结合起来。
- 强大的转换引擎(Smooks),简化了将私有数据模型(现有功能所使用的)转换为解决方案中所使用的语义数据模型。支持可视化转换定义的工具进一步简化数据转换。
- 轻量级的可扩展的jBPM促进了构建基于现有NAVTEQ功能的组合服务。
本文所介绍的JBoss中间件是基于NAVTEQ构建的个别VMS解决方案的原型。该原型基于特定领域模型,用于管理用户、位置和路由,还包括一些ESB 服务,这些服务都是对现有Web service的包装,并且服务直接在SOA平台中实现。它也包括数个使用jBPM实现的组合(还有分层次的组合18) 服务。
我们正在提高JBoss中间件的性能和稳定性——基于解决方案和使用监控和管理SOA解决方案的JBoss Operations Network19(JON)。
感谢
非常感谢我的NAVTEQ同事们,尤其是Robert Camp,Ian Mondragon和Jeffrey Herr,还有JBoss解决方案架构师们,尤其是Ray Ploski和Aaron Pestel,他们实现了本文中提到的原型并描述了他们的结果。
附录. 代码
com.navteq.jbpm.parallel; java.util.HashMap; java.util.Map; java.util.Set; org.jbpm.graph.def.ActionHandler; org.jbpm.graph.def.Node; org.jbpm.graph.def.Transition; org.jbpm.graph.exe.ExecutionContext; org.jbpm.graph.exe.Token; ParallelStart ActionHandler { serialVersionUID = 1L; String ; String ; Map
Listing 1 Start Parallel execution handler
com.navteq.jbpm.parallel; java.lang.reflect.Array; java.util.Iterator; java.util.Map; java.util.Set; org.hibernate.LockMode; org.hibernate.Session; org.jbpm.JbpmContext; org.jbpm.graph.def.ActionHandler; org.jbpm.graph.exe.ExecutionContext; org.jbpm.graph.exe.Token; class ParallelEnd implements ActionHandler { static final long serialVersionUID = 1L; /** species wether what type of hibernate lock should be acquired. value defaults to LockMode.ce */ String parentLockMode; String loopCount; String ; Map
Listing 2 End Parallel execution handler
com.navteq.jbpm.actionHandlers; org.jbpm.graph.def.ActionHandler; org.jbpm.graph.def.Node; org.jbpm.graph.exe.ExecutionContext; org.jbpm.graph.exe.Token; class ExceptionActionHandler ActionHandler { = 1L; String ; @Override execute(ExecutionContext context) throws Exception { Token token = context.getToken(); Node sourceNode = token.getNode(); Throwable throwable = context.getException(); System..println( + throwable.getMessage() + + sourceNode.getName()); Node targetNode = context.getProcessDefinition().getNode(); token.setNode(targetNode); token.signal(); }
列表 3 异常处理器
1ESB Message Action Guide-Ch. 1
2在違規廣告 ESB中,参与通信的都是消息。
3如果服务的MEP采用one-way方式,那么使用request/reply调用方式时会产生死锁,因为采用one-way方式的服务不会传输回复。
4JSR 181: Web Services Metadata for the Java Platform
5http://www.javalinuxlabs.org/drupal/
6在一组配置文件中可以配置多个服务。
7 在JBoss运行时中可以直接配置不同传输。
9http://www.smooks.org/documentation/documentation-smooks-1-1-x/user-guide
10http://www.infoq.com/articles/event-streaming-with-smooks
11JBoss Enterprise SOA Platform
14很多业务流程产品对BPEL进行了扩展,比如IBM的Process Server和Oracle的Process Manager产品都可以突破这些限制。
15Process Component Models: The Next Generation In Workflow ?
16SOA ESB jBPM Integration Guide
17http://lists.jboss.org/pipermail/esb-issues/2008-July/008059.html
18我将组合服务的组合称为层次组合服务。
查看英文原文:Using JBoss ESB and JBPM for Implementing VMS Solutions。
来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/41508/viewspace-625852/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/41508/viewspace-625852/