消息中间件

转载地址:http://ink.csdn.net/articles/show/55f27505c384fe3b0e202526


消息中间件剖析

本文首先从概念和应用场景对消息中间件做总体描述,然后对几种典型的消息中间件从原理到实现进行分析和阐述,并进一步详细介绍了基于JMS规范的消息中间件。

什么是消息中间件

消息中间件是一套系统(或平台),用于应用程序之间进行通信,系统通过消息传递完成交互。

消息中间件的主要特点有以下几个。

1. 分布式:消息中间件都是分布式的,因此才可以提供异步、解耦等功能。

2. 可靠性:基于消息的通信是可靠的,消息不会丢失。大多数消息中间件都提供将消息持久化到磁盘的功能。

3. 异步:通过消息中间件,可将远程同步调用拆解成为异步调用。对于不需要获取远程调用结果的应用场景来说,性能提升明显。

4. 松耦合:消息直接由中间件存储和分发。消息生产者只需关注如何将消息发送给消息中介服务器;消费者只需关注如何从中介服务器订阅。生产者和消费者之间是完全解耦的,不需要知道彼此的存在。

5. 事件驱动:可以将复杂的应用系统重构成为事件驱动的系统。事件溯源(Event Sourcing),表示一个对象从创建到消亡,会经过的多种状态。如果把对象的状态变化都存储下来,不但可以根据状态变化记录获取对象的当前状态,也可以回溯对象的变化过程。消息中间件能很好地支持这样的系统设计方式,将触发对象状态变化的事件放入消息队列。

在带来好处的同时,引入消息中间件也有一些需要注意的地方。

1. 分布式带来的复杂性:消息中间件都是分布式的,引入分布式会大大增加系统复杂度,在不同主机、不同进程之间的调用和调试,会带来更多的不稳定性。分布式系统还会增加对外部系统的依赖。即使自己的系统没有问题,也可能会因为依赖系统出问题而导致系统不稳定。因此,Martin Fowler曾说:“分布式调用的第一原则就是不要分布式。”

2. 同步调用应该考虑其他方式:尽管消息中间件也可用于同步调用,但这并不是它的长项,同步调用可以考虑使用HTTP、NIO等其他方式。

图1描述了消息中间件的组成部分。

3. 消息中介(Broker):可理解为消息中间件的服务器。消息中介用于存储消息,并且维护消息消费者和消息队列之间的订阅关系(也可由消费者自己维护)。

消息在中介如何存储,是决定消息中间件功能和性能的最重要因素。目前来说,最主要的两种存储消息的方式是kv存储和顺序存储。后文将详细介绍两种不同存储引擎消息中间件的区别。

消息中间件_第1张图片

1. 消息生产者(Producer):与消息消费者一起组成消息中间件的客户端。生产者用于发送消息到消息中介。

客户端连接服务器一般可以选择TCP、HTTP等协议。内网基于长连接的TCP协议效率更高,公网可以考虑HTTP协议穿透防火墙。

2. 消息消费者(Consumer):与消息生产者一起组成消息中间件的客户端。消费者用于从消息中介获得消息并交给业务系统使用。

消息的消费分成推送和拉取两种模式。推送是消息中介主动将消息发送给消息消费者,拉取则是消息消费者主动从消息中介获取消息。两种模式的使用场景不太一样,各有优缺点,下文也会详细介绍。

为了便于理解,在这里将消息中间件和关系型数据库做一个比较:1. 消息中介相当于数据库的服务器;2. 消息生产者相当于使用INSERT语句的SQL客户端;3. 消息消费者相当于使用SELECT语句的SQL客户端。

当然这个比较不是非常恰当。例如根据实现方式不同,消息的删除可能由消费者发起,也可能由消息中介主动发起。但能比较直观说明,消息中间件是由服务器和客户端组成,以及它们所承担的职责。

JMS

JMS的全称是Java Message Service,即Java消息服务,定义了Java平台消息中间件的技术规范。JMS只提供了应用程序对消息中间件操作的接口规范,并未提供实现,其实现由各个消息中间件厂商的驱动程序来提供,和Java的另一个规范标准JDBC相似。遵循JMS规范的消息中间件都使用统一的API。

JMS定义了消息的编程模型,如连接工厂、会话、消息目的地、消息生产者、消息消费者、消息体、消息优先级和消息类型等。本文并不会详细介绍JMS,有兴趣了解请查阅相关资料。但会重点介绍JMS中定义的消息类型,因为后面介绍的几种消息中间件如何支持消息类型是本文的关键之一。

消息类型分为点对点和发布/订阅两种。

1. 点对点(Point To Point):消息生产者将消息发送到消息队列(Queue)中,只有一个消费者能够消费此消息,消费完成之后消息即删除。

这里应该注意的是,任意一个消费者都可以消费这个消息,但消息绝对不会被两个消费者重复消费。

消息的消费者和生产者没有时间依赖,可以先发送消息,再启动消费者。

图2展示了JMS的点对点消息类型。

消息中间件_第2张图片

2. 发布/订阅(Publish/Subscribe):消息生产者将消息发送到消息主题(Topic)中,所有订阅这个主题的消费者都可以消费此消息,当所有订阅者都消费完成之后才能删除消息。

消息的生产者和消费者之间有时间依赖,只有事先订阅这个主题的消费者才可消费。如果先发送消息,后订阅主题,那么订阅之前的消息将不能被这个订阅者消费。

订阅者又可分为持久化订阅和非持久化订阅。如果持久化的订阅者在订阅之后离线,收到的消息仍会在订阅者再次上线时收到,不会错过消息。而非持久化的订阅者一旦离线,离线时的消息将被错过。

图3展示了JMS的发布/订阅消息类型。

消息中间件_第3张图片

最后,需要介绍消息传递语义(Message Delivery Semantics)。消息传递的担保有3种级别:最多一次(At Most Once),至少一次(At Least Once)和精确地仅发送一次(Exactly Once)。

【最多一次】

消息只发送或消费一次,无论消息中介是否收到消息,或者消息是否已消费成功,都不会再次发送。

这样做的问题是,消息可能会丢失。虽然客户端发送了消息,但消息中介还来不及存储就崩溃,那么这条消息就会丢失。

【至少一次】

如果消息中介没有明确告诉客户端消息已经收到,那么客户端会重新发送或消费这条消息。

这样做的问题是,消息可能会重复发送或消费。例如消息已经存到了消息中介,但还没来得及给客户端发送确认信息,消息中介就崩溃了。那么等消息中介重新启动之后,客户端会重新发送这条消息,造成重复。

【精确地仅发送一次】

既不会多也不会少发送消息。一般通过事务来实现,只有消息中介收到客户端确认处理成功的信息,才会提交事务,否则在经过一定时间限制之后消息会回滚。精确地仅发送一次消息,不会丢失也不会有重复的消息,但要达到这一点,对性能的损耗非常大。

JMS要求消息精确地仅发送一次。

消息中间件_第4张图片

应用场景

【业务解耦】

各个业务系统仅需要处理自己的业务逻辑,并且发送事件消息到消息中间件。下游业务系统直接订阅消息中间件的队列或主题获取事件。这样不但解耦了系统间的依赖,而且使调用异步化,提升了系统的性能。图4显示了如何通过消息中间件将订单系统解耦。

【削峰填谷】

如果上游系统的吞吐能力强于下游系统,那么在上游系统满负荷时将冲垮下游系统。使用消息中间件的定时定量推送或者定时定量拉取,可在上游峰值时堆积消息,在峰值过去时慢慢消费,增强系统的缓冲能力。图5显示了如何通过消息中间件缓冲业务洪峰,并且使用定时定量分流消费消息。

消息中间件_第5张图片

【广播通知】

系统一个状态的改变,需要通知多个相关系统,可通过消息订阅的方式推送给各个订阅者系统。比如数据库值的改变,需要通知所有的缓存系统更新,可以把数据库值改变发送消息给消息中间件,然后各缓存订阅相关主题,收到消息后更新自己的缓存。图6显示了数据中心如何通过广播通知已订阅的缓存系统更新数据。

消息中间件_第6张图片

【日志分析】

日志分析往往需要处理大量日志,不可能存储在一台物理机上。消息中间件可提供一个集群,用来存储海量消息,将其缓存到消息中间件。比较常用的日志分析系统是使用日志收集组件(如Flume)收集,存储到高吞吐量的消息中间件(如Kafka),供实时分析系统(如Storm)分析日志。图7描述了日志分析系统的基本组成部分。

消息中间件_第7张图片

消息中间件概览

表1展示了目前比较流行的消息中间件的简单对比,希望对读者的选型有所帮助。

消息中间件_第8张图片

通过上面的分析可知,处理核心业务逻辑的场景和处理海量分析的场景是截然不同的。因而,消息中间件也天然分成了两大流派——高可靠和高吞吐。下期将对这部分内容进行详细介绍。



你可能感兴趣的