Activemq项目中配置

本文详细介绍了在SpringBoot项目中配置并使用ActiveMQ的过程,特别是队列模式的应用。讨论了ActiveMQ的异步投递和同步投递模式,强调了异步投递可能导致的消息丢失场景,并给出了模拟持久化阻塞的实验。此外,还涉及了延迟投递、定时投递以及消息重试机制和有毒消息处理。最后,提到了配置参数和可能遇到的问题,欢迎读者交流指正。
摘要由CSDN通过智能技术生成

直接介绍在项目中使用的配置过程(队列模式为例,点对点)
版本情况:

  • springboot:1.5.9.RELEASE
  • activemq:5.18

生产者

  • pom
<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<!--pom.xml也要加入下面这个依赖包,否则启动报JmsMessagingTemplate注入失败 。-->
<dependency>
   <groupId>org.apache.activemq</groupId>
   <artifactId>activemq-pool</artifactId>
</dependency>
  • yml
activemq:
  broker-url: tcp://localhost:61616
  user: admin
  password: admin
  • 生产者配置类
@Configuration
@EnableJms
public class ActivemqCfg {

    @Autowired
    private JmsProperties jmsProperties;
	
    private String queueName = "test-queue";

	//这里声明一个queue的bean,如果要使用topic new topic即可
    @Bean
    public Queue queue() {
        ActiveMQQueue activeMQQueue = new ActiveMQQueue(queueName);
        return activeMQQueue;
    }

    @Bean
    public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) {
        JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
        jmsTemplate.setPubSubDomain(jmsProperties.isPubSubDomain());
        JmsProperties.Template template = jmsProperties.getTemplate();
        if (template.getDefaultDestination() != null) {
            jmsTemplate.setDefaultDestinationName(template.getDefaultDestination());
        }
        if (template.getDeliveryDelay() != null) {
            jmsTemplate.setDeliveryDelay(template.getDeliveryDelay().toMillis());
        }
        jmsTemplate.setExplicitQosEnabled(template.determineQosEnabled());
        if (template.getDeliveryMode() != null) {
            jmsTemplate.setDeliveryMode(template.getDeliveryMode().getValue());
        }
        if (template.getPriority() != null) {
            jmsTemplate.setPriority(template.getPriority());
        }
        if (template.getTimeToLive() != null) {
            jmsTemplate.setTimeToLive(template.getTimeToLive().toMillis());
        }
        if (template.getReceiveTimeout() != null) {
            jmsTemplate.setReceiveTimeout(template.getReceiveTimeout().toMillis());
        }
        //持久化消息
        jmsTemplate.setDeliveryMode(DeliveryMode.PERSISTENT);
        //开启事务
        jmsTemplate.setSessionTransacted(true);
        //客户端签收模式
        jmsTemplate.setSessionAcknowledgeMode(4);

        return jmsTemplate;
    }

}
  • 生产者发送消息
@Autowired
JmsTemplate jmsTemplate;

@Autowired
private Queue queue;

@RequestMapping("/queue/send")
public String sendQueue() throws JMSException {

    jmsTemplate.convertAndSend(queue, "abc");
    logger.info("发送消息成功");

    return "success";
}
  • 消费者配置类
@Configuration
@EnableJms
public class ActivemqCfg {

    @Autowired
    private JmsProperties jmsProperties;

    private String queueName = "test-queue";

    @Bean
    public Queue queue() {
        ActiveMQQueue activeMQQueue = new ActiveMQQueue(queueName);
        return activeMQQueue;
    }

    @Bean
    public RedeliveryPolicy redeliveryPolicy(){
        RedeliveryPolicy  redeliveryPolicy=   new RedeliveryPolicy();
        //是否在每次尝试重新发送失败后,增长这个等待时间
        redeliveryPolicy.setUseExponentialBackOff(false);
        //重发次数,默认为6次   这里设置为3次
        redeliveryPolicy.setMaximumRedeliveries(3);
        //重发时间间隔,默认为1秒
        redeliveryPolicy.setInitialRedeliveryDelay(1000);
        //第一次失败后重新发送之前等待500毫秒,第二次失败再等待500 * 2毫秒,这里的2就是value
        redeliveryPolicy.setBackOffMultiplier(2);
        //是否避免消息碰撞
        redeliveryPolicy.setUseCollisionAvoidance(false);
        //设置重发最大拖延时间-1 表示没有拖延只有UseExponentialBackOff(true)为true时生效
        redeliveryPolicy.setMaximumRedeliveryDelay(-1);
        return redeliveryPolicy;
    }

    @Bean
    public ActiveMQConnectionFactory activeMQConnectionFactory (@Value("${spring.activemq.broker-url}")String url, RedeliveryPolicy redeliveryPolicy){
        ActiveMQConnectionFactory activeMQConnectionFactory =
                new ActiveMQConnectionFactory(
                        "admin",
                        "admin",
                        url);
        activeMQConnectionFactory.setRedeliveryPolicy(redeliveryPolicy);
        return activeMQConnectionFactory;
    }

    //定义一个消息监听器连接工厂,这里定义的是点对点模式的监听器连接工厂
    @Bean(name = "jmsQueueListener")
    public DefaultJmsListenerContainerFactory jmsQueueListenerContainerFactory(ActiveMQConnectionFactory activeMQConnectionFactory) {
        DefaultJmsListenerContainerFactory factory =
                new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(activeMQConnectionFactory);
        //设置连接数
        factory.setConcurrency("1-10");
        //重连间隔时间
        factory.setRecoveryInterval(1000L);

        /**
         * AUTO_ACKNOWLEDGE = 1    自动确认
         CLIENT_ACKNOWLEDGE = 2    客户端手动确认
         DUPS_OK_ACKNOWLEDGE = 3    自动批量确认
         SESSION_TRANSACTED = 0    事务提交并确认
         INDIVIDUAL_ACKNOWLEDGE = 4    单条消息确认 activemq 独有
         */
        factory.setSessionAcknowledgeMode(2);
        return factory;
    }
  • 消费消息
@Component
public class Consumer {

    Logger logger = LoggerFactory.getLogger(getClass());

    @JmsListener(destination = "test-queue",  containerFactory = "jmsQueueListener")
    public void receive(TextMessage textMessage, Session session) throws JMSException {
        logger.info("消息为*********"+textMessage.getText());
        textMessage.acknowledge();// 使用手动签收模式,需要手动的调用,如果不在catch中调用session.recover()消息只会在重启服务后重发
        //session.recover();//手动触发 重发机制
    }
}

Activemq的高级特性:

  • 异步投递:
    Activemq支持同步异步两种发送的模式将消息发送到broker,模式的选择对发送延时有巨大的影响。使用异步发送可以显著的提高发送端的性能。
    Activemq默认使用异步发送模式:除非明确指定使用同步发送的方式,或者在未使用事务的前提下发送持久化的消息,这两中情况都是同步发送的。
    如果是同步的发送模式,每一次发送都是同步发送的且会阻塞producer直到broker返回一个确认,表示消息已经被安全的持久化到磁盘,确认机制提供了消息安全的保障,但同时会阻塞客户端带来了很大的延时。
//设置异步投递
ActiveMQConnectionFactory connectionFactory = 
    new ActiveMQConnectionFactory(URL);
connectionFactory.setUseAsyncSend(true);

异步发送丢失消息的场景是:生产者设置UserAsyncSend=true,使用producer.send(msg)持续发送消息,由于消息不阻塞,生产者会认为所有send的消息都被成功发送至mq,如果mq突然宕机,此时生产者端内存中尚未被发送到mq的消息都会丢失。所以正确的异步发送方法是需要接收回调的。

ActiveMQMessageProducer messageProducer = 
    (ActiveMQMessageProducer) session.createProducer(queue);
//发送消息的时候第二个参数是回调
messageProducer.send(textMessage, new AsyncCallback() {
    @Override
    public void onSuccess() {
        System.out.println("success");
    }

    @Override
    public void onException(JMSException exception) {
        System.out.println("failed");
    }
});

但是想法奇特的我有了一个奇怪的想法,看上面我们知道异步投递成功了之后会有一个回调通知,告诉投递成功,那么我想如果是持久化消息,那么消息一直在持久化中阻塞了会是什么结果。所以我把持久化机制改成了jdbc的方式,在持久化的表上锁来模拟一直持久化阻塞这个场景,,(由于配置jdbc持久化不是这个的重点,也很容易,所以这里就省略了)

  • 如果是持久化的,是同步的模式,创建好生产者之后,将mysql的activemq_msgs表锁住:
    lock table activemq_msgs write
    那么消息会卡在send那一行的代码上,会一直阻塞,如果释放锁:
    unlock tables
    那么就会发送成功。和理想状况是一样的

  • 如果是异步的模式,不会阻塞,我创建了一个生产者之后,锁住activemq_msgs表使其不能持久化,这时候使用异步投递发送3条消息,在一段时间之后,由于不能持久化所以触发失败回调,这时候我将mysql的锁释放掉,可以看到瞬间队列中出现了一条待消费的消息,可是刚刚已经触发了失败回调。问题就出现了,小伙伴在使用的时候要注意这个问题。

  • 延迟投递和定时 投递:
    1.修改activemq配置文件:
    在标签中加入属性 schedulerSupport="true".
    修改生产者逻辑:

//参考下面的4个变量
long delay = 3 * 1000;
long period = 4 * 1000;
int repeat = 3;

//将变量设置在消息上就ok啦
TextMessage textMessage = session.createTextMessage("哈哈" + i);
textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY,delay);
textMessage.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_PERIOD,period);
textMessage.setIntProperty(ScheduledMessage.AMQ_SCHEDULED_REPEAT, repeat);

在这里插入图片描述

  • activemq的消息重试机制 :
    具体哪些情况会引起消息重发?

    • client用了transactions且在session中调用了rollback();
    • client用了transactions且在调用commit()之前关闭或者没有commit.
    • client在CLIENT_ACKNOWLEDGE的传递模式下,在session中调用了recover()

    消息重发的时间间隔和次数(默认);

    • 间隔1s
    • 次数6

    有毒消息Poison ACK:
    一个消息被重发超过默认的最大重发次数(默认6次)时,消费端会给MQ发送一个"poison ack" 标识这个消息有毒,告诉broker不要再发了,这个时候broker会把这个消息放到DLQ(死信队列).
    配置:

//在消费者的代码中配置
redeliveryPolicy.setMaximumRedeliveries(3);
connectionFactory.setRedeliveryPolicy(redeliveryPolicy);
Connection connection = connectionFactory.createConnection();

还可以配置:
在这里插入图片描述

题后话,由于作者水平有限,文章中难免会有歧义的地方,欢迎大家批评指正,作者定会及时改正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值