EIP '贷款经纪人' 参考实现 (第 1 部分)

工程 | Oleg Zhurakousky | 2010 年 3 月 19 日 | ...

我们很高兴地宣布“贷款经纪人”参考实现的第一部分。 '贷款经纪人' 概念已成为展示 企业集成模式 (EIP) 的事实上的参考领域 - 由 Gregor Hohpe 和 Bobby Woolf 撰写,而此“贷款经纪人”RI 的部分展示了如何使用 Spring Integration (SI) 框架实现和应用企业集成模式

介绍

lb-pipesFilters

EIP 架构的核心是 管道和过滤器消息 这两个非常简单但功能强大的概念。端点(过滤器)通过通道(管道)相互连接。生产端点将消息发送到通道,消息由消费端点检索。此架构旨在定义描述如何在端点之间交换信息的不同机制,而无需了解这些端点是什么或它们交换什么信息,从而提供了一种非常松散耦合且灵活的协作模型,同时还将集成问题业务问题解耦。EIP 通过进一步定义扩展了此架构

  • 管道的类型(点对点通道、发布-订阅通道、通道适配器等)
  • 核心过滤器以及围绕过滤器如何与管道协作的模式(消息路由器、拆分器和聚合器、各种消息转换模式等)
Spring Integration (SI) 消息传递框架旨在提供基于 POJO 的编程模型,该模型构建在企业集成模式之上。

用例

此用例的详细信息和变体在 EIP 书籍的第 9 章 中进行了很好的描述,但以下是简要摘要;消费者在购物寻找最佳贷款报价时,会订阅贷款经纪人的服务,贷款经纪人负责处理诸如
  1. 消费者预筛选(例如,获取和审查消费者的信用记录)
  2. 确定最合适的银行(例如,根据消费者的信用记录/评分)
  3. 向每家选定的银行发送贷款报价请求
  4. 收集每家银行的回复
  5. 根据消费者的要求筛选回复并确定最佳报价。
  6. 将贷款报价反馈给消费者。
显然,获得贷款报价的实际过程要复杂一些,但由于我们的目标是演示如何在 SI 中实现和实施企业集成模式,因此用例已简化,仅专注于过程的集成方面。这不是试图为您提供消费金融方面的建议。 loan-broker-1 如您所见,通过聘用贷款经纪人,消费者与贷款经纪人运营的细节隔离开来,并且每个贷款经纪人的运营可能彼此不同以保持竞争优势,因此我们组装/实施的任何内容都必须灵活,以便可以快速轻松地引入任何更改。说到变化,此“贷款经纪人”RI 的第一部分实际上并没有与任何“虚拟”银行或信用机构进行通信。这些服务被存根化了。我们在此处的目标是将整个过程的集成方面进行组装、编排和测试。只有这样,我们才能开始考虑将此类过程连接到实际服务。我们将在第 2 部分中执行此操作,您将在其中了解将集成业务问题分开的真正好处。您将看到,描述此过程的过程和配置不会改变,无论特定贷款经纪人处理多少家银行,或用于与这些银行通信的通信媒体(或协议)类型(JMS、WS、TCP 等)如何。

设计

当您分析以上 6 个需求时,您会很快发现它们都属于集成问题的范畴。例如,在消费者预筛选步骤中,我们需要收集有关消费者及其愿望的更多信息,并丰富贷款请求,使其包含其他元信息。然后,我们必须过滤此类信息以选择最合适的银行列表,依此类推。丰富、过滤、选择 - 这些都是EIP 以模式形式定义解决方案的集成问题。SI 提供了这些模式的实现。

提交贷款报价请求 - 消息网关

lb-gateway

消息网关模式提供了一种简单的机制来访问消息传递系统,包括我们的贷款经纪人。在 SI 中,您将网关定义为普通的 Java 接口(无需提供实现),通过 XML <gateway> 元素或通过注释进行配置,并将其用作任何其他 Spring Bean。SI 将负责通过生成消息(有效负载映射到方法的输入参数)并将其发送到指定的通道来委托和映射方法调用到消息传递基础设施。

LoanBrokerGateway.java 是消费者使用的消息网关的接口

网关 XML 配置

<int:gateway id="loanBrokerGateway"
		     default-request-channel="loanBrokerPreProcessingChannel"
		     service-interface="org.springframework.integration.loanbroker.LoanBrokerGateway"/>

在上述配置中,每当在“loanBrokerGateway”bean 上调用任何方法时,都会构建一条消息并将其发送到“loanBrokerPreProcessingChannel”。

我们对消息网关的定义(LoanBrokerGateway.java)为消费者提供了两种与贷款经纪人交互的方式。消费者可以通过调用getLoanQuote(loanRequest) 方法请求单个(最佳)报价,或通过调用getAllLoanQuotes(loanRequest) 方法请求所有报价。这意味着我们的贷款经纪人必须知道贷款请求的类型。我们还知道有一些预筛选步骤,例如获取和评估消费者的信用评分,因为某些顶级银行通常只会接受符合最低信用评分要求的消费者的报价请求。

从本质上讲,整个过程类似于看医生,在看真正的医生之前,你会先遇到护士,护士会测量你的体温、血压等,并写下医生需要的“元信息”列表。
在 EIP 中,消息是一个简单的结构,它由消息有效负载消息标头组成。消息标头是一种存储与消息相关的元信息的绝佳机制。那么我们如何用其他信息丰富我们的消息呢?EIP 定义了 内容丰富器 模式,该模式描述了如何使用其他信息增强消息。Spring Integration 提供了一个<header-enricher> 元素,允许您快速丰富传输中的消息。但是,由于我们的贷款经纪人必须在发送报价之前执行多个任务,因此如果有一种机制可以从一组独立的任务中组合一个过程,那将是很好的。

贷款请求预筛选 – 组合消息处理器内容丰富器

组合消息处理器模式描述了构建端点的规则,这些端点维护对由多个消息处理器组成的消息流的控制。在我们的案例中,预筛选流包括 3 个步骤:a) 确定贷款请求的类型; b) 获取消费者的信用记录和评分; c) 确定(根据某些条件)通道列表(每个通道对应一家独立的银行)

Spring Integration 允许您通过<chain> 元素组合复杂的处理器

<int:chain id="preScreening" input-channel="loanBrokerPreProcessingChannel" output-channel="banksDistributionChannel">
	<int:header-enricher>
		<int:header name="RESPONSE_TYPE"
			expression="headers.history.iterator().next().attributes['method'].equals('getLoanQuote') ? 'BEST' : 'ALL'" />
	</int:header-enricher>
	<int:header-enricher>
		<int:header name="CREDIT_SCORE" ref="creditBureau" method="getCreditScore"/>
	</int:header-enricher>
	<int:header-enricher>
		<int:header name="BANKS" ref="bankSelector" method="selectBankChannels"/>
	</int:header-enricher>
</int:chain>
lb-chain

这将创建一个名为“preScreening”的 bean 作为 SI 端点,该端点还定义了一个输入/输出通道以接收和发送消息。上述由 3 个header-enricher处理器组成。第一个处理器将通过利用 SpEL 访问 消息历史记录 并根据调用的网关方法确定此标头的值来设置RESPONSE_TYPE标头。

此示例说明了如何使用 SpEL 执行简单的评估以确定标头值,但我们不建议使用 SpEL 执行复杂的业务逻辑
接下来,我们有一个header-enricher,它映射到一个负责从信用局(当前存根 CreditBureauStub)获取信用评分并设置CREDIT_SCORE标头的过程。最后一个header-enricher使用 BankChannelSelector(参见 bankSelector 配置)。BankChannelSelector.selectBankChannels(..) 方法的实现将消息作为输入,并返回一个包含通道名称的 Set<String> 值,该值将设置为BANKS标头。这完成了我们的预筛选过程,我们的贷款经纪人现在已准备好通过banksDistributionChannel向每家选定的银行发送贷款请求。

BANKS标头定义了动态生成和过滤的通道列表,每个通道充当代表银行的接收者。我们需要一个端点,允许我们将相同的消息发送到所有接收者。

将贷款报价请求分发给选定的银行 - 收件人列表

lb-recipientList EIP 定义了各种类型的路由模式,它们都派生自 消息路由器。其中之一是 收件人列表 路由器,它将消息路由到列表中的所有收件人。SI 提供了一个 <router> 元素,允许我们配置一个路由器,在本例中,它将从 bankDistributionChannel 接收 消息。此路由器将通过 SpEL 表达式从 BANKS 消息头 获取银行渠道列表(此列表在预筛选步骤中设置),并将 消息 分发到这些 渠道

下面的 XML 配置显示了此路由器的配置方式

<int:router id="bankRecipientListRouter" input-channel="banksDistributionChannel" 
								expression="headers['BANKS']" 
								apply-sequence="true"/>
您可以清楚地看到我们是如何通过通道(管道)连接各种端点(过滤器)来组装贷款经纪人的,同时传递消息。您还可以看到,我们通过基于 POJO 的编程技术实现了这一点,几乎没有使用 Spring Integration API(仅 BankChannelSelector.java)。SI 负责将我们的 POJO 与消息基础设施连接。
贷款经纪人需要做的最后一件事是从银行接收贷款报价,按消费者汇总(我们不想向另一个消费者显示一个消费者的报价),根据消费者的选择标准(最佳报价或所有报价)组装响应,并回复给消费者。

汇总贷款报价响应 - 聚合器

聚合器模式描述了一个端点,该端点将相关的 消息 分组到单个 消息 中。可以提供标准和规则来确定 聚合关联 策略。SI 提供了聚合器模式的几种实现以及方便的基于名称空间的配置。

lb-complete

我们的贷款经纪人通过 <aggregator> 元素定义了一个 'loanQuoteAggregator' bean,它提供了一个默认的 聚合器关联 策略。默认的关联策略根据 $corelationId 头关联消息(请参阅 关联标识符 模式)。有趣的是,我们从未为该标头提供值。它是在之前由收件人列表路由器自动设置的,当时它为每个银行生成了一个单独的消息。

一旦消息相关联,它们就会被释放到实际的聚合器实现中。虽然 SI 提供了默认的聚合器,但其策略(收集所有消息的有效负载列表并使用此列表作为有效负载构造新消息)不满足我们的要求。原因是我们的消费者可能需要单个最佳报价或所有报价。为了传达消费者的请求,在流程的早期,我们设置了 RESPONSE_TYPE 标头。现在我们必须评估此标头并返回 所有报价(默认聚合策略将起作用)或 最佳报价(默认聚合策略将不起作用,因为我们必须确定哪个贷款报价是最好的)。

<int:aggregator id="loanQuoteAggregator" input-channel="quotesAggregationChannel" method="aggregateQuotes">
	<bean class="org.springframework.integration.loanbroker.LoanQuoteAggregator"/>
</int:aggregator>

显然,选择最佳报价可以基于复杂的标准,并且会影响聚合器的复杂性,但目前我们将其简化。如果消费者想要最佳报价,我们将选择利率最低的报价。为了实现这一点,LoanQuoteAggregator.java 将对所有报价进行排序并返回第一个报价。LoanQuote.java 实现 Comparable,它根据 rate 属性比较报价。

创建响应消息后,它将发送到启动该流程的消息网关(因此是消费者)的 default-reply-channel。我们的消费者获得了贷款报价!

需要注意的一件重要事情是,我们没有在 <gateway> 元素上定义 default-reply-channel 属性。事实上,我们没有明确定义单个 channel。与其他消息系统类似,SI 会根据需要自动创建 输入 和默认的 回复 通道,从而为您提供另一种进一步简化 Spring 应用程序上下文配置的方法。

结论

“贷款经纪人”用例的此参考实现是使用 Spring Integration 框架完成的;一个基于 POJO 的轻量级、可嵌入的消息框架,具有松耦合的编程模型,旨在简化异构系统的集成,而无需重量级的 ESB 类引擎或专有的开发和部署环境。它构建在 企业集成模式 之上。“模式旨在描述您解决方案的‘构建块’——它们并非旨在成为解决方案本身。因此,模式本身最适合由轻量级、可嵌入的框架来实现,这些框架旨在支持您的解决方案,而不是旨在控制它的重量级、商业现成产品。这就是大型供应商在 SOA 中出错的地方……” - Tom McCuch(SpringSource)评论 InfoQ 文章 Joshua Long。集成问题存在于所有类型的应用程序(基于服务器和非基于服务器)中,如果此类应用程序需要相互集成,则不应需要更改设计、测试和部署策略。我(开发人员)不应将我的 SWT 或基于控制台的应用程序移植到 ESB 类服务器或实现专有接口,仅仅因为我有一个集成问题。我只是希望有一个框架,让我在需要时以最少或无需更改我的代码或基础设施的情况下解决这些问题。Spring Integration 就是那个框架。

在下一期中,我们将演示 Spring Integration 中可用的各种远程适配器和技术,通过使用这些适配器替换我们的存根服务,并将介绍与“贷款经纪人”用例相关的异步集成风格。

资源:

“贷款经纪人”参考实现随 Spring Integration 2.0.M3 的发布一起提供(请参阅下载部分)。它作为独立的 Eclipse/Maven 项目分发。您也可以从我们的 Subversion 存储库中检出它作为项目。

$> svn co https://src.springframework.org/svn/spring-integration/trunk/spring-integration-samples/loan-broker/ loan-broker $> cd loan-broker $> mvn install

相关链接Spring 集成 Spring 集成实战 企业集成模式 Spring 集成入门(Joshua Long) 敏捷 SOA - 第 1、2 和 3 部分(Tom McCuch)

感谢 Gary Russel (Spring Source,SI 贡献者) 和 Dave Turanski (Spring Source) 对此博客的帮助!

获取 Spring 时事通讯

与 Spring 时事通讯保持联系

订阅

领先一步

VMware 提供培训和认证,以加速您的进步。

了解更多

获取支持

Tanzu Spring 在一个简单的订阅中提供 OpenJDK™、Spring 和 Apache Tomcat® 的支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部