HarmonyOS开发者限时福利来啦!最高10w+现金激励等你拿~ 了解详情
写点什么

使用 Mule ESB 与 Groovy 编排 RESTful 服务

  • 2009-11-19
  • 本文字数:4938 字

    阅读完需:约 16 分钟

在过去几年中,REST 风格的软件架构获得了越来越多的认可,这主要是因为它减少了系统对动件的需求、同时使系统耦合性更低,弹性更好。

目前越来越多的 REST 资源出现在企业应用中,因此对这些资源进行编排就显得非常重要了。比方说,典型的业务活动就包含了资源的创建,然后是资源的查找及其他资源的创建等。

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

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

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

  1. 通过向服务发送一个 XML 实体来创建一个新订单。
  2. 寻找新创建的订单资源并从中提取出确认信息。
  3. 根据确认信息并通过邮件网关向客户发送一条确认消息。

我们将在本文详细分析上面每一步的交互过程,同时还会介绍为了获得上述交互所需的 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
...
<order xmlns='urn:acme:order:3:1'>
<customerId>123456</customerId>
<productId>P987C</productId>
<quantity>2</quantity>
</order>

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

复制代码
201 Created
Location: http://acme.com/order/O13579
...
<order id='O13579' />

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

复制代码
<service name="OrderCreationService">
<inbound>
<inbound-endpoint ref="OrderCreationQueue" />
</inbound>
<outbound>
<chaining-router>
<http:outbound-endpoint synchronous="true"
responseTimeout="15" method="POST"
host="${acme.order.hostname}"
port="${acme.order.port}" path="orders"
user="${acme.order.username}"
password="${acme.order.password}"
contentType="application/vnd.acme+xml" encoding="UTF-8">
<transformers>
<transformer ref="OrderMapToMicroformat" />
</transformers>
<response-transformers>
<message-properties-transformer>
<add-message-property key="OrderPlaced"
value="#[groovy:message.getStringProperty('http.status','ERR')=='201']" />
<add-message-property
key="OrderResourceLocation"
value="#[groovy:message.getStringProperty('Location','')]" />
</message-properties-transformer>
<object-to-string-transformer />
</response-transformers>
</http:outbound-endpoint>
<outbound-endpoint ref="OrderCreationResultQueue" />
</chaining-router>
</outbound>
</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 实体:

复制代码
<scripting:transformer name="OrderMapToMicroformat">
<scripting:script engine="groovy"> <![CDATA[
def writer = new StringWriter()
def xml = new groovy.xml.MarkupBuilder(writer)
xml.order(xmlns: 'urn:acme:order:3:1') {
customerId(payload.clientId)
productId(payload.productCode)
quantity(payload.quantity)
}
result = writer.toString() ]]>
</scripting:script>
</scripting:transformer>

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

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

复制代码
<order xmlns='urn:acme:order:3:1'>
<customerId>123456</customerId>
<productId>P987C</productId>
<quantity>2</quantity>
</order>

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

分析

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

复制代码
<service name="OrderCreationResultProcessor">
<inbound>
<inbound-endpoint ref="OrderCreationResultQueue" />
<selective-consumer-router>
<message-property-filter pattern="OrderPlaced=true" />
</selective-consumer-router>
<logging-catch-all-strategy />
</inbound>
<outbound>
<pass-through-router>
<outbound-endpoint ref="SuccessfulOrderQueue" />
</pass-through-router>
</outbound>
</service> 

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

向 Mule 发送 GET 请求

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

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

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

复制代码
<service name="SuccessfulOrderProcessor">
<inbound>
<inbound-endpoint ref="SuccessfulOrderQueue" />
</inbound>
<http:rest-service-component httpMethod="GET"
serviceUrl="#[header:OrderResourceLocation]" />
<outbound>
<pass-through-router>
<outbound-endpoint ref="EmailGatewayQueue">
<transformers>
<object-to-string-transformer />
<transformer ref="OrderMicroformatToEmailMap" />
</transformers>
</outbound-endpoint>
</pass-through-router>
</outbound>
</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 转换器的实现:

复制代码
<scripting:transformer name="OrderMicroformatToEmailMap">
<scripting:script engine="groovy"><![CDATA[
def order = new XmlSlurper().parseText(payload)
.declareNamespace(acme: 'urn:acme:order:3:1')
result = [emailId:'OrderConfirmation',
emailAddress:order.'acme:customerEmail'.text(),
orderId:order.@id.text()]
]]>
</scripting:script>
</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


给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2009-11-19 21:384624
用户头像

发布了 88 篇内容, 共 262.4 次阅读, 收获喜欢 8 次。

关注

评论

发布
暂无评论
发现更多内容

哪路神仙写的421页MySQL高级笔记,涵盖MySQL所有技术!太香了

爱好编程进阶

Java 面试 后端开发

如何在面试中机智的展现架构能力?

非凸科技

rust 编程语言 量化 构架师 互联网大厂

观测云登陆阿里云计算巢,共建ISV新生态

观测云

可观测性 可观测

LAXCUS分布式操作系统冗余容错之节点篇

LAXCUS分布式操作系统

分布式系统 冗余 集群容灾

Oceanbase 和 TiDB 粗浅对比之 - 执行计划

TiDB 社区干货传送门

别找了,这是迄今为止把微服务讲的最清楚的一篇!没有之一

爱好编程进阶

Java 面试 后端开发

华为18级大牛整理总结:微服务设计和分布式服务框架原理实践文档

爱好编程进阶

Java 面试 后端开发

单例模式你不得不知道的底层原理

爱好编程进阶

Java 面试 后端开发

大量示例彻底搞懂Linux查找,which,whereis

爱好编程进阶

Java 面试 后端开发

【高并发】为何在32位多核CPU上执行long型变量的写操作会出现诡异的Bug问题?看完这篇我懂了!

冰河

并发编程 多线程 协程 异步编程 精通高并发系列

18张图,详解SpringBoot解析yml全流程

码农参上

springboot 配置文件 4月月更

大数据基础处理框架

爱好编程进阶

Java 面试 后端开发

字节奋战7年,回头一看只剩下这份1857页的算法笔记了

爱好编程进阶

Java 面试 后端开发

不同研发协作模式在云效中的应用

阿里云云效

云计算 阿里云 云原生 研发 研发协作

CDF全球调查:软件交付性能停滞不前

SoFlu软件机器人

历经4轮2小时,终于斩下美团offer!

爱好编程进阶

Java 面试 后端开发

未来的手机操作系统在智能化上会有哪些突破

InfoQ IT百科

如何优化前端页面的LCP?

BUG侦探

前端 性能 网页指标

工作总结!日志打印的15个建议

爱好编程进阶

Java 面试 后端开发

终于有人讲明白了!原来这才是全球低时延一张网技术

华为云开发者联盟

音视频 华为云 实时音视频 低时延

为什么switch里的case没有break不行

爱好编程进阶

Java 面试 后端开发

你必须懂也可以懂的微服务系列三:服务调用

爱好编程进阶

Java 面试 后端开发

大爆料!Github上100%好评的Java多线程池面试题

爱好编程进阶

Java 面试 后端开发

netty系列之:netty中常用的字符串编码解码器

程序那些事

Java Netty 程序那些事 4月月更

诚邀报名丨首期OpenHarmony开发者成长计划分享日

OpenHarmony开发者

OpenHarmony

你知道Java是如何解决可见性和有序性问题的吗?

爱好编程进阶

Java 面试 后端开发

刚拿的字节跳动offer“打水漂”

爱好编程进阶

Java 面试 后端开发

图文并茂 教你在IDEA中如何一键生成代码,提高开发效率!

爱好编程进阶

Java 面试 后端开发

洞见科技首批通过央行国家金融科技测评中心「联邦学习」产品评测,实现「MPC+FL」金融应用双认证

洞见科技

联邦学习 隐私计算 多方安全计算

为拿几家大厂Offer,“闭关修炼

爱好编程进阶

Java 面试 后端开发

移动平台WorkPlus集成化办公,打造企业全场景业务生态

WorkPlus

使用Mule ESB与Groovy编排RESTful服务_Java_David Dossot_InfoQ精选文章