在上文中,主要介绍了SOA的概念,什么叫做“服务”,“服务”应该具备哪些特性。本篇中,我将介绍SOA的一种很常见的设计实践--基于服务总线的设计。
基于服务总线的设计
基于总线的设计,借鉴了计算机内部硬件组成的设计思想(通过总线传输数据)。在分布式系统中,不同子系统之间需要实现相互通信和远程调用,比较直接的方式就是“点对点”的通信方式,但是这样会暴露出一些很明显的问题:系统之间紧密耦合、配置和引用混乱、服务调用关系错综复杂、难以统一管理、异构系统之间存在不兼容等。而基于总线的设计,正是为了解决上述问题。总线则作为中枢系统,提供统一的服务入口,并实现了服务统一管理、服务路由、协议转换、数据格式转换等功能。这样能够将不同系统有效地连接起来,并大大降低了连接数(每个子系统只需要和总线建立连接)和系统间连接拓扑的复杂度。如图所示:
基于服务总线的设计,往往需要ESB(Enterprise Service Bus,企业服务总线)产品来充当基础设施。ESB采用了“总线”这样一种模式来管理和简化应用之间的集成拓扑结构,以广为接受的开放标准为基础来支持应用之间在消息、事件和服务的级别上动态的互连互通。 ESB是一种在松散耦合的服务和应用之间标准的集成方式。
在其内部设计和实现中,通常会应用到一些经典的架构模式,例如:Broker模式、消息总线模式、管道过滤器模式、发布订阅模式等。这里,我们将重点介绍这几种核心的架构模式。
Broker模式
Broker可以看作是服务总线中的一部分,通常应用于同步调用的场景(调用服务后阻塞,等待远程服务响应完成后再返回结果)。Broker可以看作是代理、分发,意味着对服务的调用,可以直接通过Broker来完成。在软件架构设计中,向已经存在(引用或者调用)关系的两者中间加入“第三者”是一种常见的解耦方式,显然,Broker也不例外。如下图所示:
为了进一步加深读者对Broker模式的理解,这里通过举例来说明:
在现实生活中,我们需要租房子(可以看作是一种服务调用),可以通过几种途径来完成。第一,我们可以先在网上了解,然后跑到目标小区去询问和看房,最后再找房东签合同等;第二,也可以直接找附近的地产中介,说出我们的要求和预算,请中介直接帮我们搞定一切。很明显,第一种方式,需要自己对目标有足够的了解而且还要亲自去找房东签合同(依赖具体,可以看作是紧密耦合),而第二种方式,仅仅需要告知中介需要什么即可完成租房,甚至都不需要知道哪个小区有房子、房东到底是谁等这些信息(依赖抽象,并通过第三者来实现解耦)。当然,找中介虽然省事,也会产生额外的经济开销。同理,通过Broker来调用服务也可能会产生一定的开销。
消息总线模式
SOA系统有三种基础组件:消息总线、信息转换/处理引擎和存储库。一般来说,这些组件会集成到ESB中,而在这些基础组件中,消息总线是最重要的。消息总线主要应用于异步通信场景(投递消息后立刻返回结果,而不用等待远程系统返回执行成功),可以大大提升响应速度和系统吞吐量。当然,消息总线也同时支持同步通信模式。基于消息总线模式的设计中,消息中间件屏蔽了系统间底层通信的细节,是比较典型的(异步)松耦合的架构。
在企业中,随着业务的逐渐发展,各大系统之间的调用交互变得非常频繁,关系错综复杂。
想象如果有几十或者上百个系统(在保险、金融领域很常见),将变得难以维护。通过引入消息中间件,便能有效的解决这些问题。不同系统之间,只需要连接到消息总线,能保证成功投递/接收消息即可。对于消息投递者(生产者)而言,根本不用关心消息的接收者(消费者)到底有哪些,也不用关心消费者接收到消息之后该如何处理。对于接收者(消费者)而言,只需要关注与自己有关的消息,接收到消息后并做出相应的处理即可。如下图所示:
比较典型的应用场景:张三在CRM系统中录入了一条客户签约订单的信息并审核通过后,CRM系统会向消息总线投递一条消息(消息中包含必要信息,例如员工ID,订单编号,产品编号和交易金额等,而CRM系统不用关心有哪些系统会接受到该消息)。员工绩效奖金管理系统订阅了该消息主题,收到消息通知后开始处理(给张三执行加奖金操作),同时,进销存系统收到消息通知后也会即时地更新商品库存的数量。这样,便实现异构系统之间的松耦合、异步通信。当然,真实场景可能比这里更复杂,但是实现思路上都大同小异。
在理解了上述实例之后,有必要了解下Java EE中被广泛应用和实现的JMS(Java消息服务)。
JMS是一种关于消息的规范,业界流行的ActiveMQ则是实现了JMS规范的开源消息总线。
JMS支持两种模式:发布/订阅模式和队列模式(点对点模式)。其中,发布/订阅模式借鉴了现实生活中的出版社(发布图书)和读者(订阅图书),消息的消费者(读者)只需要订阅自己感兴趣的消息(图书)即可,消息生产者(出版社)生产(出版)了消费者(读者)感兴趣的新消息(新书)后,会通知消费者(读者)接受处理。在JMS发布/订阅模式中,通常以Topic(主题)来标识消息,是一对多的模式,意味着同一个主题可以同时被多个消费者来订阅和消费。而在JMS 队列模型中,通常以Queue name来标识消息,是一对一的模型,意味着同一条消息只能被一个消费者接收并消费(且只能被消费一次)。在生产者和消费者都是集群的环境中,通常需要将这两种模式结合起来使用,情况会复杂很多,而且需要考虑容错性、负载均衡、消息一致性、消息优先级等复杂的问题。
业界主流的消息总线(消息中间件)产品,普遍支持消息过滤、自动重试、分布式事务、持久化、消息优先级、消息回溯、(生产者/消费者/中间件自身)集群、故障转移等高级特性。
总结
基于总线架构的主要优势在于:
l 可扩展性
使用消息架构,可以在不影响现有应用的情况下增加或移除应用。
l 低复杂度
每个应用只需要和总线通信,只有1个连接点,降低了应用程序集成的复杂度。
l 灵活性
对于构成复杂处理的应用程序通信组来说,只需要改变配置和控制路由参数就能满足业务逻辑或者需求变更,而不需要更改服务本身。
l 松耦合
应用程序直接和消息总线通信,不依赖其它应用程序,因此可以替换和修改。
l 可扩展性
可以把多个应用程序附加到总线上,进行并发处理,达到负载均衡的目的。
基于总线的模型,可以面向同构和异构系统(跨平台)。当前主要应用于传统企业应用的整合与系统集成中(例如:电信、保险、金融等行业)。由于基于总线的模型是一种集中式的架构,总线自身容易形成性能瓶颈,而且在实现高可用和容错性方面的复杂度和成本相对较高。所以,该模式并不是很适合于高并发、高性能、高吞吐量的互联网应用。
注:关于消息总线(消息中间件)的知识点很多,在实际应用中还有很多更加深入和复杂的细节问题。由于篇幅问题,笔者就不做过多的细节介绍和展开,感兴趣的读者,可以自行去查阅相关资料或者参考开源产品,做深入的学习和研究。
ESB产品主要包括:开源Mule ESB、ServiceMix、JBoss ESB,商业的Oracle ESB、BEA AquaLogic ServiceBus,.NET领域的NService Bus、MassTransit等。
MQ产品主要包括:ActiveMQ、RabbiteMQ、ZeroMQ、RocketMQ、Kafka、MSMQ等。