springboot与rabbitmq结合

spring:
  rabbitmq:
    username: guest #默认账号
    port: 5672   #默认端口 
    password: guest #密码
    host: 127.0.0.1 #主机地址
    virtual-host: / #虚拟路径
    publisher-returns: true #exchange到queue失败,则回调return(需设置mandatory=true,否则不回回调,消息就丢了)
    publisher-confirms: true # 如果消息没有到exchange,则confirm回调  ack消息确认机制
    listener:
      exchange: face.exchange #默认交换机 
      direct:
        acknowledge-mode: manual #手动ack
      simple:
        acknowledge-mode: manual #手动ack
        retry:
          multiplier: 2 #应用于前一重试间隔的乘法器 比如第一次10秒 第二次就是20秒 第三次就是40秒
          max-attempts: 5 #最大重试次数
          enabled: true #开启重试机制
          max-interval: 30000ms #最大30秒
          initial-interval: 10000ms #第一次10秒
        default-requeue-rejected: true #消费者抛出异常将会把消息重新放回队列  false则不放回  默认为true
        concurrency: 10 #并发监听器线程调用最小数量
        max-concurrency: 50 #并发监听器线程调用最大数量

生产者 Provider

@Component
public class Sender{
    
    protected static final Logger logger = LoggerFactory.getLogger(Sender.class);
    
    @Autowired
    private AmqpTemplate amqpTemplate;
    /**
     * 
     * @param exchange 交换机名称
     * @param routingKey 路由键
     * @param message 消息内容
     */
    public void send(String exchange, String routingKey, String message){
        amqpTemplate.convertAndSend(exchange,routingKey,message);
    }
}

监听者 Listener

import com.alibaba.fastjson.JSON;
import com.rabbitmq.client.Channel;
import facerecoftp.entity.DataModuleTest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.stereotype.Component;

import java.io.IOException;


/**
 * @author whh
 * @date 2019/3/11 11:38
 */
@Component
public class Listener {

    protected static final Logger logger = LoggerFactory.getLogger(Listener.class);

     /**
     *  ExchangeTypes.TOPIC 交换机类型
     *  durable 是否持久化
     *  concurrency 并发量    可以理解为监听者数量  可以配置在yml文件里  用"{变量名}"去取
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "队列名称", durable = "true"),
            exchange = @Exchange(name = "交换机名称", type = ExchangeTypes.TOPIC), 
            key = {"路由键"}
    ),concurrency = "1")
    public void sendImg(@Payload String content, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag){
        // dataModuleTest为我生产者发送的序列化之前的对象 这里使用fastJSON序列化
        DataModuleTest dataModuleTest = JSON.parseObject(content, DataModuleTest.class);
        try {
            // 手动ack ack之后消息队列的消息就会取出  如果抛出异常 监听者会重复收到消息
            channel.basicAck(deliveryTag,false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        logger.debug("发送message-->{}",dataModuleTest);
    }
}

如果生产者需要ack 可以使用以下代码


import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import handler.util.UuidUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeoutException;

/**
 * @author whh
 * @date 2019/3/11 10:57
 */
@Component
public class Sender implements RabbitTemplate.ConfirmCallback{

    protected static final Logger logger = LoggerFactory.getLogger(Sender.class);

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Value("${spring.rabbitmq.username}")
    private String userName;
    @Value("${spring.rabbitmq.password}")
    private String password;
    @Value("${spring.rabbitmq.host}")
    private String host;
    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHost;
    @Value("${spring.rabbitmq.port}")
    private int port;

    /**
     * rabbitTemplate如果为单例的话,那回调就是最后设置的内容
     * @param rabbitTemplate
     */
    @Autowired
    public Sender(RabbitTemplate rabbitTemplate) {
        this.rabbitTemplate = rabbitTemplate;
        rabbitTemplate.setConfirmCallback(this);
    }

    public Map<String,String[]> hashmap = new ConcurrentHashMap<>(1);
    /**
     *
     * @param exchange 交换机名称
     * @param routingKey 路由关键字
     * @param content 消息内容
     */
    public void send(String exchange,String queue, String routingKey, String content) throws IOException, TimeoutException {
        String msgId = UuidUtil.get32UUID();
        String[] arr = {exchange,queue,routingKey,content};
        hashmap.put(msgId,arr);
        Message message = MessageBuilder.withBody(content.getBytes()).setContentType(MessageProperties.CONTENT_TYPE_TEXT_PLAIN).setCorrelationId(msgId).build();
        try {
            rabbitTemplate.convertAndSend(exchange,routingKey,message,new CorrelationData(msgId));
        } catch (AmqpException e) {
            // TODO
            logger.debug("消息发送失败补偿--{}",e.getMessage());

        }
    }


    /**
     * 消息发送交换机成功 回调方法 成功ack为true 失败为false
     * @param correlationData
     * @param ack
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {

        String key = correlationData.getId();
        if (!ack) {
            ConnectionFactory connectionFactory = new ConnectionFactory();
            connectionFactory.setPort(port);
            connectionFactory.setUsername(userName);
            connectionFactory.setPassword(password);
            connectionFactory.setHost(host);
            connectionFactory.setVirtualHost(virtualHost);
            Channel channel = null;
            String[] split = hashmap.get(key);
            String exchange = split[0];
            String queue = split[1];
            String routingKey = split[2];
            String content = split[3];
            try {
                Connection connection = connectionFactory.newConnection();
                channel = connection.createChannel();
                channel.exchangeDeclare(exchange, "topic", true);
                channel.queueDeclare(queue,true,false,false,null);
                channel.queueBind(queue,exchange,routingKey);
                send(exchange,queue,routingKey,content);
            } catch (IOException | TimeoutException e) {
                logger.debug("获取通道失败---{}",e.getMessage());
            }
        } 
        hashmap.remove(key);
    }
}

防止消息丢失

发送方confirm方法的ack变量为false时,做补偿
消费者ack确认

channel.basicAck(deliveryTag,false);

队列与交换机持久化

     channel.exchangeDeclare(exchange, "topic", true);
     channel.queueDeclare(queue,true,false,false,null);
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值