使用Mule ESB与Groovy编排RESTful服务【转】很适合我们当前的架构

http://www.infoq.com/cn/articles/restful-services-mule 

在过去几年中,REST风格的软件架构获得了越来越多的认可,这主要是因为它减少了系统对动件的需求、同时使系统耦合性更低,弹性更好。 
目前越来越多的REST资源出现在企业应用中,因此对这些资源进行编排就显得非常重要了。比方说,典型的业务活动就包含了资源的创建,然后是资源的查找及其他资源的创建等。 

本质上来说,与RESTful服务的交互是相当简单的:需要构造并发送适当的请求(请求头和请求体),然后分析返回的响应(响应头和响应体)。完成这个处 理并不需要什么特别的工具或是框架,只要有一个好用的HTTP客户端程序库就足够了。除此之外,由于RESTful交互过程中所涉及到的不同实体是由所谓 的微格式定义的,因此能够轻松解析或是输出这些实体的能力就显得非常重要了。 

编排与多个资源的交互是个棘手的问题。我们需要定义编排、处理错误并不断尝试,同时系统必须能够在压力下表现良好。作为一个集成框架,Mule提供了我们所需的一切。 

来考虑一个例子吧,某商业系统对新订单的处理需要如下编排: 

通过向服务发送一个XML实体来创建一个新订单。 
寻找新创建的订单资源并从中提取出确认信息。 
根据确认信息并通过邮件网关向客户发送一条确认消息。 
我们将在本文详细分析上面每一步的交互过程,同时还会介绍为了获得上述交互所需的Mule动件和Groovy特性。 

总体的编排包含了一系列通过特定路由、过滤器以及内存消息队列(aka VM队列)连接起来的Mule服务。 最近InfoQ上的这篇文章介绍了Mule中的消息路由,大家不妨移步一观。 

Mule对REST的支持 
Mule提供了一种简单而又强大的方式与RESfFul服务交互,那就是Mule RESTPack。 

Mule RESTPack提供了一整套连接器与完整的指南以帮助用户创建新的RESTful应用。在本文撰写之际,该软件包提供了三种传送器,分别基于三种流行的REST框架:Abdera、Jersey及Restlet。这样我们就可以轻松公开新的资源,但如何集成现有的REST资源呢? 

好消息是Mule标准的脚本模块所提供的Groovy支持有助于Mule HTTP传送器的使用,这样我们就可以成功与RESTful服务交互了。 
向Mule发送POST请求 
首先来看看第一个交互。HTTP向特定的资源发送一个XML实体来创建订单,如以下程序片段所示: 

POST /orders HTTP 1.1 
... 
Java代码  收藏代码
  1. <order xmlns='urn:acme:order:3:1'>  
  2.    <customerId>123456</customerId>  
  3.    <productId>P987C</productId>  
  4.    <quantity>2</quantity>  
  5. </order>  

如果成功服务器的响应如下: 

201 Created 
Location: http://acme.com/order/O13579 
... 
<order id='O13579' /> 
在Mule中,我们可以通过一个简单的HTTP输出端点(outbound endpoint)实现该交互。注意到交互本身是通过向订单创建服务发送一个包含请求值(客户与产品ID、数量)的映射而触发的。该服务如下所示: 

Java代码  收藏代码
  1. <service name="OrderCreationService">   
  2.   <inbound>   
  3.     <inbound-endpoint ref="OrderCreationQueue" />   
  4.   </inbound>   
  5.   <outbound>   
  6.     <chaining-router>   
  7.         <http:outbound-endpoint synchronous="true"   
  8.               responseTimeout="15" method="POST"   
  9.               host="${acme.order.hostname}"   
  10.               port="${acme.order.port}" path="orders"   
  11.               user="${acme.order.username}"   
  12.               password="${acme.order.password}"   
  13.               contentType="application/vnd.acme+xml" encoding="UTF-8">   
  14.               <transformers>   
  15.                 <transformer ref="OrderMapToMicroformat" />   
  16.               </transformers>   
  17.               <response-transformers>   
  18.                   <message-properties-transformer>   
  19.                         <add-message-property key="OrderPlaced"   
  20.                             value="#[groovy:message.getStringProperty('http.status','ERR')=='201']" />   
  21.                         <add-message-property   
  22.                             key="OrderResourceLocation"   
  23.                             value="#[groovy:message.getStringProperty('Location','')]" />   
  24.                   </message-properties-transformer>   
  25.                   <object-to-string-transformer />   
  26.               </response-transformers>   
  27.          </http:outbound-endpoint>   
  28.          <outbound-endpoint ref="OrderCreationResultQueue" />   
  29.    </chaining-router>       
  30.   </outbound>   
  31. </service>  

这全是XML,我们来仔细分析一下: 

名为OrderCreationQueue的管道(可以是VM队列或是JMS队列)接收消息。 
接收到的消息被直接传递到另一个路由,该路由会将HTTP POST的结果发送到下一个服务,该服务通过名为OrderCreationResultQueue(异步的VM队列)的管道对调用结果进行分析。 
通过标准的输出端点在Groovy转换器上执行该HTTP POST请求: 
请求订单的微格式是通过一个特定的传送器创建的,下一节将对其进行详细介绍。 
通过一小段脚本将结果代码抽取出来并与期望值进行比对。进行比较的目的在于将后面的服务与纯的HTTP隔离开来:我们所创建的boolean类型的属性OrderPlaced是独立的,其名称与进行的编排密切相关。 
类似的,在更具上下文含义的OrderResourceLocation名字下复制Location头。注意,该头 有可能丢失(在失败的情况下),在这种情况下,我们将其值默认设为空字符串以避免将null属性加到消息中。 
我们使用了一个对象——字符串转换器来“分离”HTTP响应(以流的形式返回)。多亏有了这个转换器,流得到了完全的处理,其内容也通过使用与 HTTP交换相关的编码转换为字符串。当流关闭时,HTTP连接得到了释放;我们不想一直开着它,等待后面的服务从 OrderCreationResultQueue中拿出响应消息。 
Groovy MarkupBuilder的好处 

OrderMapToMicroformat转换器完成了服务中的重头戏,而它是由Groovy的MarkupBuilder实现的。MarkupBuilder API提供了一种自然的方式生成兼容于特定微格式的XML实体: 

Java代码  收藏代码
  1. <scripting:transformer name="OrderMapToMicroformat">   
  2.    <scripting:script engine="groovy"> <![CDATA[   
  3.         def writer = new StringWriter()   
  4.         def xml = new groovy.xml.MarkupBuilder(writer)   
  5.         xml.order(xmlns: 'urn:acme:order:3:1') {   
  6.           customerId(payload.clientId)   
  7.           productId(payload.productCode)   
  8.           quantity(payload.quantity)   
  9.         }   
  10.         result = writer.toString() ]]>   
  11.     </scripting:script>   
  12. </scripting:transformer>  

注意map payload中的值是如何用于组装XML元素的:这里解决了一些不匹配的命名约定(比如将clientId转换为customerId)。 

如你所期望的那样,该转换器产生了如下输入: 

Java代码  收藏代码
  1. <order xmlns='urn:acme:order:3:1'>   
  2.    <customerId>123456</customerId>   
  3.    <productId>P987C</productId>   
  4.    <quantity>2</quantity>   
  5. </order>   

除了正确的内容类型外都是订单的RESTful服务用于创建新资源所需的内容。 

分析 

现在我们来看一下负责异步分析订单创建结果并决定是否需要进一步进行编排的服务: 

Java代码  收藏代码
  1. <service name="OrderCreationResultProcessor">   
  2.   <inbound>   
  3.     <inbound-endpoint ref="OrderCreationResultQueue" />   
  4.      <selective-consumer-router>   
  5.         <message-property-filter pattern="OrderPlaced=true" />   
  6.      </selective-consumer-router>   
  7.      <logging-catch-all-strategy />   
  8.   </inbound>   
  9.   <outbound>   
  10.     <pass-through-router>   
  11.       <outbound-endpoint ref="SuccessfulOrderQueue" />   
  12.     </pass-through-router>   
  13.   </outbound>   
  14. </service>   

我们使用的选择性消费者(selective consumer)所接收的消息中一定要包含订单成功处理的头信息。如果该头信息为true,那就通过一个名为SuccessfulOrderQueue 的内存队列将该消息路由给第三个(也是最终的)服务,该服务会处理订单成功创建的消息。注意在这个例子中我们只是简单地将错误消息以日志的方式记录下来, 但在实际应用中需要将错误消息发送给专门的队列以进行后续的分析或是及时的反馈。 

向Mule发送GET请求 
组成该编排的最后一个服务负责HTTP GET处理,它会获得新创建的订单,订单中包含了额外的值以形成一个合法的消息供email网关使用。如下是个示例交互: 

GET /order/O13579 HTTP 1.1 
200 OK 
Content-Type: application/vnd.acme+xml 
... 
Java代码  收藏代码
  1. <order xmlns='urn:acme:order:3:1'>   
  2.   <customerId>123456</customerId>   
  3.   <productId>P987C</productId>   
  4.   <quantity>2</quantity>   
  5.   <customerEmail>foo@bar.baz</customerEmail>   
  6.   <estimatedShipping>2009-31-12T00:00:00Z</estimatedShipping>  
  7. </order>  

好消息是Mule的HTTP传送包含了一个名为rest-servicecomponent的组件,该组件简化了服务与REST资源的交互。幸好有了这样一个组件,我们就无需将GET操作的结果发给随后的服务了,相反可以在单独的服务中完成一切。除此以外,它还支持在配置中使用表达式,这样就能实现与动态构建的URL之间的连通了。 

Java代码  收藏代码
  1. <service name="SuccessfulOrderProcessor">   
  2. <inbound>   
  3.   <inbound-endpoint ref="SuccessfulOrderQueue" />   
  4. </inbound>   
  5. <http:rest-service-component httpMethod="GET"   
  6.    serviceUrl="#[header:OrderResourceLocation]" />   
  7. <outbound>   
  8.   <pass-through-router>   
  9.     <outbound-endpoint ref="EmailGatewayQueue">   
  10.       <transformers>   
  11.        <object-to-string-transformer />   
  12.        <transformer ref="OrderMicroformatToEmailMap" />   
  13.       </transformers>   
  14.     </outbound-endpoint>   
  15.   </pass-through-router>   
  16. </outbound>   
  17. </service>  

在接收到成功的订单消息后,该服务使用REST服务组件生成一个HTTP GET请求,并将该请求发送给此前存储在OrderResourceLocation属性(aka heade)中的URL。注意到我们是如何通过Mule表达式框架(使用#[...]语法)在组件中注入动态URL的。 

在将GET请求的响应发送给负责与email网关进行通信的服务前,我们对XML订单实体进行了两次转换: 

如上所述,我们“分离”了响应实体。 
使用转换器解析XML实体并构建一个映射以供接下来的服务使用。这一次,又利用到了Groovy的强大功能。 
Groovy's XmlSlurper Happiness 

Groovy的XmlSlurper是解析XML微格式的理想工具,这要归功于其类似DSL的API。 

如下代码展示了OrderMicroformatToEmailMap转换器的实现: 

Java代码  收藏代码
  1. <scripting:transformer name="OrderMicroformatToEmailMap">   
  2.   <scripting:script engine="groovy"><![CDATA[   
  3.     def order = new XmlSlurper().parseText(payload)  
  4.                 .declareNamespace(acme: 'urn:acme:order:3:1')   
  5.     result = [emailId:'OrderConfirmation',   
  6.         emailAddress:order.'acme:customerEmail'.text(),   
  7.         orderId:order.@id.text()]   
  8.    ]]>   
  9.   </scripting:script>   
  10. </scripting:transformer>  

没错,就两行Groovy代码,我们使用了一个命名空间感知的XML解析器和一个map构建器。其好处真是让人难以置信,不过这就是创建map(email网关服务所期望的)所需的全部内容。 

最终的REST 
在本文中,我们遵循着一个事先定义好的编排,新订单资源的URL是唯一的动态元素。开发者可以利用示例中介绍的转换器和表达式来支持更多的动态交互,如果你打算遵从HATEOAS路线,你就能获得一切。如果事实如此,那其他的Mule路由就唾手可得了,比如可以创建幂等接收者的 路由。你也看到了Groovy的强大功能和灵巧性加上Mule的通信能力极大地简化了与RESTful服务的交互。Mule的表达式和少量的Groovy 脚本能够有效地动态定义Mule配置。除此之外,通过使用异步的VM队列将编排的各个步骤链接起来可以在最大负荷下优雅地降级,这要归功于Mule内在的SEDA架构。 

借助于这些强大的工具,集成REST资源变得非常简单了。 

祝你好运! 

大家可以从http://dossot.net/datastore/mule-groovy-rest.tar.gz下载完整的配置和相关的测试资源,这是一个独立的Maven项目。 

查看英文原文:Orchestrating RESTful Services With Mule ESB And Groovy。 
http://www.infoq.com/articles/restful-services-mule

转载于:https://www.cnblogs.com/yecllsl/archive/2011/03/15/1985061.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/462732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Starling框架帮助手册中文版(PDF下载)

什么是Statling&#xff1f;Starling 是一个基于Stage3D&#xff08;这是Flash Player11及Adobe AIR 3中新增的为3D加速功能所提供的API&#xff09;所开发的一个能够使用GPU来加速的2D Flash应用程序的ActionScript3框架。Starling主要是为游戏开发而设计的, 但是它的用途不仅…

园区网VLAN应用实例

园区环境&#xff1a;一台核心路由器&#xff0c;一台核心交换机&#xff1b;四台汇聚层交换机&#xff1b;四台接入层交换机&#xff08;可根据实际需求和带宽的大小增加接入层的数量&#xff09;——接入层下接入大量的PC机。技术要求&#xff1a;通过VLAN的划分及VTP的设置实…

GMF 教程 Mindmap 5

2019独角兽企业重金招聘Python工程师标准>>> 这一部分主要实现的是&#xff1a; 使用扩展的Plug-in为我们的Diagram添加一个自定义动作 Creating a Customization Plug-in 尽管可以给生成的代码添加备注generated NOT来避免下次重新生成代码时覆盖我们修改的代码…

Jmeter(四十二)_控制器下遍历一组参数

概述 在接口自动化的过程中&#xff0c;经常遇到需要遍历的参数组。jmeter在中&#xff0c;foreach控制器可以实现遍历参数&#xff0c;但是只能有一个入参。一旦遇到数组&#xff0c;foreach控制器表示我也无能为力。。。 为了解决这个问题&#xff0c;今天教大家一个实现数组…

JS 停留几秒后返回上一页

2019独角兽企业重金招聘Python工程师标准>>> <script type"text/javascript" language"javascript"> var i 5; intervalid setInterval("fun()", 1000); function fun(){if(i0){history.go(-1);clearInterval(intervalid);}i…

spring boot 分布式锁组件 spring-boot-klock-starter

基于redis的分布式锁spring-boot starter组件&#xff0c;使得项目拥有分布式锁能力变得异常简单&#xff0c;支持spring boot&#xff0c;和spirng mvc等spring相关项目 快速开始 spring boot项目接入 1.添加lock starter组件依赖&#xff0c;目前还没上传到公共仓库&#xff…

std::string中的find_first_of()和find_last_of()函数

编程语言: c/linux 在std::string中&#xff0c;有时需要找到一个string中最后一个或者第一个以某个特定的字符开始的位置或者下标&#xff0c;这时就需要使用find_first_of()和find_last_of()函数。 find_first_of() &#xff1a; 找到一个string中第一个以 某个 字符开始的…

【学习笔记4】Action名称的搜索顺序

2019独角兽企业重金招聘Python工程师标准>>> 1&#xff0e;获得请求路径的URI&#xff0c;例如url是&#xff1a;http://server/struts2/path1/path2/path3/test.action 2&#xff0e;首先寻找namespace为/path1/path2/path3的package&#xff0c;如果不存在这个pac…

std::map中的lower_bound与upper_bound

最近在工作中遇到了std::map中的lower_bound与upper_bound&#xff0c;再次记录下其功能和使用方式。 std::map<char, int> mp; mp.lower_bound<key> &#xff1a; 返回的是第一个大于、等于key的iterator&#xff0c;如果没有则返回空。 mp.upper_bound<key…

IoC容器Autofac(3) - 理解Autofac原理,我实现的部分Autofac功能(附源码)

上篇文章中&#xff0c;举了一个Autofac的简单例子&#xff0c;园友eflay反应: “虽然对autofac架构原理还是完全不懂&#xff0c;不过基本是明白ioc的用途了&#xff0c;会从已注册的类中找出符合接口的类来调用构造函数进行构造” 所以这篇文章&#xff0c;还是针对上次的例子…

[置顶] 我的iOS作品

我的iOS作品 罗朝辉 ( http://blog.csdn.net/kesalin)CC 许可&#xff0c;转载请注明出处前言 做了好几年的 iOS 开发了&#xff0c;业余也零零散散地写了不少代码和博文教程。可惜一直都没有整理下&#xff0c;上次过年回家在张江广兰路把笔记本给丢了&#xff0c;损失惨重&am…

SSM框架搭建

SSM&#xff08;SpringSpringMvcMybatis&#xff09;项目环境搭建&#xff1a; 1、项目环境&#xff1a; jdk-1.8 tomcat-9.0 mysql-5.1.44 spring 5.1.6 mybatis 3.5.1 maven 3.5.42、项目目录结构&#xff1a; 3、pom.xml中引入的依赖&#xf…

制作Slider组件

利用as3&#xff0c;我们可以尝试制作一些有趣的组件&#xff0c;虽然现在已经有很多实用的组件&#xff0c;但是自己尝试写一下也是不错的。利用as3语法&#xff0c;借用了绘图Api我们尝试制作一下这个组件。因为我们不需要很强大的功能&#xff0c;对此我们只是需要选取其一部…

Android 编程下的四大组件之服务(Service)

服务&#xff08;Service&#xff09; 是一种在后台运行&#xff0c;没有界面的组件&#xff0c;由其他组件调用开始。Android 中的服务和 Windows 中的服务是类似的东西&#xff0c;它运行于系统中不容易被用户发觉&#xff0c;可以使用它开发如监控之类的程序。 服务&#xf…

第六章实验报告(函数和宏定义实验)

C程序设计实验报告 一、实验项目: 1、编写由三角形三边求面积的函数 2、编写求N阶乘的函数 3、求两个整数的最大公约数 4、打印输出三角形 5、求500以内的所有亲密数对 姓名&#xff1a;廖云福 实验地点&#xff1a;教学楼514教室  实验时间&#xff1a;2019.4.30 一、实验目…

mapreduce shuffle过程问答

通过hadoop权威指南学习hadoop&#xff0c;对shuffle过程一直很疑惑&#xff0c;经过查看网上多个帖子&#xff0c;最终 完成此篇问答总结。 1.什么叫shuffle 从map任务输出到reducer任务输入之间的过程就叫做shuffle 2.每个map任务都有对应的缓存吗&#xff1f;默认是多少&…

S2-016、S2-017

前言 由于S2-016、S2-017出现的原因时相同的&#xff0c;只是由于poc不一样&#xff0c;造成了不同的攻击。S2-016是RCE&#xff0c;S2-017是开发型重定向漏洞。这里将两个漏洞放一起分析。另外“Struts2系列起始篇”是我整各系列的核心&#xff0c;希望大家能花些时间先看看。…

struts 2 配置通配符

2019独角兽企业重金招聘Python工程师标准>>> 随着Web应用程序的增加&#xff0c;所需的Action也会更多&#xff0c;从而导致大量的action映射&#xff0c;使用通配符可以减少action配置的数量&#xff0c;使一些具有类似行为的Action或者Action方法可以使用通用的样…

记一次使用pt-query-digest工具分析MySQL慢查询日志

最近遇到了MySQL性能问题&#xff0c;使用percona 的 pt-query-digest工具分析性能的瓶颈点。并且pt-query-digest工具要优于MySQL本身自带的mysqldumpslow工具。 查看pt-query-digest工具在ubuntu下的安装流程请看&#xff1a;ubuntu下安装pt-query-digest_一缕阳光a的博客-CS…

Entity Framework 4.1 DbContext使用记之三——如何玩转实体的属性值?

之前的两篇有关EF4.1的文章反响不错&#xff0c;感谢大家的支持&#xff01;想体验EF4.1的新功能&#xff1f;RTW版本已经发布啦&#xff0c;http://www.microsoft.com/downloads/en/details.aspx?FamilyIDb41c728e-9b4f-4331-a1a8-537d16c6acdf&displaylangen Entity …