ActiveMQ的集群
Queue consumer clusters ActiveMQ支持Consumer对消息高可靠性的负载平衡消费,
如果一个Consumer死掉, 该消息会转发到其它的Consumer消费的Queue上。如果一个Consumer获得消息比其它 Consumer快,那么他将获得更多的消息。因此推荐ActiveMQ的Broker和Client使用 failover://transport的方式来配置链接。
Broker clusters 大部情况下是使用一系列的Broker和Client链接到一起。如果一个Broker死掉了,
Client可以自动链接到其它Broker上。实现以上行为需要用failover协议作为Client。 如果启动了多个Broker,Client可以使用static discover或者 Dynamic discovery 容易的从一个broker到另一个broker直接链接。 这样当一个broker上没有Consumer的话,那么它的消息不会被消费的,然而该
broker会通过存储和转发的策略来把该消息发到其它broker上。
特别注意:ActiveMQ默认的两个broker,static链接后是单方向的,broker-A可以访问消费broker-B的消息,如果要支持双向通信,需要在netWorkConnector配置的时候, 设置duplex=true 就可以了。
Master Slave
在5.9的版本里面,废除了Pure Master Slave的方式,目前支持:
1:Shared File System Master Slave:基于共享储存的Master-Slave:多个broker实例使用 一个存储文件,谁拿到文件锁就是master,其他处于待启动状态,如果master挂掉了,某 个抢到文件锁的slave变成master
2:JDBC Master Slave:基于JDBC的Master-Slave:使用同一个数据库,拿到LOCK表的写锁的 broker成为master
3:Replicated LevelDB Store:基于ZooKeeper复制LevelDB存储的Master-Slave机制,这个 是5.9新加的
具体的可以到官方察看: http://activemq.apache.org/masterslave.html
JDBC Master Slave的方式
利用数据库作为数据源,采用Master/Slave模式,其中在启动的时候Master首先获 得独有锁,其它Slaves Broker则等待获取独有锁。
推荐客户端使用Failover来链接Brokers。 具体如下图所示:
Master失败
如果Master失败,则它释放独有锁,其他Slaver则获取独有锁,其它Slaver立即获得独有锁后,此时它将变成Master,并且启动所有的传输链接。同时,Client将停止链接之前的Master和将会轮询链接到其他可以利用的Broker即新Master。
如上中图所示 n Master重启
任何时候去启动新的Broker,即作为新的Slave来加入集群,如上右图所示
JDBC Master Slave的配置
使用<jdbcPersistenceAdapter/>来配置消息的持久化,自动就会使用JDBC Master Slave的方式。
Destination高级特性之Wildcards
Wildcards用来支持名字分层体系,它不是JMS规范的一部分,是ActiveMQ的扩展。
ActiveMQ支持以下三种wildcards:
1:“.” 用于作为路径上名字间的分隔符
2:“*” 用于匹配路径上的任何名字
3:">" 用于递归地匹配任何以这个名字开始的destination
示例,设想你有如下两个destinations
PRICE.STOCK.NASDAQ.IBM (IBM在NASDAQ的股价)
PRICE.STOCK.NYSE.SUNW (SUN在纽约证券交易所的股价)
那么:
1:PRICE.> :匹配任何产品的价格变动
2:PRICE.STOCK.> :匹配任何产品的股票价格变动
3:PRICE.STOCK.NASDAQ.* :匹配任何在NASDAQ下面的产品的股票价格变动
4:PRICE.STOCK.*.IBM:匹配任何IBM的产品的股票价格变动
客户化路径分隔符,比如你想要用 “/” 来替换 “.”
<plugins>
<destinationPathSeparatorPlugin/>
</plugins>
Destination高级特性之Composite Destinations
组合队列 Composite Destinations 组合队列允许用一个虚拟的destination代表多个destinations。这样就可以通过composite destinations在一个操作中同时向多个queue发送消息。
1:客户端实现的方式
在composite destinations中,多个destination之间采用“,”分割。例如:
Queue queue = new ActiveMQQueue("FOO.A,FOO.B,FOO.C"); 如果你希望使用不同类型的destination,那么需要加上前缀如queue:// 或topic://,
例如: Queue queue = new ActiveMQQueue("FOO.A,topic://NOTIFY.FOO.A");
2:在xml配置实现的方式
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<compositeQueue name="MY.QUEUE">
<forwardTo>
<queue physicalName="my-queue" />
<queue physicalName="my-queue2" />
</forwardTo>
</compositeQueue>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
3:使用filtered destinations,在xml配置实现的方式
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<compositeQueue name="MY.QUEUE">
<forwardTo>
<filteredDestination selector="odd = 'yes'" queue="FOO"/>
<filteredDestination selector="i = 5" topic="BAR"/>
</forwardTo>
</compositeQueue>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
4:避免在network连接broker中,出现重复消息
<networkConnectors>
<networkConnector uri="static://(tcp://localhost:61617)">
<excludedDestinations>
<queue physicalName="Consumer.*.VirtualTopic.>"/>
</excludedDestinations>
</networkConnector>
</networkConnectors>
Configure Startup Destinations 如果需要在ActiveMQ启动的时候,创建Destination的话,可以如下配置:
<broker xmlns="http://activemq.apache.org/schema/core">
<destinations>
<queue physicalName="FOO.BAR" />
<topic physicalName="SOME.TOPIC" />
</destinations>
</broker>
Destination高级特性之Delete Inactive Destinations
Delete Inactive Destinations 一般情况下,ActiveMQ的queue在不使用之后,可以通过web控制台或是JMX方式来删除掉。当然,也可以通过配置,使得broker可以自动探测到无用的队列(一定时间内为空的队列)并删除掉,回收响应资源。可以如下配置:
<broker xmlns="http://activemq.apache.org/schema/core" schedulePeriodForDestinationPurge="10000">
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue=">" gcInactiveDestinations="true" inactiveTimoutBeforeGC="30000"/>
</policyEntries>
</policyMap>
</destinationPolicy>
</broker>
说明:
schedulePeriodForDestinationPurge:设置多长时间检查一次,这里是10秒,默认为0 inactiveTimoutBeforeGC:设置当Destination为空后,多长时间被删除,这里是30秒,默认为60 gcInactiveDestinations: 设置删除掉不活动队列,默认为false
Destination高级特性之Destination Options
Destination Options 队列选项是给consumer在JMS规范之外添加的功能特性,通过在队列名称后面使用类似URL的语法添加多个选项。包括:
1:consumer.prefetchSize,consumer持有的未确认最大消息数量,默认值 variable 2:consumer.maximumPendingMessageLimit:用来控制非持久化的topic在存在慢消费者的情况下,丢弃的数量,默认0
3:consumer.noLocal :默认false
4:consumer.dispatchAsync :是否异步分发 ,默认true
5:consumer.retroactive:是否为回溯消费者 ,默认false
6:consumer.selector:Jms的Selector,默认null
7:consumer.exclusive:是否为独占消费者 ,默认false
8:consumer.priority:设置消费者的优先级,默认0
使用示例:
queue = new ActiveMQQueue("TEST.QUEUE?consumer.dispatchAsync=false&consumer.prefetchSize=10");
co nsumer = session.createConsumer(queue);
Destination高级特性之Visual Destinations
概述
虚拟Destinations用来创建逻辑Destinations,客户端可以通过它来生产和消费消
息,它会把消息映射到物理Destinations。ActiveMQ支持两种方式:
1:虚拟主题(Virtual Topics)
2:组合 Destinations(Composite Destinations)
为何使用虚拟主题 ActiveMQ中,topic只有在持久订阅下才是持久化的。
持久订阅时,每个持久订阅者,都相当于一个queue的客户端,它会收取所有消息。这种情况下存在两个问题:
1:同一应用内consumer端负载均衡的问题:也即是同一个应用上的一个持久订阅不能使用多 个consumer来共同承担消息处理功能。因为每个consumer都会获取所有消息。 queue模式可以解决这个问题,但broker端又不能将消息发送到多个应用端。所以, 既要发布订阅,又要让消费者分组,这个功能JMS规范本身是没有的。
2:同一应用内consumer端failover的问题:由于只能使用单个的持久订阅者,如果这个订阅者出错,则应用就无法处理消息了,系统的健壮性不高
为了解决这两个问题,ActiveMQ中实现了虚拟Topic的功能
如何使用虚拟主题
1:对于消息发布者来说,就是一个正常的Topic,名称以VirtualTopic.开头。例如 VirtualTopic.Orders,代码示例如下:
Topic destination = session.createTopic("VirtualTopic.Orders");
2:对于消息接收端来说,是个队列,不同应用里使用不同的前缀作为队列的名称,即可表明 自己的身份即可实现消费端应用分组。
例如Consumer.A.VirtualTopic.Orders,说明它是名称为A的消费端,同理 Consumer.B.VirtualTopic.Orders说明是一个名称为B的客户端。可以在同一个应用里使用 多个consumer消费此queue,则可以实现上面两个功能。 又因为不同应用使用的queue名称不同(前缀不同),所以不同的应用中都可以接收 到全部的消息。每个客户端相当于一个持久订阅者,而且这个客户端可以使用多个消费者 共同来承担消费任务。
代码示例如下:
Destination destination = session.createQueue("Consumer.A.VirtualTopic.Orders");
3:默认虚拟主题的前缀是 :VirtualTopic.> 自定义消费虚拟地址默认格式:Consumer.*.VirtualTopic.> 自定义消费虚拟地址可以改,比如下面的配置就把它修改了。 xml配置示例如下:
<broker xmlns="http://activemq.apache.org/schema/core">
<destinationInterceptors>
<virtualDestinationInterceptor>
<virtualDestinations>
<virtualTopic name=">" prefix="VirtualTopicConsumers.*." selectorAware="false"/>
</virtualDestinations>
</virtualDestinationInterceptor>
</destinationInterceptors>
</broker>
Destination高级特性之Mirrored Queues
概述
ActiveMQ中每个queue中的消息只能被一个consumer消费。然而,有时候你可能希望
能够监视生产者和消费者之间的消息流。你可以通过使用Virtual Destinations 来建立一 个virtual queue 来把消息转发到多个queues中。但是 为系统中每个queue都进行如此的 配置可能会很麻烦。
使用
ActiveMQ支持Mirrored Queues。Broker会把发送到某个queue的所有消息转发到一个名称类似的topic,因此监控程序只需要订阅这个mirrored queue topic。为了启用 Mirrored Queues,首先要将BrokerService的useMirroredQueues属性设置成true,然后可 以通过destinationInterceptors设置其它属性,如mirror topic的前缀,缺省是 “VirtualTopic.Mirror.”。
比如修改后缀的配置示例如下:
<destinationInterceptors>
<mirroredQueue copyMessage="true" postfix=".qmirror" prefix=""/>
</destinationInterceptors>
Message Dispatch高级特性之Message Cursors
概述
ActiveMQ发送持久消息的典型处理方式是:当消息的消费者准备就绪时,消息发送系统把存储的
消息按批次发送给消费者,在发送完一个批次的消息后,指针的标记位置指向下一批次待发送消息的位 置,进行后续的发送操作。这是一种比较健壮和灵活的消息发送方式,但大多数情况下,消息的消费者 不是一直处于这种理想的活跃状态。
因此,从ActiveMQ5.0.0版本开始,消息发送系统采用一种混合型的发送模式,当消息消费者处 理活跃状态时,允许消息发送系统直接把持久消息发送给消费者,当消费者处于不活跃状态下,切换使 用Cursors来处理消息的发送。
当消息消费者处于活跃状态并且处理能力比较强时,被持久存储的消息直接被发送到与消费者关联的发 送队列,见下图
当消息已经出现积压,消费者再开始活跃;或者消费者的消费速度比消息的发送速度慢 时,消息将从Pending Cursor中提取,并发送与消费者关联的发送队列。见下图
Message Cursors分成三种类型 1:Store-based
2:VM
3:File-based
Store-based 从activemq5.0开始,默认使用此种类型的cursor,其能够满足大多数场景的使用要
求。同时支持非持久消息的处理,Store-based内嵌了File-based的模式,非持久消息直接 被Non-Persistent Pending Cursor所处理。工作模式见下图
VM
相关的消息引用存储在内存中,当满足条件时,消息直接被发送到消费者与之相关
的发送队列,处理速度非常快,但出现慢消费者或者消费者长时间处于不活跃状态的情况 下,无法适应。工作模式见下图 :
File-based 当内存设置达到设置的限制,消息被存储到磁盘中的临时文件中。
工作模式见下
配置使用
在缺省情况下,ActiveMQ会根据使用的Message Store来决定使用何种类型的Message Cursors,
但是你可以根据destination来配置Message Cursors,例如:
1:对Topic subscribers
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic="org.apache.>" producerFlowControl="false" memoryLimit="1mb">
<dispatchPolicy>
<strictOrderDispatchPolicy />
</dispatchPolicy>
<deadLetterStrategy>
<individualDeadLetterStrategy topicPrefix="Test.DLQ." />
</deadLetterStrategy>
<pendingSubscriberPolicy>
<vmCursor />
</pendingSubscriberPolicy>
<pendingDurableSubscriberPolicy>
<vmDurableCursor/>
</pendingDurableSubscriberPolicy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
配置说明:有效的Subscriber类型是vmCursor和fileCursor,缺省是store based cursor。有效的持久 化Subscriber的cursor types是storeDurableSubscriberCursor, vmDurableCursor 和 fileDurableSubscriberCursor,缺省是store based cursor。
对于Queues的配置
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry queue="org.apache.>">
<deadLetterStrategy>
<individualDeadLetterStrategy queuePrefix="Test.DLQ."/>
</deadLetterStrategy>
<pendingQueuePolicy>
<vmQueueCursor />
</pendingQueuePolicy>
</policyEntry>
</policyEntries>
</policyMap>
</destinationPolicy>
配置说明:有效的类型是storeCursor, vmQueueCursor 和 fileQueueCursor
Message Dispatch高级特性之Async Sends
Async Sends AciveMQ支持异步和同步发送消息,是可以配置的。通常对于快的消费者,是直接把消息同步发送过去,但对于一个Slow Consumer,你使用同步发送消息,可能出现Producer堵塞等现象,慢消费者适合使用异步发送。
配置使用
1:ActiveMQ默认设置dispatcheAsync=true是最好的性能设置。如果你处理的是 Fast Consumer则使用dispatcheAsync=false
2:在Connection URI级别来配置使用Async Send
cf = new ActiveMQConnectionFactory("tcp://locahost:61616?jms.useAsyncSend=true");
3:在ConnectionFactory级别来配置使用Async Send ((ActiveMQConnectionFactory)connectionFactory).setUseAsyncSend(true);
4:在Connection级别来配置使用Async Send ((ActiveMQConnection)connection).setUseAsyncSend(true);
Message Dispatch高级特性之Dispatch Policies
严格顺序分发策略(Strict Order Dispatch Policy)
通常ActiveMQ会保证topic consumer以相同的顺序接收来自同一个producer的消息,但有时候也需要保证不同的topic consumer以相同的顺序接收消息,然而,由于多线 程和异步处理,不同的topic consumer可能会以不同的顺序接收来自不同producer的消息,Strict order dispatch policy 会保证每个topic consumer会以相同的顺序接收消息,代价是性能上的损失。
以下是一个配置例子:
<policyEntry topic="ORDERS.>">
<dispatchPolicy>
<strictOrderDispatchPolicy />
</dispatchPolicy>
</policyEntry>
对于Queue的配置为:
<policyEntry queue=">" strictOrderDispatch="false" />
轮询分发策略(Round Robin Dispatch Policy) ActiveMQ的prefetch缺省参数是针对处理大量消息时的高性能和高吞吐量而设置的,所以缺省的prefetch参数比较大。而且缺省的dispatch policies会尝试尽可能快的填 满prefetch缓冲。然而在有些情况下,例如只有少量的消息而且单个消息的处理时间比较长,那么在 缺省的prefetch和dispatch policies下,这些少量的消息总是倾向于被分发到个别的 consumer上。这样就会因为负载的不均衡分配而导致处理时间的增加。
Round robin dispatch policy会尝试平均分发消息,以下是一个例子:
<policyEntry topic="ORDERS.>">
<dispatchPolicy>
<roundRobinDispatchPolicy/>
</dispatchPolicy>
</policyEntry>
Message Dispatch高级特性之Optimized Acknowledgement
ActiveMQ缺省支持批量确认消息,由于批量确认会提高性能。如果希望在应用程序中禁止 经过优化的确认方式,那么可以采用如下方法:
1:在Connection URI 上启用Optimized Acknowledgements cf = new
ActiveMQConnectionFactory("tcp://locahost:61616?jms.optimizeAcknowledge=true");
2:在ConnectionFactory 上启用Optimized Acknowledgements ((ActiveMQConnectionFactory)connectionFactory).setOptimizeAcknowledge(true);
3:在Connection上启用Optimized Acknowledgements ((ActiveMQConnection)connection).setOptimizeAcknowledge(true);
4:5.6以后的版本,还可以在Connection URI上设置setOptimizeAcknowledgeTimeOut参数, 默认值为300ms,你可以设置自己要用的值,0表示禁用。
Message Dispatch高级特性之Producer Flow Control
生产者流量控制(Producer Flow Control) 流量控制的含义:当生产者产生消息过快,超过流量限制的时候,生产者将会被阻塞直到资源可以继续使用,或者抛出一个JMSException,可以通过<systemUsage>来配置。
同步发送消息的producer会自动使用producer flow control ;对于异步发送消息的producer,要使用producer flow control,你先要为connection配置一个 ProducerWindowSize参数,如下:
((ActiveMQConnectionFactory)cf).setProducerWindowSize(1024000);
ProducerWindowSize是producer在发送消息的过程中,收到broker对于之前发送消 息的确认之前, 能够发送消息的最大字节数
可以禁用producer flow control,以下是ActiveMQ配置文件的一个例子
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic="FOO.>" producerFlowControl="false"/>
</policyEntries>
</policyMap>
</destinationPolicy>
注意,自从ActiveMQ 5.x中引入新的消息游标之后,非持久化消息被分流到了临时文件存 储中,以此来减少非持久化消息传送使用的内存总量。
结果就是,你可能会发现一个队列的内存限制永远达不到,因为游标不需要使用太多的内存。如果你真的想把所有的非持久化消息存放在内存中,并在达到内存限制的时候停掉生产者,你需要配置<vmQueueCursor>,
示例如下:
<policyEntry queue=">" producerFlowControl="true" memoryLimit="1mb">
<pendingQueuePolicy>
<vmQueueCursor/>
</pendingQueuePolicy>
</policyEntry>
上面的配置可以保证所有的非持久化队列消息都保存在内存中,每一个队列的内存限制为1Mb
配置客户端的异常为了应对代理空间不足,而导致不确定的阻塞send()方法的一种替代方案,就是将
其配置成客户端抛出的一个异常。通过将sendFailIfNoSpace属性设置为true,代理将会引 起send()方法失败,并抛出javax.jms.ResourceAllocationException异常,传播到客户 端。
下面是一个配置的示例:
<systemUsage>
<systemUsage sendFailIfNoSpace="true">
<memoryUsage>
<memoryUsage limit="20 mb"/>
</memoryUsage>
</systemUsage>
</systemUsage>
这么配置的好处是,客户端可以捕获javax.jms.ResourceAllocationException异常,稍等一
下,并重试send()操作,而不是无限期地傻等下去。
从5.3.1版本之后,sendFailIfNoSpaceAfterTimeout属性被加了进来。这个属性同样导致send()方法失
败,并在客户端抛出异常,但仅当等待了指定时间之后才触发。如果在配置的等待时间过去之后,代理
上的空间仍然没有被释放,仅当这个时候send()方法才会失败,并且在客户端抛出异常。示例:
<systemUsage>
<systemUsage sendFailIfNoSpaceAfterTimeout="3000">
<memoryUsage>
<memoryUsage limit="20 mb"/>
</memoryUsage>
</systemUsage>
</systemUsage>
定义超时的单位是毫秒
System usage
可以通过<systemUsage>元素的一些属性来减慢生产者,
如下例子:
<systemUsage>
<systemUsage>
<memoryUsage>
<memoryUsage limit="64 mb" />
</memoryUsage>
<storeUsage>
<storeUsage limit="100 gb" />
</storeUsage>
<tempUsage>
<tempUsage limit="10 gb" />
</tempUsage>
</systemUsage>
</systemUsage>
你可以为非持久化的消息设置内存限制,为持久化消息设置磁盘空间,以及为临时 消息设置总的空间,broker将在减慢生产者之前使用这些空间。使用上述的默认设置, broker将会一直阻塞send()方法的调用,直至一些消息被消费,有了可用的空间。
Message高级特性之Message Properties
ActiveMQ支持很多消息属性,具体可以参见 http://activemq.apache.org/activemq-message-properties.html
常见的一些属性说明
1:Queue的消息默认是持久化的
2:消息的优先级默认是4
3:消息发送时设置了时间戳
4:消息的过期时间默认是永不过期,过期的消息进入DLQ,可以配置DLQ及其处理策略
5:如果消息时重发的,将会标记出来
6:JMSReplyTo标识响应消息发送到哪个Queue
7:JMSCorelationID标识此消息相关联的消息id,可以用这个标识把多个消息连接起来
8:JMS同时也记录了消息重发的次数,默认是6次
9:如果有一组关联的消息需要处理,可以分组:只需要设置消息组的名字和这个消息是第几个消息
10:如果消息中一个事务环境,则TXID将被设置
11:此外ActiveMQ在服务器端额外设置了消息入列和出列的时间戳
12:ActiveMQ里消息属性的值,不仅可以用基本类型,还可以用List或Map类型
Message高级特性之Advisory Message
Advisory Message是ActiveMQ自身的系统消息地址,可以监听该地址来获取activemq的系统信息。目前 支持获取如下信息:
1:consumers, producers 和 connections的启动和停止
2:创建和销毁temporary destinations
3:topics 和 queues的消息过期
4:brokers 发送消息给 destinations,但是没有consumers
5:connections 启动和停止
几点说明:
1:所有Advisory的topic,前缀是:ActiveMQ.Advisory
2:所有Advisory的消息类型是:‘Advisory’ ,所有的Advisory都有的消息属性有:originBrokerId、
originBrokerName、originBrokerURL 3:具体支持的topic和queue,请参看http://activemq.apache.org/advisory-message.html
打开Advisories , 默认Advisory的功能是关闭的
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic=">" advisoryForConsumed="true" />
</policyEntries>
</policyMap>
</destinationPolicy>
关闭Advisories , 有好几种方法 1:<broker advisorySupport="false"> 2:也可在Java中写:
BrokerService broker = new BrokerService();
broker.setAdvisorySupport(false);
...
broker.start();
3:也可以在ActiveMQConnectionFactory上设置‘watchTopicAdvisories’ 属性 ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(); factory.setWatchTopicAdvisories(false);
4:也可在ConnectionURl上写: "tcp://localhost:61616?jms.watchTopicAdvisories=false"
使用的方法和步骤:
1:要在配置文件里面开启Advisories
2:消息发送端没有变化
3:消息接收端:
(1)根据你要接收的信息类型,来设置不同的topic,当然也可以使用AdvisorySupport这个类来辅助创
建,比如你想要得到消息生产者的信息,你可以:
Topic d=session.createTopic("ActiveMQ.Advisory.Producer.Topic.MyTopic"); 也可以使用: Topic d = session.createTopic("MyTopic");
Destination d2 = AdvisorySupport.getProducerAdvisoryTopic(destination);
(2)由于这个topic默认不是持久化的,所以应该先开启接收端,然后再发送topic信息
(3)接收消息的时候,接收到的消息类型是ActiveMQMessage,所以类型转换的时候,要转换成
ActiveMQMessage,然后再通过getDataStructure方法来得到具体的信息对象,如:
if(message instanceof ActiveMQMessage){
try {
ActiveMQMessage aMsg = (ActiveMQMessage) message;
ProducerInfo prod = (ProducerInfo) aMsg.getDataStructure();
System.out.println("count===" + aMsg.getProperty("producerCount"));
System.out.println(" prodd===" + prod.getProducerId());
} catch (Exception e) {
e.printStackTrace();
}
}
Message高级特性之延迟和定时消息投递
延迟和定时消息投递(Delay and Schedule Message Delivery) 有时候我们不希望消息马上被broker投递出去,而是想要消息60秒以后发给消费者,或者我们想让消息没隔一定时间投递一次,一共投递指定的次数。。。类似这种需求,ActiveMQ提供了一种broker 端消息定时调度机制。 我们只需要把几个描述消息定时调度方式的参数作为属性添加到消息,broker端的调度器就会按 照我们想要的行为去处理消息。当然需要在xml中配置schedulerSupport属性为true
一共有4个属性
1:AMQ_SCHEDULED_DELAY :延迟投递的时间
2:AMQ_SCHEDULED_PERIOD :重复投递的时间间隔
3:AMQ_SCHEDULED_REPEAT:重复投递次数
4:AMQ_SCHEDULED_CRON:Cron表达式
ActiveMQ也提供了一个封装的消息类型:org.apache.activemq.ScheduledMessage,可以使用这个类来
辅助设置,使用例子如:延迟60秒
MessageProducer producer = session.createProducer(destination); TextMessage message = session.createTextMessage("test msg");
long time = 60 * 1000; message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, time); producer.send(message);
例子:延迟30秒,投递10次,间隔10秒:
TextMessage message = session.createTextMessage("test msg");
long delay = 30 * 1000;
long period = 10 * 1000;
int repeat = 9;
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, delay); message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, period); message.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, repeat);
例子:使用 CRON 表达式,每个小时发送一次
TextMessage message = session.createTextMessage("test msg"); message.setStringProperty(ScheduledMessage.AMQ_SCHEDULED_CRON, "0 * * * *");
CRON表达式的优先级高于另外三个参数,如果在设置了CRON的同时,也有repeat和period参数,
则会在每次CRON执行的时候,重复投递repeat次,每次间隔为period。就是说设置是叠加的效果。例如
每小时都会发生消息被投递10次,延迟1秒开始,每次间隔1秒:
TextMessage message = session.createTextMessage("test msg"); message.setStringProperty(ScheduledMessage.AMQ_SCHEDULED_CRON, "0 * * * *"); message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, 1000); message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD, 1000); message.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, 9);
Message高级特性之Blob Messages
有些时候,我们需要传递Blob(Binary Large OBjects)消息,可以按照如下方式: n 配置BLOB Transfer Policy,可以在发送方的连接URI上设置,如:
"tcp://192.168.1.106:61679?jms.blobTransferPolicy.uploadUrl=http://192.168.1.10
6:8171/fileserver/"
Sending BlobMessages,有几种方式:
1:如果你发送到的文件或者URL存在,比如发给共享文件系统或者是Web server上的web应用,那么你可以使用如下方式:
BlobMessage message = session.createBlobMessage(new URL("http://some.shared.site.com"); producer.send(message);
2:也可以在客户端动态的创建文件流,如下:
BlobMessage message = session.createBlobMessage(new File("/foo/bar"); 或者:
InputStream in = ...;
BlobMessage message = session.createBlobMessage(in);
Receiving BlobMessages示例:
if(message instanceof BlobMessage){
BlobMessage blobMessage = (BlobMessage) message;
InputStream in = blobMessage.getInputStream();
// process the stream...
}
Message高级特性之Message Transformation
有时候需要在JMS provider内部进行message的转换。从4.2版本起,ActiveMQ 提供了一个MessageTransformer 接口用于进行消息转换,可以在如下对象上调 用:
ActiveMQConnectionFactory、ActiveMQConnection、ActiveMQSession、ActiveMQMessageConsumer、ActiveMQMessageProducer
在消息被发送到JMS provider的消息总线前进行转换。通过producerTransform 方法
在消息到达消息总线后,但是在consumer接收到消息前进行转换。通过 consumerTransform方法
当然MessageTransformer 接口的实现,需要你自己来提供
Consumer高级特性之Exclusive Consumer
独有消费者(Exclusive Consumer) Queue中的消息是按照顺序被分发到consumers的。然而,当你有多个consumers同时从相同的queue中提取消息时,你将失去这个保证。因为这些消息是被多个线程并发的处 理。有的时候,保证消息按照顺序处理是很重要的。例如,你可能不希望在插入订单操作 结束之前执行更新这个订单的操作。
ActiveMQ从4.x版本起开始支持Exclusive Consumer。 Broker会从多个consumers中 挑选一个consumer来处理queue中所有的消息,从而保证了消息的有序处理。如果这个 consumer失效,那么broker会自动切换到其它的consumer。 可以通过Destination Options 来创建一个Exclusive Consumer,
如下:
queue = new ActiveMQQueue("TEST.QUEUE?consumer.exclusive=true");
consumer = session.createConsumer(queue);
还可以给consumer设置优先级,以便针对网络情况进行优化,如下:
queue = new ActiveMQQueue("TEST.QUEUE?consumer.exclusive=true&consumer.priority=10");
Consumer高级特性之Consumer Dispatche Async
在activemq4.0以后,你可以选择broker同步或异步的把消息分发给消费者。可以设置 dispatchAsync 属性,默认是true,通常情况下这是最佳的。
你也可以修改,可以通过如下几种方式 1:在ConnectionFactory层设置
((ActiveMQConnectionFactory)connectionFactory).setDispatchAsync(false);
2:在Connection上设置这个设置将会覆盖ConnectionFactory上的设置
((ActiveMQConnection)connection).setDispatchAsync(false);
3:在Consumer来设置
queue = new ActiveMQQueue("TEST.QUEUE?consumer.dispatchAsync=false");
consumer = session.createConsumer(queue);
Consumer高级特性之Consumer Priority
JMS JMSPriority 定义了十个消息优先级值,0 是最低的优先级,9 是最高的优先级。另 外,客户端应当将0‐4 看作普通优先级,5‐9 看作加急优先级。
如何定义Consumer Priority的优先级呢? 配置如下:
queue = new ActiveMQQueue("TEST.QUEUE?consumer.priority=10");
consumer = session.createConsumer(queue);
Consumer的Priority的划分为0~127个级别,127是最高的级别,0是最低的也是ActiveMQ默认的。 这种配置可以让Broker根据Consumer的优先级来发送消息先到较高的优先级的 Consumer上,如果某个较高的Consumer的消息装载慢,则Broker会把消息发送到仅次于它 优先级的Consumer上。
Consumer高级特性之Manage Durable Subscribers
消息的持久化,保证了消费者离线后,再次进入系统,不会错过消息,但是这也会消耗很 多的资源。从5.6开始,可以对持久化消息进行如下管理:
Removing inactive subscribers 我们还可能希望删除那些不活动的订阅者,如下:
<broker name="localhost" offlineDurableSubscriberTimeout="86400000" offlineDurableSubscriberTaskSchedule="3600000">
1: offlineDurableSubscriberTimeout:离线多长时间就过期删除,缺省是-1,就是不删除
2: offlineDurableSubscriberTaskSchedule:多长时间检查一次,缺省300000,单位毫秒
Consumer高级特性之Message Groups
Message Groups就是对消息分组,它是Exclusive Consumer功能的增强: 逻辑上,Message Groups 可以看成是一种并发的Exclusive Consumer。跟所有的消
息都由唯一的consumer处理不同,JMS 消息属性JMSXGroupID 被用来区分message group。 Message Groups特性保证所有具有相同JMSXGroupID 的消息会被分发到相同的consumer (只要这个consumer保持active)。
另外一方面,Message Groups特性也是一种负载均衡的机制。 在一个消息被分发到 consumer之前,broker首先检查消息JMSXGroupID属性。如果存在,那么broker 会检查是 否有某个consumer拥有这个message group。如果没有,那么broker会选择一个consumer, 并将它关联到这个message group。此后,这个consumer会接收这个message group的所有 消息,直到:
1:Consumer被关闭
2:Message group被关闭,通过发送一个消息,并设置这个消息的JMSXGroupSeq为-1
创建一个Message Groups,只需要在message对象上设置属性即可,如下: message.setStringProperty("JMSXGroupID","GroupA");
关闭一个Message Groups,只需要在message对象上设置属性即可,如下: message.setStringProperty("JMSXGroupID","GroupA"); message.setIntProperty("JMSXGroupSeq", -1);
Consumer高级特性之Message Selectors
JMS Selectors用在获取消息的时候,可以基于消息属性和Xpath语法对消息进行过滤。JMS Selectors 由SQL92语义定义。以下是个Selectors的例子:
consumer = session.createConsumer(destination, "JMSType = 'car' AND weight > 2500");
1:JMS Selectors表达式中,可以使用IN、NOT IN、LIKE等
2:需要注意的是,JMS Selectors表达式中的日期和时间需要使用标准的long型毫秒值
3:表达式中的属性不会自动进行类型转换,例如:
myMessage.setStringProperty("NumberOfOrders", "2"); 那么此时“NumberOfOrders > 1” 求值结果会是false
4:Message Groups虽然可以保证具有相同message group的消息被唯一的consumer顺序处理,但是却不能确 定被哪个consumer处理。在某些情况下,Message Groups可以和JMS Selector一起工作,
例如:设想有三个consumers分别是A、B和C。你可以在producer中为消息设置三个message groups分别是“A”、“B”和“C”。然后令consumer A使用“JMXGroupID = ‘A’”作为selector。B 和C也同理。这样就可以保证message group A的消息只被consumer A处理。需要注意的是,
这种做法有 以下缺点:
(1)producer必须知道当前正在运行的consumers,也就是说producer和consumer被耦合到一起。
(2)如果某个consumer失效,那么应该被这个consumer消费的消息将会一直被积压在broker上。
Consumer高级特性之Redelivery Policy
ActiveMQ在接收消息的Client有以下几种操作的时候,需要重新传递消息: 1:Client用了transactions,且在session中调用了rollback() 2:Client用了transactions,且在调用commit()之前关闭 3:Client在CLIENT_ACKNOWLEDGE的传递模式下,在session中调用了recover()
可以通过设置ActiveMQConnectionFactory和ActiveMQConnection来定制想要的再次传送策略,可用的
Redelivery属性如下:
1:collisionAvoidanceFactor:设置防止冲突范围的正负百分比,只有启用useCollisionAvoidance参数时
才生效。也就是在延迟时间上再加一个时间波动范围。默认值为0.15
2:maximumRedeliveries:最大重传次数,达到最大重连次数后抛出异常。为-1时不限制次数,为0时表示
不进行重传。默认值为6。
3:maximumRedeliveryDelay:最大传送延迟,只在useExponentialBackOff为true时有效(V5.5),假设首
次重连间隔为10ms,倍数为2,那么第二次重连时间间隔为 20ms,第三次重连时间间隔为40ms,当重连
时间间隔大的最大重连时间间隔时,以后每次重连时间间隔都为最大重连时间间隔。默认为-1。 4:initialRedeliveryDelay:初始重发延迟时间,默认1000L
5:redeliveryDelay:重发延迟时间,当initialRedeliveryDelay=0时生效,默认1000L 6:useCollisionAvoidance:启用防止冲突功能,默认false
7:useExponentialBackOff:启用指数倍数递增的方式增加延迟时间,默认false
8:backOffMultiplier:重连时间间隔递增倍数,只有值大于1和启用useExponentialBackOff参数时才生
效。默认是5
在接受的Client可以如下设置:
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory( "failover:(tcp://192.168.1.106:61679,tcp://192.168.1.106:61819)?randomize=false");
RedeliveryPolicy policy = new RedeliveryPolicy(); policy.setMaximumRedeliveries(3); cf.setRedeliveryPolicy(policy);
当消息试图被传递的次数超过配置中maximumRedeliveries属性的值时,那么,broker会认 定该消息是一个死消息,并被把该消息发送到死队列中。 默认,aciaveMQ中死队列被声明 为“ActivemMQ.DLQ”,所有不能消费的消息都被传递到该死队列中。 你可以在
acivemq.xml中配置individualDeadLetterStrategy属性,示例如下:
<policyEntry queue= "> " >
<deadLetterStrategy>
<individualDeadLetterStrategy queuePrefix= "DLQ." useQueueForQueueMessages= "true" />
</deadLetterStrategy>
</policyEntry>
自动删除过期消息 有时需要直接删除过期的消息而不需要发送到死队列中,可以使用属性
processExpired=false来设置,示例如下:
<policyEntry queue= "> " >
<deadLetterStrategy>
<sharedDeadLetterStrategy processExpired= "false" />
</deadLetterStrategy>
</policyEntry>
存放非持久消息到死队列中 默认情况下,Activemq不会把非持久的死消息发送到死队列中。非持久性如果你想把非持久的消息发送到死队列中,需要设置属性processNonPersistent=“true”,示例如
下:
<policyEntry queue= "> " >
<deadLetterStrategy>
<sharedDeadLetterStrategy processNonPersistent= "true" />
</deadLetterStrategy>
</policyEntry>
Redelivery Policy per Destination
在V5.7之后,你可以为每一个Destination配置一个Redelivery Policy。示例如: ActiveMQConnection connection ... // Create a connection
RedeliveryPolicy queuePolicy = new RedeliveryPolicy(); queuePolicy.setInitialRedeliveryDelay(0);
queuePolicy.setRedeliveryDelay(1000); queuePolicy.setUseExponentialBackOff(false); queuePolicy.setMaximumRedeliveries(2);
RedeliveryPolicy topicPolicy = new RedeliveryPolicy(); topicPolicy.setInitialRedeliveryDelay(0); topicPolicy.setRedeliveryDelay(1000); topicPolicy.setUseExponentialBackOff(false); topicPolicy.setMaximumRedeliveries(3);
// Receive a message with the JMS API
RedeliveryPolicyMap map = connection.getRedeliveryPolicyMap(); map.put(new ActiveMQTopic(">"), topicPolicy);
map.put(new ActiveMQQueue(">"), queuePolicy);
Consumer高级特性之Slow Consumer Handling
Prefetch机制 ActiveMQ通过Prefetch机制来提高性能,方式是在客户端的内存里可能会缓存一定
数量的消息。缓存消息的数量由prefetch limit来控制。当某个consumer的prefetch buffer已经达到上限,那么broker不会再向consumer分发消息,直到consumer向broker发 送消息的确认,确认后的消息将会从缓存中去掉。
可以通过在ActiveMQConnectionFactory或者ActiveMQConnection上设置 ActiveMQPrefetchPolicy对象来配置prefetch policy。也可以通过connection options或 者destination options来配置。例如:
tcp://localhost:61616?jms.prefetchPolicy.all=50 tcp://localhost:61616?jms.prefetchPolicy.queuePrefetch=1
queue = new ActiveMQQueue("TEST.QUEUE?consumer.prefetchSize=10");
n prefetch size的缺省值如下:
1:persistent queues (default value: 1000) 2:non-persistent queues (default value: 1000)
3:persistent topics (default value: 100)
4:non-persistent topics (default value: Short.MAX_VALUE -1)
慢Consumer处理 慢消费者会在非持久的topics上导致问题:一旦消息积压起来,会导致broker把大量消息保存在内存中,broker也会因此而变慢。目前ActiveMQ使用Pending Message Limit Strategy来解决这个问 题。除了prefetch buffer之外,你还要配置缓存消息的上限,超过这个上限后,新消息到来时会丢弃 旧消息。 通过在配置文件的destination map中配置PendingMessageLimitStrategy,可以为不用的topic namespace配置不同的策略。
Pending Message Limit Strategy(等待消息限制策略)目前有以下两种: 1: Constant Pending Message Limit Strategy Limit可以设置0、>0、-1三种方式:
0表示:不额外的增加其预存大小。
>0表示:再额外的增 加其预存大小。
-1表示:不增加预存也不丢弃旧的消息。
这个策略使用常量限制,
配置如下: <constantPendingMessageLimitStrategy limit="50"/>
2:Prefetch Rate Pending Message Limit Strategy 这种策略是利用Consumer的之前的预存的大小乘以其倍数等于现在的预存大小。
比如: <prefetchRatePendingMessageLimitStrategy multiplier="2.5"/>
3:说明:在以上两种方式中,如果设置0意味着除了prefetch之外不再缓存消息;如果设置-1意味着禁止丢
弃消息。
配置消息的丢弃策略,目前有三种方式: 1:oldestMessageEvictionStrategy:这个策略丢弃最旧的消息。 2:oldestMessageWithLowestPriorityEvictionStrategy:这个策略丢弃最旧的,而且具有最
低优先级的消息。 3:uniquePropertyMessageEvictionStrategy:从5.6开始,可以根据自定义的属性来进行抛弃,比如<uniquePropertyMessageEvictionStrategy propertyName=“STOCK” />,这就
表示抛弃属性名称为Stock的消息
配置示例:
<destinationPolicy>
<policyMap>
<policyEntries>
<policyEntry topic="FOO.>">
<dispatchPolicy>
<roundRobinDispatchPolicy />
</dispatchPolicy>
</policyEntry>
<policyEntry topic="ORDERS.>"> <dispatchPolicy>
<strictOrderDispatchPolicy />
</dispatchPolicy>
</policyEntry>
<policyEntry topic="PRICES.>">
<!-- lets force old messages to be discarded for slow consumers -->
<pendingMessageLimitStrategy>
<constantPendingMessageLimitStrategy limit="10"/>
</pendingMessageLimitStrategy>
</policyEntry>
<policyEntry tempTopic="true" advisoryForConsumed="true" />
<policyEntry tempQueue="true" advisoryForConsumed="true" />
</policyEntries>
</policyMap>
</destinationPolicy>
监控和管理Broker
Web Console方式 直接访问ActiveMQ的管理页面:http://192.168.1.106:8161/admin/,
默认的用户名和密码是admin/admin。具体配置在conf/jetty.xml里面
Hawtio-web Management Console方式 默认的用户名密码是admin/admin
JMX方式
集成ActiveMQ和Tomcat
ActiveMQ和Tomcat可以很自如的集成到一起使用,而不需要使用JNDI的方式,启动Tomcat的时候就可以 启动ActiveMQ,方式如下:
1:修改web.xml
<context-param>
<param-name>brokerURI</param-name>
<param-value>/WEB-INF/activemq.xml</param-value>
</context-param>
<listener>
<listener-class>org.apache.activemq.web.SpringBrokerContextListener</listener-class>
</listener>
2:增加WEB-INF/activemq.xml,这里给个最简单的
<beans
xmlns="http://www.springframework.org/schema/beans" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-
5.2.0.xsd http://activemq.apache.org/camel/schema/spring
http://activemq.apache.org/camel/schema/spring/camel-spring.xsd">
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost">
<persistenceAdapter>
<kahaDB directory="/usr/common/testdata/kahadb"/>
</persistenceAdapter>
<transportConnectors>
<transportConnector name="openwire" uri="tcp://192.168.1.106:61616"/>
</transportConnectors>
</broker>
</beans>
3:在web应用下拷入activemq的jar包,在ActiveMQ下面的lib包,例如: cp -r lib /usr/...目的地址lib
4:在lib下面传入spring的包,就从前面的arch1web应用下面的lib找spring的包就可以了
5:还需要xbean,这是apache的,可以从maven依赖的仓库里面找到
6:然后就可以启动tomcat,进行测试了
什么时候使用ActiveMQ
异步调用
一对多通信
做多个系统的集成,同构、异构
作为RPC的替代
多个应用相互解耦
作为事件驱动架构的幕后支撑
为了提高系统的可伸缩性
ActiveMQ优化
ActiveMQ的性能依赖于很多因素,
比如:
1:网络拓扑结构,比如:嵌入、主从复制、网络连接
2:transport协议
3:service的质量,比如topic还是queue,是否持久化,是否需要重新投递,消息超时等
4:硬件、网络、JVM和操作系统等
5:生产者的数量,消费者的数量
6:消息分发要经过的destination数量,以及消息的大小等
非持久化消息比持久化消息更快,原因如下:
1:非持久化发送消息是异步的,Producer不需要等待Consumer的receipt消息
2:而持久化是要把消息先存储起来,然后再传递
尽量使用异步投递消息,示例如:
cf.setUseAsyncSend(true);
Transaction比Non-transaction更快
可以考虑内嵌启动broker,这样应用和Broker之间可以使用VM协议通讯,速度快
尽量使用基于文件的消息存储方案,比如使用KahaDB的方式
调整Prefetch Limit,ActiveMQ默认的prefetch大小不同的: 1:Queue Consumer 默认1000
2:Queue Browser Consumer默认500
3:Persistent Topic Consumer默认1000 4:Non-persistent Topic Consumer默认32767
Prefecth policy设置示例如下:
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(); Properties props = new Properties(); props.setProperty("prefetchPolicy.queuePrefetch", "1000"); props.setProperty("prefetchPolicy.queueBrowserPrefetch", "500"); props.setProperty("prefetchPolicy.durableTopicPrefetch", "1000"); props.setProperty("prefetchPolicy.topicPrefetch", "32767"); cf.setProperties(props);
也可以在创建Destination的时候设置prefetch size,示例如下:
Queue queue = new ActiveMQQueue("TEST.QUEUE?consumer.prefetchSize=10"); MessageConsumer consumer = session.createConsumer(queue);
可以考虑生产者流量控制,可以通过xml配置,代码开启方式如下: cf.setProducerWindowSize(1024000);
n 可以考虑关闭消息的复制功能,也能部分提高心能,在连接工厂上设置,如下: ActiveMQConnectionFaction cf = .....
cf.setCopyMessageOnSend(false);
调整TCP协议 TCP协议是ActiveMQ中最常使用的协议,常见有如下配置会影响协议性能:
1:socketBufferSize:socket的缓存大小,默认是65536
2:tcpNoDelay:默认是false
示例如:
String url = "failover://(tcp://localhost:61616?tcpNoDelay=true)";
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(url);
消息投递和消息确认
官方建议使用自动确认的模式,同时还可以开启优化确认的选项,如下: ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(); cf.setOptimizeAcknowledge(true);
在消费者这边,session会在一个单独的线程中分发消息给消费者,如果你使用的自动确认模 式,为了增加吞吐量,你可以直接通过session传递消息给消费者,示例如下: ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory(); cf.setAlwaysSessionAsync(false);
如果使用KahaDB进行消息存储的话,可以调整如下选项来优化性能:
1:indexCacheSize:默认为10000,用来设定缓存页的个数,默认情况一页是4KB,一般来说缓存的
大小尽可能的设置大一些,以避免内存不足时频繁的交换。
2:indexWriteBatchSize:默认1000,用来设置脏索引(脏索引就是cache中的index和消息存储中的
index状态不一样)达到多少之后,就需要把索引存储起来。如果你想最大化broker的速度,那 么就把这个值设置的尽可能的大一些,这样的话,仅会在到达checkpoint的时候,索引才会被存 储起来。但是这样会增大系统出错的时候,丢失大量的元数据的风险。
3:journalMaxFileLength:缺省32mb,当broker的吞吐量特别大的时候,日志文件会很快被写满, 这样会因为频繁的关闭文件,打开文件而导致性能低下。你可以通过调整文件的size,减少文件 切换的频率,从而获得轻微的性能改善。
4:enableJournalDiskSyncs:缺省为true,通常,broker会在给producer确认之前,把消息同步到 磁盘上(并且确保消息物化到磁盘上)。你可以通过设置这个选项为false,从而获得本质的性 能改善。但是这样的话,多少会降低broker的可靠性。