写点什么

ESB 架构之企业实施案例

2011 年 4 月 06 日

本文讲述了 ESB 架构在企业内的实际运用,包括在部门内、部门间以及企业级 ESB 架构的设计和案例;分享了 ESB 设计过程需要考虑的关键问题;描述了不同 ESB 域的实施重心。

概述

ESB 的存在主要是为了整合企业内部的应用,使企业内的应用能融为一体,而不是成为一个个信息孤岛。可以说 ESB 是企业内所有服务的中心点,其它系统间的交互都需要通过 ESB 来完成。为此,它需拥有如下质量属性:可用性、性能、可修改性、可测试性、易用性。参考“ESB 的质量属性”一节。

为了解释这些架构属性,我们可以从企业域、部门域、ESB 内部视角三个层次来进行说明。ESB 除了高可用性和性能之外,高可伸缩性也很重要,在实际实施过程中,读者可以对整个结构进行裁减,在开始时,可能只需要一个部门域,部门域内支持水平扩展。当达到瓶颈之后,则可能需要部署到多个部门域,这样就可以扩展出多个水平扩展的节点,减少单个节点的职责。

ESB 的质量属性

可用性

ESB 是企业内应用之间及对外第三方系统之间交互的集中点,它集中管理了交互的所有服务。它还提供服务查找、管理、审计、监控、分析等功能。当 ESB 服务出现故障,就将会影响企业内所有应用的正常运行。所以,可用被性放在了第一位。

性能

随着企业内部整合的推进,ESB 内部的服务交易量应该不会少,高性能对于 ESB 来说也是非常重要的。

可修改性

因为 SOA 的企业治理是一个循序渐进的过程,在 ESB 部署之初,很难准确估计未来的交易量,所以,对性能的扩展性要求也比较高。

在实际的生产运维过程中,我们还是会常常发现,服务可能会出现这样或那样的问题。为了不影响服务消费者对服务的正常使用,快速的修改和部署,是一个很重要的问题。

ESB 项目是随着 SOA 治理的发展而一次次迭代的,这也就要求了很高的可修改性。

可测试性

ESB 上线既然是一个迭代的过程,服务会根据 SOA 理念的深入而增多。在迭代过程中,要保证以前的服务能顺利通过测试,可测试性是一个很重要的保障。

企业内的应用应该只需面向 ESB,它们交互时并不需要知道这个服务位于哪里或是谁在使用该服务。这时,ESB 测试就是一个很大的问题,因为当一笔交易开始的时候,你可能并不知道它会在哪里,但我们却要保证这笔交易是正确的,这样才能保持服务的正确性。

易用性

实现易用性需要提高服务的开发效率,即能快速开发和部署服务。因为它对生产上的活动没有影响,所以将它放在末尾。

企业域视图

在大多数据情况下,如果交易量不大,你大可以只使用一个部门域来支撑整个企业内的服务。但如果只是一个 ESB 的部门域的话,是没有办法支撑后来交易量的逐年增长的。虽然每一个部门域都可以自行进行水平的扩展,但这还是有一个度的,超过这个度后,水平扩展的难度就会提高,此时可能需要在业务上进行垂直拆分,这种方式当然没有水平扩展来得廉价,但它能更容易的支撑更大的业务量。

在企业域中,最大的特点就是有多个部门子域(图 2.1),每个部门子域都是高度自治的。它们可以独立地处理域内各个系统的整合,只有当需要使用其他域中的服务时,才会请求其它的域。为了防止部门域之间变成一个蜘蛛网,这里我们引入了企业域管理器,来统一管理域内的服务与及对这些部门域进行必要的监控。

在企业域管理器中主要有以下的几个组件:

  • 企业服务查找注册组件:这个组件一般情况下是独立部署的,而且应该有很高的可用性,在理想状态下,应该可以查找到所有部门域中的所有交易。跨域的交易都需要通过这个组件来查找到对应域的服务。
  • 监控组件:这个组件可以查看各个部门域的运行情况。

图 2.1

元素

企业域管理器

企业服务查找注册组件

这个是企业域管理器的核心组件,使用它来管理企业内的所有服务,这个组件应该有以下几个功能。

  • 服务注册:注册服务地址、服务描述及服务规约。
  • 服务版本管理:管理服务的多个版本。
  • 服务客户端代码的生成:根据服务地址及说明生成服务客户端,在我们的实施中,一般为 java 版本。
  • 服务路由表的查找:主要作用是查找对应的服务地址,而且可以推送给服务路由器。
  • 服务的使用方注册:要调用服务,就必须到注册组件中进行注册,没有注册的使用方不允许进行服务的调用。这样就可以通过此组件找到此服务的使用路径,从而当服务进行更改后,可以有效的通知相应的服务使用方。
监控组件

这个组件可以查看各个部门域内的运行情况,并在部门域的运行超过阀值时发出预警。必要时,操作域内流控组件。具体的功能如下:

  • 查看各个部门域的运行情况。如硬件资源、交易信息、流控信息和配置信息。
  • 对资源使用情况进行预警。
  • 根据情况操作部门域内的配置参数,比如流控的配置参数。
  • 操作域内的流控组件,保证重要交易,放弃次要交易。
  • 定时收集各个域内的信息。信息保存之后,为报表、决策分析等提供信息支持。

部门域

部门域是企业内的一个个 ESB 节点,每个部门域内会根据项目群,或者根据部门来进行划分,在各个部门域内都有一个 ESB 组件,通过这个 ESB 来整合部门内的服务及应用。这个组件我们将会在部门域的视角中详细描述。

场景

子域间交互

所有服务都会注册到企业管理器的服务查找组件中,这个组件拥这些服务的描述及地址信息。图 2.2 描述了一个流程示例,部门域 A 如果要发起一个跨域的服务请求,就必须要使用企业域管理器的服务查找组件,通过这个组件的路由表获取此服务提供者所在的部门域 B 的服务地址后,才能请求对应的服务。

为了提高性能,在这个场景里,也可以在启动的时就获取所有路由信息,并缓存起来,服务请求时通过缓存来找到部门域 B 的地址。在这里有一点需要注意,当部门域改变了服务地址之后的通知机制,我们的实施中有下以几种策略:

  • 服务查找组件进行推送
  • 如果服务请求地址出错,重新请求服务查找组件
  • 定时清空路由缓存

图 2.2

部门域视图

部门域是企业 ESB 实施的基本组成单元,在一定交易量范围内,它甚至可以独立存在于企业内。部门域 ESB 可以独立地进行水平扩展,以进行性能的伸缩,而且,这种性能的伸缩在一定程度上应该是相对廉价的。

在部门域的视角,我们不用关心 ESB 的内部实现,在一般情况下,只有以下四个场景

  • 同步请求服务
  • 异步请求服务
  • 同步提供服务
  • 异步提供服务

同一域内的系统只需要知道以上四种场景就足够了,其它工作会在 ESB 内部进行整合。比如,与某个遗留系统的交易,ESB 会通过适配器与之整合,我们会在 ESB 内部视图进行阐述这一内容。

部门域内主要涉及多种应用系统和 ESB 两种元素(图 3.1),所有应用系统之间的交互都要经过 ESB,它们是星型拓扑结构,所以,ESB 成了一个单点故障点,出了问题会影响到整个部门域的各个子系统,这也是为什么在 ESB 的系统中可用性的质量属性如此重要的原因。

图 3.1

元素

ESB 组件

ESB 组件是核心,这个元素内部的功能将在 ESB 内部视图中详细阐述。它位于部门域内,其主要作用是:

  • 减少各应用间的依赖:ESB 最大的好处是可以把蜘蛛网结构的依赖关系理顺,使各个应用只依赖于 ESB。
  • 整合现有应用:ESB 可以通过自身的一些技术组件,对现有的应用进行协议转换,让现有的应用能快速融入到企业整合的大环境中,不至于形成一个个的信息孤岛。
  • 流控:保证高优先级服务的高可用性。

域内应用系统

域内应用系统是企业内部信息 / 服务的实际提供者和消费者,当它需要为其它消费者提供信息 / 服务,或者要消费其它系统的信息 / 服务时,就会和 ESB 产生关系。

企业外系统

当今企业大多会与企业外部系统产生关系。我们不应在应用系统内部直接和外部的系统产生关系,这样会耗费更多的时间在安全管理上,而且很多时候这些外系统并不是只有一个应用在使用。此时,不但会增加了单个应用系统的复杂度,而且还会出现一些冗余。我们完全可以通过 ESB 来统一完成这些工作,简化应用系统消费服务的过程。

BPM

BPM 系统实际上是应用系统的一部分,把 BPM 独立出来进行管理,是因为 BPM 在 ESB 架构中占有比较大的成分。在 ESB 实际实施过程中,我们可以使用 ESB 内部的各种路由和端点的组合实现一定程度上的的 BPM 功能,但这样实际上会复杂化 ESB。如果能使用 BPM 产品来做这个交易的流程编排,就能减化 ESB 内部的复杂性。

如果应用系统中没有 BPM 这类的应用系统,如果可能的话,我们最好能使在企业域中加入一个 BPM 组件来实现业务流程。此时,ESB 需要能很好地与 BPM 应用系统进行交互。

场景

在以下场景中,一次请求实际上会通过两个或更多的组件,之所以会这样,是因为 ESB 会屏蔽 ESB 的请求方和服务方的细节,当系统要与外部系统进行交互时,只应知道 ESB 这个系统。这也是为什么可测试性在 ESB 系统很重要的原因。在 ESB 系统中,整合测试是非常重要的。

在同异步服务提供的场景下,因为交易的请求方只知道 ESB,或者有时候根本没有请求方,在这种场景下的测试就显得非常重要了,ESB 不但要满足技术上的测试功能,还要和服务方一起完成业务测试,这样才能保证这笔交易的正确性。

同步请求

下图是一个最简单的应用场景(图 3.2),在这个场景中 ESB 只做交易转发,当然中间也可能做了报文转换的工作。这一切应该在部门级视角上看应该透明的,在部门域内服务的使用者只需要依赖 ESB 提供的服务接口,而不需要依赖服务的最终实现。此处的细节会在 ESB 的内部视角中进行阐述。

图 3.2

异步请求

如果服务不能在短时间能完成操作,就不应该使用同步请求,而应该使用异步请求。当该服务完成操作后,再回调一个方法来获取处理结果,当然,也可能不需要返回结果。

图 3.3

同步提供服务

图 3.4

异步提供服务

图 3.5

ESB 内部视图

静态看 ESB 系统,它主要由三部分组成(图 4.1)

  • 端点(Endpoint):它的职责可分为两部分,一部分是接收服务请求,另一部分是调用服务提供者
  • 路由器(Router):主要是消息的路由。当端点接收到一个请求后,会交由路由器来选择相应的消息服务方。
  • 基础组件:支持通用 ESB 模式的通用组件。

图 4.1

从动态地看 ESB 系统,你会发现,我们可以将 ESB 内部看作一个个有组织的消息通道(图 4.2),客户端请求 ESB 时选择一个相应的消息通道。在这个消息通道中,会有很多的消息处理器,它们根据处理器自己的职责对消息流进行相应的处理。

图 4.2

元素

消息处理通道

消息处理通道是 ESB 架构的核心部分,ESB 核心的消息处理器分为两部分,一部分是路由处理器,一部分是端点处理器。当然,我们的基础组件也会适时地在两个处理器中间,拦截加入多个基础组件处理器。例如,日志组件会加入日志处理器,以记录 ESB 运行的日志。

图 4.2 中描述的是一个简单通道,在这个通道中没有分支,路由处理器也只有一个,在实际使用过程中当然没有这么简单,路由处理器可能有多个,Endpoint 也可能有多个。当整个通道的分支过于复杂的时候,建议还是把它看成一个业务流程,交给专业的 BPM 产品来做,这样不但可以减少 ESB 实施的复杂度,还还可以大大提升其可修改性。

在实际开发过程中,我们可以使用责任链模式(Chain of Responsibility Pattern 图 4.3)。一条责任链就是一个通道,消息处理器就是责任链中的一个个处理器(handler)。我们可以使用配置组件,在 ESB 初始化的时候就完成一个个消息处理器初始化工作。

图 4.3

消息对象

因为使用了消息通道,通道内的消息流必然是要有统一的消息对象(图 4.4)来进行良好的定义,这个消息对象,不但带有消息内容,还应该包含消息状态,以及消息所处通道的上下文信息。

图 4.4

MessageObject:消息载体

Context:上下文,所有的组件和配置都会注册到 Context 中

Session:主要记录用户请求的信息。

Endpoint(端点)

Endpoint 可以分为两种,一种集成在 ESB 内部,另一种嵌入到使用 ESB 的应用系统中。

理想情况下,推荐使用第二种,因为它的可操作范围要大一些。而且还支持一些特性,比如,支持 SOA 分布式事务和实现负载均衡的功能。在嵌入式的端点中实现负载均衡的一个优点是——可以避免出现水平扩展的负载均衡所带来的单点故障问题,可以在一定的程度上提高可用性。虽然理想是美好的,但现实情况却不允许我们在所有的应用系统中嵌入端点。

在 ESB 系统中我们可看到两类的端点 Inbound Endpoint 和 Outbound Endpoint,这两类的端点一类是接收用户的消息,一类是发送消息的服务方。

典型的 Endpoint 可以分为 Transport、Transformer 和 Filter 三个功能块,如下:

Transport

Transport 的最主要的功能是接收和发送数据,提供通讯协议适配器的功能。图 4.5 是一个典型结构,它主要由三部分组成

  • Connector:主要职责是处理通讯协议的连接,此连接可以是一个 TCP 连接也可以是 VM 内部的一个虚拟连接。
  • MessageRecerver:主要职责是通过 Connector 的连接,监听连接并获得用户请求的消息,并把数据组成一个 ESB 内部消息对象,并把这个消息对象交由 ESB 管道来进行处理。
  • MessageDispatcher:主要职责是把消息发送给服务方,并接收消息服务方的响应。它将被组成一个消息处理的节点,放入到管道中。

图 4.5

Transformer

其主要职责是对异构的消息格式进于转换,使其成为 ESB 内部能识别的消息格式。它本身是一个消息处理器,可以在初使化时置入到消息处理管道中去。

为了减化消息转换的次数,一般来说,ESB 内部会定义一个通用的消息格式,如果进来的消息不符合通用消息格式,它将被转换成通用格式。目前,我们喜欢采用的做法是使用 SOAP 作为一个通用的消息格式。

如果企业内的部分系统使用 XML 作为消息载体,那么建议使用 XSLT 来把 XML 消息转换成 SOAP 格式。XSLT 是一个相对便捷的报文转换技术 / 工具,并且 XSLT 本身也是 XML,对其进行修改也较方便,这对提升 ESB 的可修改性有一定的帮助。随着 XSLT 的普及,现在已经有大量的辅助的开发工具来提高 XSLT 开发效率。

Filter

它的主要职责是进行消息过滤。其普通的使用场景是这样的,根据数据的某些信息来判定是否要把消息送到下一个消息处理器进行处理。这个判定的过程,最好的做法是使用基础组件中的规则引擎来判定,这样会实现很好的可修改性。

路由

在架构上,路由应该和 Transformer 一样,只是一个消息的处理节点,它必须实现消息处理器的接口。这样,它才能成为消息通道的一部分。其最大的职责是,让消息找到正确的消费者。

路由是可以联合工作的,例如,我可以先使用路由的分解器分解消息,再使用内容路由来决定各部分消息的走向,使用 filter 来对消息进行过滤。这样,通过一层层的嵌套完成路由工作,不过这严重增加了路由的复杂性,虽然,我们支持这种做法,但并不建议这么做,专业的事应该交给专门的系统来进行处理,这些处理应该属于流程的编排,那就交给 BPM 来做吧。我们的原则是,如果路由超过了三层嵌套,就建议放到 BPM 中去。

基础组件

基础组件主要是为了保障整个 ESB 能更好地完成 ESB 任务的组件,每个组件都需要进行良好的设计,在这里,只是对一些组件进行一下必要的说明,在实际实施中可能还会增加一些基础组件。

  • 日志组件:主要是记录 ESB 交易产生的日志,这些日志用来追踪业务交易。
  • 流控组件:保障 ESB 内部不会因为交易量突然超过其承受能力而出现宕机。
  • 规则引擎:用在需要执行 filter 或是基于内容的路由的情况,可以提升可修改性。
  • 线程池:对 ESB 很重要,它是最基础组件之一
  • 对象池:在 ESB 的实现中,有一些对象不是线程安全的,而实例化这种对象要消耗大量的资源,这时,我们就会使且对象池来提高性能。它也是最基础组件之一。
  • 消息队列:消息队列可以是内部队列,也可以是外部队列,它也是非常基础的组件之一。
  • 配置组件:因为 ESB 实际上是一个可以做水平扩展的系统,而且,还要接受企业域的控制,所以配置组件需要有很高的可修改性,最好能应用 JMX 来对配置进行管理。
  • 监控组件:这个组件实际上只是监控平台的一个客户端,它主要是为监控平台收集一些 ESB 内部的信息而存在。
  • SEDA**** 组件:(Staged Event-Driven Architecture) 的核心思想是把请求处理的过程分成几个阶段,不同阶段使用不同数量的线程来处理,阶段间使用事件驱动的异步通信模式。

场景

简单同步处理

在 ESB 实施当中,有很大一部分是简单的同步处理,在简单的中的流程图如下所示(图 4.6)。这是一个最简单流程,当然 ESB 框架也会自动在这些消息处理器中间加入一些处理,比如记录日志等基础消息处理组件。

图 4.6

简单异步处理

简单异步处理需要使用公共组件 SEDA,SEDA 会把消息对象放入消息队列中,然后直接就返回给请求方。接着 SEDA 的线程池会去处理队列中的消息对象,后面的处理与同步处理一样,这种简单的异步处理,只适合于无返回消息的情况。

SEDA 的典型结构如图 4.7 所示,它需要一个事件队列,当某个请求消息是异步时,处理完报文转换后就会把消息放入到管道内的事件队列中。管道内的线程池监听该队列,取出队列内的消息传给下一步路由组件。也就是说,我们只是在简单同步的基础上,在 transport 和 transformer 之问放入了一个 SEDA 组件,这样就可以实现异步。

图 4.7

复杂路由

复杂路由是路由的多层嵌套,因为每个路由都由一个 messageProcessor 完成。这就为嵌套提供了必要的条件,复杂路由可以看作是一棵路由树。

内嵌式端点

有时我们会把端点内嵌到应用系统中,这样做有以下几点好处。

  • 端点的路由功能,可以减少出现负载均衡的单点故障
  • 减少应用系统的开发难度,我们对内嵌式的端点已经实现了良好的封装,应用系统可以很简单的使用它。
  • 可以拥有事务之类的特别的功能属性。

异常处理

异常处理是每个系统中必要的组成部分,在 ESB 中当然也不例外。在 ESB 系统中我们设有一个专门的异常处理组件来。我们会在管道中 cache 所有的异常,然后把异常抛给异常处理组件,异常处理组件根据上下文和异常类型选择相应的异常处理通道,并对异常进行相应的处理。

交易保障通道

交易保障请求就是要保证消息的消费者确实消费了该消息,或许消费者在处理该消息时可能出现异常,但这种情况我们会认为这个消息已经被消费了。只有在服务 / 消息消费者失去连接时,才会起用消息保障通道,它使用一定策略来实现消息的重发。

自定义异常通道

自定义异常通道,是处理用户自定义异常的通道,自定义异常可能是一个业务异常。

总结

ESB 作为一个 SOA 体系架构的的重要节点。我认为,在技术上它与传统的 EAI 有很多的相似之处,没有过多的新意,市面上也已经有多种多样的成熟产品,或开源或收费,技术上已经相当成熟,不会有太多技术问题。同时,我们可能还要考虑业务层面的问题,如:在实施 ESB 的时候,项目的干系人会远比普通项目来得多,如何处理他们之间的关系,比如服务治理,就显得十分重要了。在 ESB 项目里,我更多地从事技术架构和核心代码开发的工作,所以还不能总结出一套处理这类问题的方法论,这是我感到最遗憾的地方。

关于作者

郭时光:高伟达厦门分软工程师,有多年的企业系统架构和实施经验,对建立高并发高可用的互联网站点有浓厚的兴趣,现专注于 SOA 体系架构。博客 http://guoshiguan.iteye.com/ 欢迎大家来踩。


感谢马国耀对本文的审校。

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

2011 年 4 月 06 日 00:0015597

评论

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

架构师训练营第二周总结

月殇

极客大学架构师训练营

架构师训练营 - 命题作业 - 第二周

徐时良

架构师训练营第二次作业

月殇

极客大学架构师训练营

Week 2 命题作业及总结

阿泰

学习总结1

Wee权

Serverless 的收益与挑战 | 2020年度状态报告

donghui2020

Serverless

第二周作业

fmouse

极客大学架构师训练营

极客时间架构1期:第2周框架设计-学习总结

Null

架构师训练营 week2 总结

陈皓07

面向对象设计原则及框架案例

garlic

极客大学架构师训练营

架构师训练营第 1 期第二周课后练习题

郑凯元

极客大学架构师训练营

作业二

泡泡

第二周作业及学习笔记

橘子皮嚼着不脆

架構師訓練營 week2 作業

ilake

极客大学架构师训练营

作业一

泡泡

第 2 周 框架设计 腐败的代码

Pyr0man1ac

设计模式

高兵

架构师训练营 - 学习笔记 - 第二周

徐时良

华为18级工程师十年之作,整整3625页互联网大厂面试题合集

云流

学习 程序员 Java 面试 架构师技能

数据结构之堆栈

C语言与CPP编程

c++ 数据结构 堆栈 C语言 数据结构与算法

依赖倒置原则和接口隔离原则

garlic

极客大学架构师训练营

极客时间架构 1 期:第 2 周框架设计 - 命题作业

Null

架构师训练营 -week02- 总结

大刘

极客大学架构师训练营

【第二周】课后作业

云龙

极客大学架构师训练营

架构师训练营 week2 作业

陈皓07

架构师训练营 2 期 - 第二周总结

Geek_no_one

极客大学架构师训练营

架构师训练营第 1 期第二周学习总结

郑凯元

极客大学架构师训练营

第二周总结

fmouse

极客大学架构师训练营

极客大学 - 架构师训练营第一期 - 第二周作业

Black Eyed Peter

极客大学架构师训练营

数据结构之线性表

C语言与CPP编程

c++ 数据结构 C语言 线性表 数据结构与算法

架构师训练营 1 期第 2 周:框架设计

Wee权

InfoQ 极客传媒开发者生态共创计划线上发布会

InfoQ 极客传媒开发者生态共创计划线上发布会

ESB架构之企业实施案例-InfoQ