近期在赶一个项目进度,项目中使用了消息队列activemq。至于为什么在选型的时候选了activemq在此不过多的解释。本文主要想阐述,如何更好的使用activemq.
提到消息队列,一般会涉及如下的名词,在此作统一的解释,以外免后面理解上有歧义。
broker指的是消息中间件,在此指activemq实例,即安装activemq安装之后,并且启动我们就有了broker;
指borker与其客户端传递的字节码。
broker的客户端之一,但是它是消息的生产者,即它产生的消息传递给broker.
broker的另一种形式客户端,它从broker获取之前producer生产的消息;
目前有Queue 与Topic
发布在此文指的,broker启动之后,producer是否启动连接过broker,如果联接过,即称为发布过。
发布在此文指的,broker启动之后,consumer是否启动连接过broker,如果联接过,即称为订阅过。
从producer角度指broker是否存储producer发送过来的消息;从consumer角度指boroker是否存储consumer消费者的信息,即是否订阅过.
中文翻译为点对点。即一个producer发送消息,经broker之后,最终只能被一个consumer消息获取到,即一个消息只能被消息一次。当producer 生产消息速度远远大于消费者时,可以通过增加consumer端的机器来进行负载均衡。或者说Queue对于consumer天然就是可以负载均衡的;
在使用queue时,默认是持久化的,所以只有一种情况是丢数据的,即先发布后订阅。即之前的消息是无法获取的。这种情况的消息无法获取也是很容易理解的,即broker不知道该consumer的存在。那么如果需要一条消息,能被多个不同的consumer同时获取。此种情况单个queue是不适合,我们可以通过Topic来解决。
中文翻译主题,这个很难理解。如果需要一条消息,能被多个不同的consumer同时获取,我们使用topic进行发布,就可以达到这个目的。但是在jms1.1中中需要consumer的唯一性,无法负载均衡。这一句话不是很好理解,举个栗子。
producer 以 topic发送一条消息至broker,consumer a,b,c,d同时订阅此条消息,则a,b,c,d均能收到消息。如果topic发送消息的速速远远大小consumer a,b,c,d 的其中一个的消息能力(比如a),那么此种情况整个系统将会受到影响,注意是整个系统,而不单单是某一个consumer.那么能否通过增加机器来解决consumer a的消费能力,答案是可以(后面描述)。但是在我们现在描述的Topic下是不行。因为broker通过clientid来唯一识别一个consumer,当两个节点通过一个clientId来连接broker,另一个会报错。
使用 topic需要注意点,activemq 默认对于producer的消息是非持久化的。需要producer明确告知broker是否需要持久化。对于consumer也是需要分持久订阅与非持久订阅。所以如果组合不好很容易丢数据。
使用topic的时候,丢数据的主要情况:
或者说Topic 不丢数据的方式即为 producer 持久发布,consumer持久订阅。
如何解决Topic 情况下的consumer负载均衡问题,或者 consumer的多节点的消息重复消费问题。consumer的负载均衡问题与consumer的多节点消息重复消费,可以看成是两个问题,但是它们又有重叠。对于多节点的消息重复消费,只需要consumer端作到业务方法幂等或者进行消费记录即可解决。但是consumer的负载均衡即不好解决。
在jms2.0有一种 共享持久订阅者,它可以帮我们解决这个问题。但是activemq只实现了jms1.1,而没有实现jms2.0(或者说至少目前没有实现)。但是activemq 对于我们的这个需要也不是没实现,而是采用另外一种形式即VirtualTopic.在解释这个之前,让我们自己出一个方案来解决如果上问题,如何解决呢?一个直观的想法,即消息的转发。producer使用topic形式发送消息给broker,consumer a 采用topic 形式接收消息,然后将消息以queue的形式转发给broker,真正的业务consumer 则订阅consumer a发布的queue,而非之前producer的topic.这个解决方案堪称完美?它的问题是 consumer a的容错(failover)怎么解决?系统的复杂度如何降低?
一句话描述VirtualTopic即activemq 将我们上面的方案实现了,并且解决了consumer a 的问题。 VirtualTopic是activemq高级特性。关于它的具体解释,请看官网。在此只描述它的使用方式及注意事项。
producer的使用与普通Topic没有任何的不同,只是topic地名字以VirtualTopic.(注意有个点)作前缀即可,比如VirtualTopic.order.
而consumer则有两种选择:一种是像与普通的topic那样使用,这种方式遇到的问题上面都说过了,一点都不会少,当然,如果consumer还是像普通的方式使用,我们也就不需要那么啰嗦了。
另一种是comsumer可以以queue的形式去订阅该topic.默认情况下consumer的订阅队列名为Consumer.***.topic名字,比如Consumer.readorderMsg.VirtualTopic.order. 当然了这个默认的前缀是可以更改的(它的配置在activemq.xml中)。
使用VirtualTopic的丢数据的情况与queue相同。