Spring boot 整合 RabbitMQ 介绍

消息确认机制

消息确认分为两部分: 生产确认 和 消费确认。
生产确认: 生产者生产消息后,将消息发送到交换机,触发确认回调;交换机将消息转发到绑定队列,若失败则触发返回回调。
消费确认: 默认情况下消息被消费者从队列中获取后即发送确认,不管消费者处理消息时是否失败,不需要额外代码,但是不能保证消息被正确消费。我们增加手动确认,则需要代码中明确进行消息确认。
在配置文件中添加:

spring:
  profiles:
    active: dev
  #线程池配置
  task:
    pool:
      corePoolSize : 1000
      maxPoolSize : 2000
      keepAliveSeconds : 60
      queueCapacity : 1000
  rabbitmq:
    template:
        # 使用return-callback时必须设置mandatory为true
        mandatory: true
    # 消息发送到交换机确认机制,是否确认回调
    publisher-confirms: true
    # 消息发送到交换机确认机制,是否返回回调
    publisher-returns: true
    listener:
      simple:
        # 并发消费者初始化值
        concurrency: 5
        # 最大值
        max-concurrency: 10
        # 每个消费者每次监听时可拉取处理的消息数量
        prefetch: 20
        # 确认模式设置为手动签收
        acknowledge-mode: manual

1. 引入pom文件

<dependency>
   <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.5.0</version>
</dependency>
<dependency>
    <groupId>com.alibaba.mq-amqp</groupId>
    <artifactId>mq-amqp-client</artifactId>
    <version>1.0.4</version>
</dependency>

2. bootstrap.yml

spring:
  profiles:
    active: dev
  #线程池配置
  task:
    pool:
      corePoolSize : 1000
      maxPoolSize : 2000
      keepAliveSeconds : 60
      queueCapacity : 1000
  rabbitmq:
    template:
        # 使用return-callback时必须设置mandatory为true
        mandatory: true
    # 消息发送到交换机确认机制,是否确认回调
    publisher-confirms: true
    # 消息发送到交换机确认机制,是否返回回调
    publisher-returns: true
    listener:
      simple:
        # 并发消费者初始化值
        concurrency: 5
        # 最大值
        max-concurrency: 10
        # 每个消费者每次监听时可拉取处理的消息数量
        prefetch: 20
        # 确认模式设置为手动签收
        acknowledge-mode: manual

3. RabbitConfig

package com.cecurs.usercenter.common.rabbit;

import com.cecurs.usercenter.common.config.Config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.amqp.RabbitProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
@Slf4j
public class RabbitConfig {
    //资源owner账户 ID 信息
    private static final long RESOURCE_OWNER_ID =1594045111309684L;
    @Autowired
    private RabbitProperties rabbitProperties;
    @Autowired
    private Config config;

    @Bean
    public ConnectionFactory getConnectionFactory() {
        log.info("mq host: " + config.getAliyunHost() + " port: " +  config.getAliyunPort() +" vhost: "+config.getAliyunVirtualHost());
        log.info("mq username: "+config.getAliyunUsername() + " mq pwd: " +config.getAliyunPassword());

        com.rabbitmq.client.ConnectionFactory rabbitConnectionFactory =
                new com.rabbitmq.client.ConnectionFactory();
        rabbitConnectionFactory.setHost(config.getAliyunHost());
        rabbitConnectionFactory.setPort(config.getAliyunPort());
        rabbitConnectionFactory.setVirtualHost(config.getAliyunVirtualHost());

        AliyunCredentialsProvider credentialsProvider = new AliyunCredentialsProvider(
                config.getAliyunUsername(), config.getAliyunPassword(), RESOURCE_OWNER_ID);
        rabbitConnectionFactory.setCredentialsProvider(credentialsProvider);
        rabbitConnectionFactory.setAutomaticRecoveryEnabled(true);
        rabbitConnectionFactory.setNetworkRecoveryInterval(5000);
        ConnectionFactory connectionFactory = new CachingConnectionFactory(rabbitConnectionFactory);
        ((CachingConnectionFactory)connectionFactory).setPublisherConfirms(rabbitProperties.isPublisherConfirms());
        ((CachingConnectionFactory)connectionFactory).setPublisherReturns(rabbitProperties.isPublisherReturns());
        return connectionFactory;
    }

    /**
     * 创建队列
     * @return
     */
    @Bean
    public Queue create() {
        return new Queue(config.getAliyunQueueName());
    }


    /**
     * 创建同步用户的队列
     * @return
     */
    @Bean
    public Queue synchroUserQueue() {
        return new Queue("synchroUserCenterQueue");
    }

    /**
     * 创建新增用户的队列
     * @return
     */
    @Bean
    public Queue addUserQueue() {
        return new Queue("addUserCenterQueue");
    }



    /**
     * 创建广播模式队列 收银台
     * @return
     */
    @Bean
    public Queue queueFirst() {
        return new Queue("fanout.first");
    }

    /**
     * 创建广播模式队列 云卡
     * @return
     */
    @Bean
    public Queue queueSecond() {
        return new Queue("fanout.second");
    }

    /**
     * 创建广播模式队列 玺商街
     * @return
     */
    @Bean
    public Queue queueThird() {
        return new Queue("fanout.third");
    }

    @Bean
    FanoutExchange fanoutExchange() {
        return new FanoutExchange(config.getAliyunExchange());
    }

    /**
     * 第一个队列绑定到 Fanout 交换机
     * @param queueFirst
     * @param fanoutExchange
     * @return
     */
    @Bean
    Binding bindingExchangeFirst(Queue queueFirst,FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queueFirst).to(fanoutExchange);
    }

    /**
     * 第二个队列绑定到 Fanout 交换机
     * @param queueSecond
     * @param fanoutExchange
     * @return
     */
    @Bean
    Binding bindingExchangeSecond(Queue queueSecond, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queueSecond).to(fanoutExchange);
    }

    /**
     * 第三个队列绑定到 Fanout 交换机
     * @param queueThird
     * @param fanoutExchange
     * @return
     */
    @Bean
    Binding bindingExchangeThird(Queue queueThird, FanoutExchange fanoutExchange) {
        return BindingBuilder.bind(queueThird).to(fanoutExchange);
    }


    /**
     * 申明队列
     *
     * @return
     */
    @Bean
    public Queue queue() {
        Map<String, Object> arguments = new HashMap<>(4);

        return new Queue("queue-rabbit-springboot-advance5", true, false, false, arguments);
    }

    @Bean
    public Exchange exchange() {
        Map<String, Object> arguments = new HashMap<>(4);

        return new DirectExchange("exchange-rabbit-springboot-advance5", true, false, arguments);
    }

    /**
     * 申明绑定
     *
     * @return
     */
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queue()).to(exchange()).with("product").noargs();
    }


}

4. AliyunSender

package com.cecurs.usercenter.common.rabbit;

import com.alibaba.fastjson.JSON;
import com.cecurs.usercenter.common.config.Config;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.stereotype.Component;

@Component
@Slf4j
public class AliyunSender {
	private static final Logger logger = LoggerFactory.getLogger(AliyunSender.class);
	@Autowired
	private RabbitTemplate rabbitTemplate;
	@Autowired
	private Config config;

	/**
	 * 如果消息没有到exchange,则confirm回调,ack=false
	 * 如果消息到达exchange,则confirm回调,ack=true
	 * exchange到queue成功,则不回调return
	 * exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
	 */
	private final RabbitTemplate.ConfirmCallback confirmCallback = (correlationData, ack, cause) -> {
		if (!ack) {
			logger.error("消息发送失败:correlationData: {},cause: {}", correlationData, cause);
		}else {
			logger.info("消息发送成功:correlationData: {},ack: {}", correlationData, ack);
		}
	};

	private final RabbitTemplate.ReturnCallback returnCallback = (message, replyCode, replyText, exchange, routeKey) ->
			logger.error("消息丢失: exchange: {},routeKey: {},replyCode: {},replyText: {}", exchange, routeKey, replyCode, replyText);

	/**
	 * 简单模式
	 * @param msg
	 */
	public void sendHandler(String msg) {
		logger.info("新增用户推送 mq 消息队列,{}",JSON.toJSON(msg));
		this.setSendConfig();
		rabbitTemplate.convertAndSend("addUserCenterQueue",msg);
	}

	/**
	 * 简单消息模式,同步用户ID
	 * @param msg
	 */
	public void sendSynchroUserInfo(Object msg) {
		logger.info("同步用户数据,推送 mq 消息队列,{}",JSON.toJSON(msg));
		this.setSendConfig();
		rabbitTemplate.convertAndSend("synchroUserCenterQueue",msg);
	}

	void setSendConfig(){
		// 针对网络原因导致连接断开,利用retryTemplate重连3次
		RetryTemplate retryTemplate = new RetryTemplate();
		retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3));
		rabbitTemplate.setRetryTemplate(retryTemplate);
		rabbitTemplate.setConfirmCallback(confirmCallback);
		rabbitTemplate.setReturnCallback(returnCallback);
	}

	/**
	 * 广播模式发布消息队列
	 * @param obj
	 */
	public void sendByFanout(Object obj) {
		this.setSendConfig();
		this.rabbitTemplate.convertAndSend(config.getAliyunExchange(),config.getAliyunRoutingKey(), obj);
	}

}
import java.util.UUID;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class CallBackSender implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback {
	@Autowired
	private RabbitTemplate rabbitTemplate;
 
	public void send(String topic, String message) {
		rabbitTemplate.setConfirmCallback(this);
		CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
 
		System.out.println("消息id:" + correlationData.getId());
		// 用RabbitMQ发送MQTT需将exchange配置为amq.topic
		this.rabbitTemplate.convertAndSend("amq.topic", topic, message, correlationData);
	}
 
	public void confirm(CorrelationData correlationData, boolean ack, String cause) {
		System.out.println("消息id:" + correlationData.getId());
		if (ack) {
			System.out.println("消息发送确认成功");
		} else {
			System.out.println("消息发送确认失败:" + cause);
		}
	}
 
	@Override
	public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
		System.out.println("return--message:" + new String(message.getBody()) + ",replyCode:" + replyCode
				+ ",replyText:" + replyText + ",exchange:" + exchange + ",routingKey:" + routingKey);
	}
}

总结下就是:
如果消息没有到exchange,则confirm回调,ack=false
如果消息到达exchange,则confirm回调,ack=true
exchange到queue成功,则不回调return
exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)

Spring Boot 整合RabbitMQ环境下的配置参数解释

★RabbitMQ
★Version: 1.5.13.RELEASE

★属性文件:org.springframework.boot.autoconfigure.amqp.RabbitProperties

★Config:
# base
spring.rabbitmq.host: 服务Host
spring.rabbitmq.port: 服务端口
spring.rabbitmq.username: 登陆用户名
spring.rabbitmq.password: 登陆密码
spring.rabbitmq.virtual-host: 连接到rabbitMQ的vhost
spring.rabbitmq.addresses: 指定client连接到的server的地址,多个以逗号分隔(优先取addresses,然后再取host)
spring.rabbitmq.requested-heartbeat: 指定心跳超时,单位秒,0为不指定;默认60s
spring.rabbitmq.publisher-confirms: 是否启用【发布确认】
spring.rabbitmq.publisher-returns: 是否启用【发布返回】
spring.rabbitmq.connection-timeout: 连接超时,单位毫秒,0表示无穷大,不超时
spring.rabbitmq.parsed-addresses:


# ssl
spring.rabbitmq.ssl.enabled: 是否支持ssl
spring.rabbitmq.ssl.key-store: 指定持有SSL certificate的key store的路径
spring.rabbitmq.ssl.key-store-password: 指定访问key store的密码
spring.rabbitmq.ssl.trust-store: 指定持有SSL certificates的Trust store
spring.rabbitmq.ssl.trust-store-password: 指定访问trust store的密码
spring.rabbitmq.ssl.algorithm: ssl使用的算法,例如,TLSv1.1


# cache
spring.rabbitmq.cache.channel.size: 缓存中保持的channel数量
spring.rabbitmq.cache.channel.checkout-timeout: 当缓存数量被设置时,从缓存中获取一个channel的超时时间,单位毫秒;如果为0,则总是创建一个新channel
spring.rabbitmq.cache.connection.size: 缓存的连接数,只有是CONNECTION模式时生效
spring.rabbitmq.cache.connection.mode: 连接工厂缓存模式:CHANNEL 和 CONNECTION


# listener
spring.rabbitmq.listener.simple.auto-startup: 是否启动时自动启动容器
spring.rabbitmq.listener.simple.acknowledge-mode: 表示消息确认方式,其有三种配置方式,分别是none、manual和auto;默认auto
spring.rabbitmq.listener.simple.concurrency: 最小的消费者数量
spring.rabbitmq.listener.simple.max-concurrency: 最大的消费者数量
spring.rabbitmq.listener.simple.prefetch: 指定一个请求能处理多少个消息,如果有事务的话,必须大于等于transaction数量.
spring.rabbitmq.listener.simple.transaction-size: 指定一个事务处理的消息数量,最好是小于等于prefetch的数量.
spring.rabbitmq.listener.simple.default-requeue-rejected: 决定被拒绝的消息是否重新入队;默认是true(与参数acknowledge-mode有关系)
spring.rabbitmq.listener.simple.idle-event-interval: 多少长时间发布空闲容器时间,单位毫秒

spring.rabbitmq.listener.simple.retry.enabled: 监听重试是否可用
spring.rabbitmq.listener.simple.retry.max-attempts: 最大重试次数
spring.rabbitmq.listener.simple.retry.initial-interval: 第一次和第二次尝试发布或传递消息之间的间隔
spring.rabbitmq.listener.simple.retry.multiplier: 应用于上一重试间隔的乘数
spring.rabbitmq.listener.simple.retry.max-interval: 最大重试时间间隔
spring.rabbitmq.listener.simple.retry.stateless: 重试是有状态or无状态


# template
spring.rabbitmq.template.mandatory: 启用强制信息;默认false
spring.rabbitmq.template.receive-timeout: receive() 操作的超时时间
spring.rabbitmq.template.reply-timeout: sendAndReceive() 操作的超时时间
spring.rabbitmq.template.retry.enabled: 发送重试是否可用
spring.rabbitmq.template.retry.max-attempts: 最大重试次数
spring.rabbitmq.template.retry.initial-interval: 第一次和第二次尝试发布或传递消息之间的间隔
spring.rabbitmq.template.retry.multiplier: 应用于上一重试间隔的乘数
spring.rabbitmq.template.retry.max-interval: 最大重试时间间隔
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值