ActiveMQ在配合使用JmsTemplate的一些坑点注意事项

       1.在进行bean的JmsTemplate中设置DefaultDestination:

       有些时候我们不去指定消息的发送目标,采用默认的设置,这个时候如果调用jmsTemplate.execute((session, messageProducer) ->{}且在里面实现messageProducer.send(queue,message);将会出现以下错误

java.lang.UnsupportedOperationException: This producer can only send messages to: defaultQueue
	at org.apache.activemq.ActiveMQMessageProducer.send(ActiveMQMessageProducer.java:267) ~[activemq-client-5.15.8.jar:5.15.8]
	at org.apache.activemq.ActiveMQMessageProducer.send(ActiveMQMessageProducer.java:223) ~[activemq-client-5.15.8.jar:5.15.8]
	at org.springframework.jms.connection.CachedMessageProducer.send(CachedMessageProducer.java:160) ~[spring-jms-5.1.5.RELEASE.jar:5.1.5.RELEASE]

       为了不影响其他方法的调用,我们肯定是不能去在初始化bean的JmsTemplate中删除了DefaultDestination,这个时候我们可以在自己定义的方法里把DefaultDestination给删除了,即如下:

jmsTemplate.setDefaultDestination(null);

       2.消息优先级设置

      针对每个 消息优先级的发送只能在使用原生的消息发送才起作用,jmsTemplate.execute((session, messageProducer) ->{},在messageProducer中设置优先级才可以产生效果。优先级的设置范围是0-9。如果需要在整个项目设置可以通过jmsTemplate进行如下设置:

// 使用QosSettings值来提供默认的配置
QosSettings qosSettings = new QosSettings(DeliveryMode.PERSISTENT, 8, 2000);
jmsTemplate.setQosSettings(qosSettings);
// 启用QosSettings配置
jmsTemplate.setExplicitQosEnabled(true);

       3.时间戳的设置

       如果在定义jmsTemplate的时候setMessageTimestampEnabled为true,那么自定义设置message.setJMSExpiration(2000);是不起作用的,默认情况下setMessageTimestampEnabled为true。

      4.持久化订阅的ConnectionFactory不能和非持久化的订阅一起使用,否则会出现下面的告警信息

Could not refresh JMS Connection for destination 'durableHelloTopic' - retrying using FixedBackOff{interval=5000, currentAttempts=6, maxAttempts=unlimited}. Cause: setClientID call not supported on proxy for shared Connection. Set the 'clientId' property on the SingleConnectionFactory instead.

      5.topic消息实现负载均衡时的queue调用

       topic消息可以通过在activemq服务器上配置virtualTopic的方式将消息发送给匹配的queue,如下所示

<destinationInterceptors>
	<virtualDestinationInterceptor>
		<virtualDestinations>
		<!--
			name:主题名,可以是通配符
			prefix:队列的前缀
			selectorAware:表示从Topic中将消息转发给Queue时,是否关注Consumer的
			selector情况。如果为false,那么Topic中的消息全部转发给Queue,否则只会转发匹配Queue
			Consumer的selector的消息
		-->
			<virtualTopic name="virtualTopic.>" prefix="virtualTopicConsumers.*." selectorAware="false"/>
		</virtualDestinations>
	</virtualDestinationInterceptor>
</destinationInterceptors>

       需要注意的是prefix定义一定是xx.的形式,最后一定要包含一个.符号,而在代码中的订阅则是

@JmsListener(destination = "virtualTopicConsumers.virtualTopic1.virtualTopic.topic")

       需要注意的是在prefix定义的xx.后面一定要加上topic的名称,否则是接收不到消息的。

       6.消费者预取数量的获取

       在使用jmsTemplate的时候,通常我们只能在连接地址上面设置预取的数量,即

spring:
  activemq:
    # 设置消费者预取的消息数量为10,即设置的数量queuePrefetch*10
    # 默认情况下预取的配置为:持久化和非持久化queue都为1000,持久化topic为100,非持久化topic为Short.MAX_VALUE - 1
    broker-url: tcp://localhost:61616?jms.prefetchPolicy.queuePrefetch=1

       就如配置里面的说明,我们必须小心的是预取的数量并不是设置的数量,而是设置的数量queuePrefetch*10即10倍关系。

       7.消息确认机制使用客户端确认时查看ActiveMQ主界面出队不正常的显示情况

       当我们在jmsTemplate中使用

jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);

       进行消息确认的时候,明明产生异常了,却还是发现queue的消息都已经出队了,按照我们的认知,如果客户端没有正常执行Message.acknowledge()的话消息最后是会返回给activemq的,这个地方也许应该提供一个重试次数,在主界面虽然看到了消息已经出队,但是通过点击queue名称进入在点击View Consumers进去,可以看到消费端并没有出队,因此这个地方容易误解。

       8.重试机制

       在Apache的activemq代码中提供了重试机制的代码RedeliveryPolicy类,但是我们在结合springboot的时候,它并没有自动的注入该类,需要我们手动实现,其配置如下:

    @Bean
    public RedeliveryPolicy redeliveryPolicy(){
        RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
        // 是否在每次失败重发时增长等待时间
        redeliveryPolicy.setUseExponentialBackOff(true);
        // 设置重发最大拖延时间,-1 表示没有拖延,只有setUseExponentialBackOff(true)时生效
        redeliveryPolicy.setMaximumRedeliveryDelay(-1);
        // 重发次数
        redeliveryPolicy.setMaximumRedeliveries(3);
        // 重发时间间隔
        redeliveryPolicy.setInitialRedeliveryDelay(2000);
        // 第一次失败后重发前等待2秒,第二次2*2,依次递增
        redeliveryPolicy.setBackOffMultiplier(2);
        // 是否避免消息碰撞
        redeliveryPolicy.setUseCollisionAvoidance(false);
        // 避免消息碰撞的百分比
        redeliveryPolicy.setCollisionAvoidancePercent((short)5);
        // 开始发送重发消息的延迟时间
        redeliveryPolicy.setRedeliveryDelay(2000);
        // 这2个设置没有作用
        redeliveryPolicy.setQueue("helloQueue1");
        redeliveryPolicy.setTopic("helloTopic1");
        return redeliveryPolicy;
    }

       定义完成后需要手动添加到ConnectionFactory的实现bean上面,如下所示

    @Bean(value = "durableConnectionFactory")
    public ConnectionFactory durableConnectionFactory(RedeliveryPolicy redeliveryPolicy) {
        // 创建连接工厂,可以在brokerUrl后面跟上jms.useAsyncSend=true来表示异步发送消息
        ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(brokerUrl);
        // 设置异步发送消息
        connectionFactory.setAlwaysSyncSend(true);
        // 设置重发的策略
        connectionFactory.setRedeliveryPolicy(redeliveryPolicy);
        return connectionFactory;
    }

       我们需要注意的是在自定义JMsTemplate的时候,如下配置要小心,不然收不到重发的消息

// 消息的存活时间,如果设置了重试机制,注意存活时间
jmsTemplate.setTimeToLive(1000);
// 使用QosSettings值来提供默认的配置,如果设置了重试机制,注意存活时间
QosSettings qosSettings = new QosSettings(DeliveryMode.PERSISTENT, 8, 2000);
jmsTemplate.setQosSettings(qosSettings);
// 启用QosSettings配置
jmsTemplate.setExplicitQosEnabled(true);

         9.针对主从集群,broker节点配置updateClusterClients等参数不会生效。

        10.在通过static方式进行分布式配置节点的时候,需要以下改动:

        1).在broker属性上需要修改brokername,如下:

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost7" dataDirectory="${activemq.data}">
......
</broker>

       2).配置networkConnector属性的时候,uri地址选择其他broker节点的ip,自己的ip就不用配置了,否则出错,如下的local broker配置:

<transportConnectors>
    <!-- DOS protection, limit concurrent connections to 1000 and frame size to 100MB -->
    <transportConnector name="openwire" uri="tcp://0.0.0.0:61623?maximumConnections=1000&amp;wireFormat.maxFrameSize=104857600"/>
</transportConnectors>
		
<networkConnectors>
    <networkConnector uri="static:(tcp://localhost:61621,tcp://localhost:61622)"/>
</networkConnectors>

      11.在使用masterslave进行分布式的配置节点的改动:

      1).在broker属性上需要修改brokername,如下:

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost7" dataDirectory="${activemq.data}">
......
</broker>

      2).配置networkConnector属性的时候,uri地址选择其他broker节点的ip,自己的ip就不用配置了,在有多个networkConnector节点时一定要指定name属性,如下:

<networkConnectors>
    <networkConnector uri="masterslave:(tcp://172.19.3.157:61616,tcp://172.19.3.157:61617)" name="localhost1"/>
    <networkConnector uri="masterslave:(tcp://172.19.3.158:61616,tcp://172.19.3.158:61617)" name="localhost2"/>
</networkConnectors>

      如果不指定name属性,进行多个networkConnector配置后,启动将会出现下面的错误:

2020-01-07 10:57:26,748 | ERROR | Failed to start Apache ActiveMQ (localhost5, null) | org.apache.activemq.broker.BrokerService | main
java.io.IOException: Network Connector could not be registered in JMX: org.apache.activemq:type=Broker,brokerName=localhost5,connector=networkConnectors,networkConnectorName=NC
	at org.apache.activemq.util.IOExceptionSupport.create(IOExceptionSupport.java:28)[activemq-client-5.15.10.jar:5.15.10]
	at org.apache.activemq.broker.BrokerService.registerNetworkConnectorMBean(BrokerService.java:2299)[activemq-broker-5.15.10.jar:5.15.10]
	at org.apache.activemq.broker.BrokerService.start(BrokerService.java:623)[activemq-broker-5.15.10.jar:5.15.10]
	at org.apache.activemq.xbean.XBeanBrokerService.afterPropertiesSet(XBeanBrokerService.java:73)[activemq-spring-5.15.10.jar:5.15.10]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)[:1.8.0_141]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)[:1.8.0_141]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)[:1.8.0_141]
	at java.lang.reflect.Method.invoke(Method.java:498)[:1.8.0_141]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1759)[spring-beans-4.3.24.RELEASE.jar:4.3.24.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1696)[spring-beans-4.3.24.RELEASE.jar:4.3.24.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1626)[spring-beans-4.3.24.RELEASE.jar:4.3.24.RELEASE]

       12.在分布式主从集群中,只能将所有的节点信息进行配置才能实现分布式,如下:

failover:(tcp://localhost:61621,tcp://localhost:61625,tcp://localhost:61626,tcp://localhost:61622,tcp://localhost:61627,tcp://localhost:61623,tcp://localhost:61628)?randomize=true

       如果只是配置所有的master的broker节点,是无法实现分布式主从集群的,它只能连接到其中一台,如下:

failover:(tcp://localhost:61621,tcp://localhost:61622,tcp://localhost:61623)?updateURIsURL=file:/D:/software/connectUrl_network.txt

      因此通过下面这种方式是无法进行动态扩容的,需要进行统一配置。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值