转自InfoQ中文网:
http://www.infoq.com/cn/articles/esb-enterprises-case
概述
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项目里,我更多地从事技术架构和核心代码开发的工作,所以还不能总结出一套 处理这类问题的方法论,这是我感到最遗憾的地方。