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

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

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

引言

lb-pipesFilters

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

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

用例

此用例的详细信息和变体在 EIP 书籍的第9章 中有非常好的描述,这里是简要总结;消费者在寻找最佳贷款报价时,会订阅贷款经纪人的服务,贷款经纪人处理以下细节:
  1. 消费者预筛查(例如,获取并审查消费者的信用记录)
  2. 确定最合适的银行(例如,基于消费者的信用历史/评分)
  3. 向每个选定的银行发送贷款报价请求
  4. 收集每个银行的回复
  5. 过滤回复并根据消费者的要求确定最佳报价
  6. 将贷款报价传回给消费者。
显然,获得贷款报价的实际过程要复杂一些,但由于我们的目标是展示企业集成模式如何在 SI 中实现和应用,因此用例已简化,仅关注流程的集成方面。这并不是试图为您提供消费者金融方面的建议。loan-broker-1 如您所见,通过聘请贷款经纪人,消费者与贷款经纪人操作的细节隔离,并且每个贷款经纪人的操作可能会有所不同以保持竞争优势,因此我们组装/实现的任何内容都必须是灵活的,以便能够快速、无痛地引入任何更改。说到更改,本次贷款经纪人参考实现的第一个部分实际上并没有与任何“假想的”银行或信用局交谈。这些服务都被模拟(stubbed out)了。我们的目标是组装、协调和测试整个流程的集成方面。只有这样,我们才能开始考虑将此流程连接到实际服务。我们将在第二部分中实现这一点,届时您将体会到分离集成业务关注点的真正好处。您将看到,描述此流程的流程和配置不会改变,无论特定的贷款经纪人与多少家银行打交道,或用于与这些银行通信的通信媒介(或协议)类型(JMS、WS、TCP 等)。

设计

当您分析上面的6个需求时,您会很快发现它们都属于集成关注点。例如,在消费者预筛查步骤中,我们需要收集关于消费者和消费者意愿的额外信息,并用额外的元信息丰富贷款请求。然后,我们必须过滤这些信息以选择最合适的银行列表,依此类推。丰富、过滤、选择——这些都是集成关注点,EIP 以模式的形式为其定义了解决方案。SI 提供了这些模式的实现。

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

lb-gateway

消息网关模式提供了一种访问消息系统的简单机制,包括我们的贷款经纪人。在 SI 中,您将网关定义为一个普通的 Java 接口 (POJO),无需提供实现,通过 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,该元素提供了一个默认的 aggregatorcorrelation 策略。默认的关联策略基于 $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 会根据需要自动创建 input 和默认的 reply 通道,这为您提供了另一种进一步简化 Spring Application Context 配置的方式。

结论

此 '贷款经纪人' 用例的参考实现是使用 Spring Integration 框架完成的;一个基于 POJO 的、轻量级的、可嵌入的消息框架,具有松散耦合的编程模型,旨在简化异构系统的集成,而无需重量级的 ESB 类引擎或专有开发和部署环境。它是构建在 企业集成模式 之上的。"模式旨在描述您解决方案的'构建块'——它们本身并非解决方案。因此,模式最适合由轻量级、可嵌入的框架实现,这些框架服务于您的解决方案,而不是旨在控制它的重量级、商业现成产品。这正是大型供应商在 SOA 方面都走错了路的地方..." - Tom McCuch (Spring Source) 在 Joshua Long 的 InfoQ 文章中评论道。集成关注点存在于所有类型的应用中(基于服务器的或非基于服务器的),并且如果这些应用需要彼此集成,则不应要求改变设计、测试和部署策略。我(开发人员)不应该仅仅因为存在集成关注点,就将我的 SWT 或控制台应用移植到 ESB 类服务器或实现专有接口。我只是希望有一个框架,允许我在需要时解决这些关注点,并且对我的代码或基础设施进行最少甚至不进行更改。Spring Integration 就是那个框架。

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

资源:

'Loan Broker' 参考实现随 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 Integration Spring Integration in Action 企业集成模式 Spring Integration 入门 (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 社区所有即将举办的活动。

查看全部