17.1.1 发送消息

大多数人都使用过邮政服务。每天会有数百万信件、明信片和包裹交到邮递员手上,我们相信自己邮寄的东西会被送到目的地。世界实在是太大了,我们无法自己去运送这些东西,因此我们依赖邮政系统为我们运送。我们在信封上写明地址,贴张邮票,接着把它们投到信箱里,而不需要考虑信件如何到达目的地。

邮政服务的关键在于间接性。当奶奶的生日到来时,如果我们直接送给她一张贺卡,这非常不方便。我们必须留出几小时甚至是几天的时间去为她送生日贺卡,这取决于她住哪里。幸运的是,邮局可以将贺卡送到奶奶那里,而我们可以继续自己的生活。

与此类似,间接性也是异步消息的关键所在。当一个应用向另一个应用发送消息时,两个应用之间没有直接的联系。相反的是,发送方的应用程序会将消息交给一个服务,由服务确保将消息投递给接收方应用程序。

在异步消息中有两个主要的概念:消息代理(message broker)和目的地(destination)。当一个应用发送消息时,会将消息交给一个消息代理。消息代理实际上类似于邮局。消息代理可以确保消息被投递到指定的目的地,同时解放发送者,使其能够继续进行其他的业务。

当我们通过邮局邮递信件时,最重要的是要写上地址,这样邮局就可以知道这封信应该被投递到哪里。与此类似,每条异步消息都带有一个目的地,目的地就好像一个邮箱,可以将消息放入这个邮箱,直到有人将它们取走。

但是,并不像信件地址那样必须标识特定的收件人或街道地址,消息中的目的地相对来说并不那么具体。目的地只关注消息应该从哪里获得 —— 而不关心是由谁取走消息的。这种情况下,目的地就如同信件的地址为 “本地居民”。

尽管不同的消息系统会提供不同的消息路由模式,但是有两种通用的目的地:队列(queue)和主题(topic)。每种类型都与特定的消息模型相关联,分别是点对点模型(队列)和发布/订阅模型(主题)。

点对点消息模型

在点对点模型中,每一条消息都有一个发送者和一个接收者,如图 17.3 所示。当消息代理得到消息时,它将消息放入一个队列中。当接收者请求队列中的下一条消息时,消息会从队列中取出,并投递给接收者。因为消息投递后会从队列中删除,这样就可以保证消息只能投递给一个接收者。

尽管消息队列中的每一条消息只被投递给一个接收者,但是并不意味着只能使用一个接收者从队列中获取消息。事实上,通常可以使用几个接收者来处理队列中的消息。不过,每个接收者都会处理自己所接收到的消息。

这与在银行排队等候类似。在等待时,我们可能注意到很多银行柜员都可以帮助我们处理金融业务。在柜员帮助客户完成业务后,她就空闲了,此时,她会要求排队等候的下一个人前来办理业务。如果我们排在队伍的最前边时,我们就会被叫到,然后由其中的一个空闲柜员来帮助我们处理业务,而其他的柜员则会帮助其他的银行客户。

从另一个角度看,我们在银行排队时,并不知道哪一个柜员会帮助我们办理业务。我们可以计算队伍中有多少人,与柜员的数目进行比较,注意哪一个柜员业务办理速度最快,然后猜测会由哪一个柜员办理我们的业务。但是,一般情况下我们都会猜错,最终会由另一个柜员来办理。

同样,在点对点的消息中,如果有多个接收者监听队列,我们也无法知道某条特定的消息会由哪一个接收者处理。这种不确定性实际上有很多好处,因为我们只需要简单地为队列添加新的监听器就能提高应用的消息处理能力。

发布—订阅消息模型

在发布—订阅消息模型中,消息会发送给一个主题。与队列类似,多个接收者都可以监听一个主题。但是,与队列不同的是,消息不再是只投递给一个接收者,而是主题的所有订阅者都会接收到此消息的副本,如图 17.4 所示。

正如它的名字所暗示的,发布—订阅消息模型与杂志发行商和杂志订阅者很相似。杂志(消息)出版后,发送给邮局,然后所有的订阅者都会收到杂志的副本。

杂志的类比就到此为至,因为对于异步消息来讲,发布者并不知道谁订阅了它的消息。发布者只知道它的消息要发送到一个特定的主题 —— 而不知道有谁在监听这个主题。也就是说,发布者并不知道消息是如何被处理的。

现在,我们已经介绍了异步消息的基本概念,下面让我们看看它与同步 RPC 的对比。

Last updated