JMS(Java Messaging Service)是Java平台上有关面向消息中间件的技术规范,实际上是一套api,ActiveMQ而是这个规范的一个具体实现。
JMS规范:
ActiveMQ可以支持单台6000+的并发。
队列:每条消息通仅会有一个消费者消费
主题:只要订阅这个主题的消费者都可以收到消息
消费者同步:使用receive方法,消息没来之前一直阻塞
消费者异步:使用监听器
生产者同步:使用send方法,一直阻塞到MQ确认收到消息。持久化非事务下,默认同步。
生产者异步:使用send方法,不会阻塞,这种可能丢失数据。非持久化情况下,默认异步。在开启事务时,默认使用异步,虽然send不会阻塞,但commit方法会阻塞。
队列消息默认是持久化,主题默认是非持久化。非持久化数据保存在内存中,一旦MQ重启,数据丢失,并且非持久化数据如果消息者在生产者发送消息之后再启动,无法消费到数据。
(1)AMQ消息存储
AMQ消息存储是基于文件的存储方式,写入速度快和容易恢复。消息存储在一个个文件中,文件默认大小为32M,如果一条消息的大小超过了32M,那么这个值需要设置大一点。当一个存储文件中的消息已经全部被消费,那么这个文件将被标识为可删除,在下一个清除阶段,这个文件被删除。AMQ适用于ActiveMQ5.3之前的版本。
(2)KahaDB
5.4版本之后默认的持久化方式。基于文件的本地数据库储存形式,提高了容量和恢复能力,强扩展性、恢复的时间比AMQ短,但写入速度没有AMQ的速度快。默认KahaDB文件存在ActiveMQ安装路径下的/data/KahaDB目录。
(3)关系型数据库存储
ActiveMQ 4版本开始,ActiveMQ支持使用关系型数据库进行持久化。
使用JDBC的方式持久化
其中?relaxAutoCommit=true必须有,其他的属性根据数据库的配置自行决定。
activemq_acks:用于存储订阅关系,字段如下:
container:消息的目的地
sub_dest:如果是使用static集群,这个字段会有集群其他系统的信息
client_id:每个订阅者都必须有一个唯一的客户端id用以区分
sub_name:订阅者名称
selector:选择器,可以选择只消费满足条件的消息。条件可以用自定义属性实现,可支持多属性and和or操作
last_acked_id:记录消费过的消息的id
activemq_lock:记录哪个Broker是当前的Master Broker,在集群环境中才有用,只有一个Broker可以获得消息,称为Master Broker,其他的只能作为备份。
activemq_msgs:用于存储消息,Queue和Topic都存储在这个表中。
id:自增的数据库主键
container:消息的目的地
msgid_prod:消息发送者客户端的主键
msg_seq:是发送消息的顺序,msgid_prod+msg_seq可以组成jms的messageid
expiration:消息的过期时间,存储的是从1970-01-01到现在的毫秒数
msg:消息本体的java序列化对象的二进制数据
priority:优先级,从0-9,数值越大优先级越高
(1)AUTO_ACKNOWLEDGE
自动确认, 同步(receive)方法返回message给消息时会立即确认。在异步(messageListener)中,如果onMessage方法正常结束,消息将会正常确认。如果onMessage方法异常,将要求ActiveMQ重发消息。但消息的重发次数有限制,每条消息中都包含“redeliveryCounter”计数器,如果重发次数达到阀值,将导致broker端认为此消息无法消费,此消息将会被删除或者迁移到"dead letter"通道中。
(2)CLIENT_ACKNOWLEDGE
客户端手动确认。使用message.acknowledge()和session.acknowledge()效果一样。
消息未确认会导致重发。
(3)DUPS_OK_ACKNOWLEDGE
自动批量确认,具有延迟确认的特点,ActiveMQ会根据内部算法,在收到一定数量的消息自动进行确认。在此模式下,也可能会出现重复消息,比如consumer故障重启后,那些尚未ACK的消息会重新发送过来。
(4)SESSION_TRANSACTED
当session使用事务时,就是使用此模式,其他模式设置也不生效。使用此模式,必须调用session.commit()方法,将当前session的事务中所有消息立即被确认。在事务开始之后的任何时机调用rollback(),意味着当前事务的结束,事务中所有的消息都将被重发。当然在commit之前抛出异常,也会导致事务的rollback。
“.” 用于作为路径上名字间的分隔符。
“*” 用于匹配路径上的任何名字。
“>” 用于递归地匹配任何以这个名字开始的destination。
死信队列是用来保存处理失败或者过期的消息。
出现下面情况时,消息会被重发:
如何保证一个消息可以被不同集群下消息者都消息,但同一集群下消息者只能有一个消费。队列和主题单独使用都不能满足
(1)队列和主题级联
比如先将消息发往主题,多个消息者消费后再发往队列。
(2)虚拟主题
对生产者来说就是一个正常的Topic,名称以VirtualTopic.开头。如VirtualTopic.AA。默认虚拟主题的前缀是 :VirtualTopic。
对于消费者来说是个队列,不同集群使用不同的前缀作为队列的名称,即可实现消费端应用分组。例如Consumer.A.VirtualTopic.AA,说明它是名称为A群组的消费端,同理Consumer.B.VirtualTopic.AA说明是一个名称为B群组的消费端。
消费虚拟地址默认格式:Consumer.*.VirtualTopic。
(3)组合队列
组合队列允许用一个虚拟的destination代表多个destinations。这样就可以通过在一个操作中同时向多个destination发送消息。多个destination之间采用“,”分割。例如:
Queue queue = new ActiveMQQueue(“A,B,C”);如果使用不同类型的destination,那么需要加上前缀如queue:// 或topic://,例如:Queue queue = new ActiveMQQueue(“A,topic://B”);
ActiveMQ中可以延迟投递消息,这个不多说。首先在activemq.xml启动延迟投递配置,然后代码中设置消息属性。
ActiveMQ的集群方式主要由两种:Master-Slave和Broker Cluster。
Master-Slave方式中,只有Master提供服务,Slave是实时地备份Master的数据,以保证消息的可靠性。当Master失效时,Slave会自动升级为Master。Master-Slave模式分为四类:Pure Master Slave、Shared File System Master Slave和JDBC Master Slave,以及Replicated LevelDB Store方式 。
(1)Pure Master Slave
ActiveMQ5.8以前支持。Slave配置activemq.xml文件,在节点中添加连接到Master的URI和设置Master失效后不关闭Slave。
(2)Shared File System Master Slave
利用共享文件系统做ActiveMQ集群,基于kahaDB完成的,kahaDB的底层是文件系统。这种方式的集群,Slave的个数没有限制,哪个ActiveMQ实例先获取共享文件的锁,那个实例就是Master,其它的ActiveMQ实例就是Slave。
如果各个ActiveMQ实例需要运行在不同的机器,就需要用到分布式文件系统了。
(3)Shared JDBC Master Slave
JDBC Master Slave模式和Shared File Sysytem Master Slave模式的原理是一样的,只是把共享文件系统换成了共享数据库。只需在所有的ActiveMQ的配置文件activemq.xml中添加数据源,所有的数据源都指向同一个数据库。
(4)Replicated LevelDB Store
ActiveMQ5.9以后新增特性,使用ZooKeeper协调选择一个node作为master。被选择的master broker node开启并接受客户端连接。 其他node转入slave模式,连接master并同步他们的存储状态。slave不接受客户端连接。所有的存储操作都将被复制到连接至Master的slaves。
(5)LevelDB
Leveldb是一个google实现的非常高效的kv数据库,具有很高的随机写,顺序读/写性能,但是随机读的性能很一般。
Broker-Cluster中各个broker通过网络互相连接,并共享queue。当broker-A上面指定的queue-A中接收到一个message处于pending状态,而此时没有consumer连接broker-A时。如果cluster中的broker-B上面由一个consumer在消费queue-A的消息,那么broker-B会先通过内部网络获取到broker-A上面的message,并通知自己的consumer来消费。Broker的集群分为Static Discovery和Dynamic Discovery两种
(1)Static Discovery集群
Static Discovery集群就是通过硬编码的方式使用所有已知ActiveMQ实例节点的URI地址。如:消息生产者应用连接一个ActiveMQ实例,我们暂时称为MQ1,所有的消息都由该实例提供;两个消息消费者应用分别连接另外两个ActiveMQ实例,分别为MQ2和MQ3,两个消息消费者需要消费MQ1上的消息,但它们连接的都不是MQ1,可以通过Static Discovery方式把MQ1上的消息路由到MQ2和MQ3,为了保证消费者不因某个节点的失效而导致不能消费消息,在消费者应用中需要配置所有节点的URI。
(2)Dynamic Discovery
Dynamic Discovery集群方式在配置ActiveMQ实例时,不需要知道所有其它实例的URI地址,只需在所有实例的activemq.xml文件中配置同一地址