消息传送机制:

     Producer客户端使用来发送消息的, Consumer客户端用来消费消息;它们的协同中心就是ActiveMQ broker,broker也是让producer和consumer调用过程解耦的工具,最终实现了异步RPC/数据交换的功能。随着ActiveMQ的不断发展,支持了越来越多的特性,也解决开发者在各种场景下使用ActiveMQ的需求。比如producer支持异步调用;使用flow control机制让broker协同consumer的消费速率;consumer端可以使用prefetchACK来最大化消息消费的速率;提供"重发策略"等来提高消息的安全性等。在此我们不详细介绍。

 

起重要作用的ACK模式:

        上篇也说了一点,这回来个全的(ACK百科:确认字符)

       AUTO_ACKNOWLEDGE:consumer自动确认 确认的时间点很重要,开发者需要明确确认的具体时机,否则会丢失消息或消息重复接收;

       CLIENT_ACKNOWLEDGE:客户端自动确认,无论同步/异步,MQ均不发送STANDARD_ACK_TYPE,直到message.acknowledge调用确认消息;如果在client端未确认消息个数达到prefetchSize * 0.5,会补送一个ACK_TYPE为DELIVERED_ACK_TYPE的确认指令,这会触发broker端 可继续push消息到client端。

        DUPS_OK_ACKNOWLEDGE:"消息可重复"确认,为批量确认而生,是潜在的"AUTO_ACK"确认机制,而且具有“延迟”确认的特点。对于开发者而言,这种模式下的代码结构和AUTO_ACKNOWLEDGE一样,不需要像CLIENT_ACKNOWLEDGE那样调用acknowledge()方法来确认消息。

       SESSION_TRANSACTED:事务开启后,在session.commit()之前,所有消费的消息,要么全部正常确认,要么全部redelivery。在基于GROUP(消息分组)场景下特别适合。

        INDIVIDUAL_ACKNOWLEDGE:确认时机和CLIENT_ACKNOWLEDGE几乎一样;当消息消费成功后,调用message.acknowledege来确认此消息(单条),而CLIENT_ACKNOWLEDGE模式message.acknowledge()方法将导致整个session中所有消息被确认(批量确认)。

 

opoptimizeACK:可优化的ACK 

       consumer在消费消息时,对消息ACK的优化,开启

String brokerUrl = "tcp://localhost:61616?" +

"jms.optimizeAcknowledge=true" +

"&jms.optimizeAcknowledgeTimeOut=30000" +

"&jms.redeliveryPolicy.maximumRedeliveries=6";


ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);

 

优化的具体的参数:

1、asyncDispatch:broker是否容许使用“异步转发”

//在brokerUrl中设置  
tcp://localhost:61616?jms.dispatchAsync=true  
//在destinationUri中为当前通道设定  
orderQueue?consumer.dispatchAsync=true

activemq消费_线程池

       broker异步转发采用线程池模式,当任务单元运行时间足够短时、线程切换的成本变得很重要,如果通道中consumer接收、消息速度很快,可关闭异步避免线程切换;如consumer慢速且关闭异步,意味着每次主线程转发消息需要阻塞较长时间,最好不要关闭

 

2、alwaysSessionAsync客户端session是否使用异步转发,默认true

//在brokerUrl中设置  
tcp://localhost:61616?jms.alwaysSessionAsync=true

       底层Trancport接收到Broker消息交付Session;

       false表同步、c使用receive获msg,s将消息添到c的本地queue,唤醒receive等待,当c使用messageListener异步侦听调onMessage直到方法执行完,返回;

       true:异步,session接受消息、放入session buffer(队列),线程池负责移除buffer中方法,并转发给相应的consumer;

       session多个consumer,或transport消息量比较密集、异步最佳;如只有一个consumer或transport消息量很少,异步不能明显提升性能;

 

3、maxThreadPoolSize:true创个session共享滴线程池

//在brokerUrl中设置  
tcp://localhost:61616?jms.maxThreadPoolSize=64

        true,引入一个Connection内所有的session共享的线程池,池中线程的个数 默认为Integer.MAX_VALUE,可指定合适的值自定义线程池的大小。

 

4、useDedicatedTaskRunner:当前connetion的每个session都将创建一个单独的线程

//brokerUrl中,默认为false  
tcp://localhost:61616?jms.useDedicatedTaskRunner=true

       异步转发使用了线程池,且线程池是当前Connection下所有的session共享;有时候我们可能期望在threadLocal或者基于当前线程绑定一些特殊的数据,那么我们就可以使用useDedicatedTaskRunner=true:每个session都将创建一个单独的线程,整个生命周期中都使用它来转发消息。

        当每个session只有一个Consumer,或者有多个consumer但它们的处理业务类似(比如selector具有一定逻辑关系),或者这些consumer消费速度都很快,或者我们期望在Consumer中使用类似于Thread.sleep(long)这样的方式来阻塞session转发消息时,这个参数可以帮助我们。

 

5、redeliveryPolicy:重发策略

//在brokerUrl中设置  
tcp://localhost:61616?jms.redeliveryPolicy.maximumRedeliveries=6

        consumer使用的重发策略,当消息在client端处理失败(比如onMessage方法抛出异常,事务回滚等),将会触发消息重发。对于Broker端,需要重发的消息将会被立即发送(如果broker端使用异步发送,且发送队列中还有其他消息,那么重发的消息可能不会被立即到达Consumer)。我们通过此Policy配置最大重发次数、重发频率等,如果你的Consumer客户端处于不良网络环境中,可以适当调整相关参数。参数列表,请参见(RedeliveryPolicy)

 

6、messagePrioritySupported:Consumer端是否支持权重,默认为true

//在brokerUrl中设置  
tcp://localhost:61616?jms.messagePrioritySupported=true

       true:c接受的所有消息按照权重排序,即权重较高的消息,优先被传递给messageListener.onMessage()方法等。

       false:FIFO队列,消息达到Consumer之后将会加入到队列,忽略权重属性,不过这在消息有重发的情况下,可能有少许影响。这个参数可以配合broker端“strictOrderDispath”达成消息权重排序。[参见Priority]

 

7、priority:消费者权重,标记Consumer消费消息的优先级

//在destinationUril中设定,默认所有的consumer权重都一样,为0  
orderQueue?consumer.priority=10

      broker端将会对优先级较高的consumer,优先转发消息(优先填充pending buffer),比如Consumer1的权重为10,Consumer2的权重为5,它们的prefetch(预获取消息的buffer尺寸)都是10,那么当Broker端有12条消息,将会优先将Consumer1的buffer填充完毕(获取10条消息),Consumer2将会获得2条消息。我们通常可以对网络良好、业务简单(比如selector更加简单)的Consumer设定较高的权重。参见ConsumerInfo类。

       对于Topic而言,当指定了consumer的priority之后,还有一个可选的转发策略,用来优化消息传送优先级,理论上所有的Subscripter(订阅者)都会收到相同的消息,但是我们在broker端的转发时机上,让优先级较高的订阅者先得到消息;

 

8. optimizeAcknowledge是否开启“优化ACK选项”/optimizeAcknowledgeTimeOut****

//brokerUrl中,默认开启,timeout为300  
tcp://localhost:61616?jms.optimizeAcknowledge=true&jms.optimizeAcknowledgeTimeOut=3000

       第一个为true时,可以指定optimizeAcknowledgeTimeOut数值用来约束ACK最大延迟确认的时间,我们通过optimizeAck,可以实现可靠的批量消息确认。具体参看[ActiveMQ消息确认机制]      

 

9、prefetchSize 预获取消息数量***

//在destinationUri中指定,默认为1000  
//也可以在Topic中使用,可以优化ACK策略  
orderQueue?customer.prefetchSize=100

      当Consumer活跃时,broker将会批量发送prefetchSize条消息给Consumer,consumer也可以配合optimizeAcknowledge来批量确认它们;批量传送,极大的提高了网络传输效率,此值默认为1000。

      broker端将会根据consumer指定的prefetchSize来决定pendingBuffer的大小,prefetchSize越大,broker批量发送的消息就回越多,如果消费者消费速度较快,再配合optimizeAck,这将是相对完美的消息传送方案。

        prefetchSize也会带来一定的问题,在Queue中(Topic中没有效果),broker将使用“轮询”的方式来平衡多个消费者之间的消息传送数量。如果消费者消费速度较慢,而且prefetchSize较大,这将不利于消息量在多个消费者之间平衡。通常情况下,如果consumer数量较多,或者消费速度较慢,或者消息量较少时,我们设定prefetchSize为较小的值。

      如果开发者使用messageListener方式异步侦听消息,将不能设定prefetchSize <= 0的任何值。如果使用receive方式,且prefetchSize = 0时,将触发Client端使用Pull机制,即每次receive调用,都会向Broker端发送Pull指令,如果broker端有消息才会转发,在这种情况下,Broker不会主动Push消息给client。

 

10、noLocal:consumer是否接受本地消息

//默认为false,如果你通过此方式设定了nolocal属性  
//那么在创建consumer时指定的值将会被忽略。  
orderQueue?customer.noLocal=false  

session.createConsumer(topic,null,true)
通常在Topic中使用noLocal,以忽略本地的消息通知等

       Local:和Comsumer共享Connection实例的Producer发送的消息。

    当客户端创建connection时都会生成一个全局唯一的connectionId,当consumer、producer创建时都会向broker发送标识信息,比如consumer创建时会向broker发送一个ConsumerInfo数据包(数据包中包含ConsumerId,唯一标记一个consumer,consumerId中即包含ConnectionId),Producer同样在创建时发送携带有ProducerId的ProducerInfo数据包;最重要的是,Producer发送消息时,所有的消息都封装了MessageId属性,这个属性中将携带ProducerId,那么我们就很方便根据这些信息判断是否为Local,如果消息被持久化,connectionId也将随之。

 

11、exclusiveConsumer“排他消费者”,此参数仅对Queue有效**

//在brokerUrl中指定,默认值为false,对connectionFactory下所有的Queue有效  
tcp://localhost:61616?jms.exclusiveConsumer=true  

//在destinationUri中设定,只对当前Queue有效。  
orderQueue?consumer.exclusive=true

       如果Queue通道中,有多个consumer同时活跃时只会有一个consumer能够获取消息,对于broker而言,如果Queue中,有任意一个Consumer是“排他的”,那么消息只会转发给“exclusiveConsumer=true”的消费者;如果全部的消费者都是“排他的”,那么最新创建的consumer将会获取消息。

      通常在分布式环境中,为了避免对某些重要数据并发操作时使用此特性,比如:订单中心修改订单状态(来自各个系统的消息,都想修改订单状态,但是它们必须串行操作)。

      也可通过api的方式,为特定的Queue下的消费者设定此值

      只要全局中有任何一个Consumer设定了exclusive=true,都会导致整个Queue的所有的Consumer为“排他的”,直到此Consumer失效且其他Consumer中没有“排他的”时,才会解除排他性;