springboot2.0.4+rabbitMQ配置

最近的業務需要用到rabbitMQ,這裏記錄一下配置。本地使用Docker自己啓動的rabbitMQ測試,線上使用的阿里的消息隊列 for AMQP,完全兼容 RabbitMQ 開源生態,不用修改代碼。

1.導入依賴包

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

 

2. springboot配置

spring.rabbitmq.host = localhost
spring.rabbitmq.port = 5672
spring.rabbitmq.username = test
spring.rabbitmq.password = test
spring.rabbitmq.virtual-host= /
spring.rabbitmq.connection-timeout = 60s

這裏直接使用了springboot的默認配置,沒有自己寫 ConnectionFactory。生產者和消費者都使用此配置。

 

3. 配置文件

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * rabbitmq配置
 * @author qiqi.zhao
 * @date 2019/9/11
 */
@Configuration
public class RabbitConfig {

    //durable = true開啓持久化將消息持久化到磁盤
    @Bean
    public Queue testQueue() {
        return new Queue("test-queue",true);
    }

    @Bean
    TopicExchange testExchange() {
        return new TopicExchange("test-exchange", false, false);
    }

    @Bean
    Binding bindingStepsExchangeMessage(@Qualifier("testQueue") Queue testQueue, @Qualifier("testExchange") TopicExchange testExchange) {
        // 將隊列burnQueue綁定到名爲routingKey的routingKey
        return BindingBuilder.bind(testQueue).to(testExchange).with("*.test.*");
    }

    /**
     * 配置json轉換,使rabbitMQ可以接收和發送json格式數據
     * @param connectionFactory
     * @return
     */
    @Bean("rabbitListenerContainerFactory")
    public RabbitListenerContainerFactory<?> rabbitListenerContainerFactory(ConnectionFactory connectionFactory){
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(new Jackson2JsonMessageConverter());
        return factory;
    }

}

這裏配置了隊列和話題模式的交換機,並把它們綁定,並配置了RabbitListenerContainerFactory用於發送和接收json格式數據。生產者需要配置以上所有的信息,消費者只需要配置RabbitListenerContainerFactory就可以了。

 

4. 生產者

@Slf4j
@Component
public class RabbitProducer<T> {
    private AmqpTemplate amqpTemplate;
    private Gson gson = new Gson();
    private MessageProperties properties = new MessageProperties();

    @Autowired
    public RabbitProducer(AmqpTemplate amqpTemplate) {
        this.amqpTemplate = amqpTemplate;
        properties.setContentType(MessageProperties.CONTENT_TYPE_JSON);
    }

    public void sendData(T data, String routingKey) {
        log.info("Rabbit send:" + data.toString());
        //Topic 方式;
        String json = gson.toJson(data);
        Message msg = new Message(json.getBytes(), properties);
        amqpTemplate.convertAndSend("test-exchange", routingKey, msg);
    }
}

 

5.  消費者

@Slf4j
@Component
public class RabbitConsume {
    
    @RabbitHandler
    @RabbitListener(queues = "test-queue", containerFactory="rabbitListenerContainerFactory")
    public void process(@Payload Data data) {
        log.info("-----------------------------------");
        System.out.println(data.toString());
    }
    
}

6. 關於消息不丟失

這裏摘錄一下博主“一條路上的鹹魚”:關於MQ的幾件小事(四)如何保證消息不丟失

6.1 丟失數據場景

丟數據一般分爲兩種,一種是mq把消息丟了,一種就是消費時將消息丟了。下面從rabbitmq和kafka分別說一下,丟失數據的場景,
A:生產者弄丟了數據 生產者將數據發送到rabbitmq的時候,可能在傳輸過程中因爲網絡等問題而將數據弄丟了。
B:rabbitmq自己丟了數據 如果沒有開啓rabbitmq的持久化,那麼rabbitmq一旦重啓,那麼數據就丟了。所依必須開啓持久化將消息持久化到磁盤,這樣就算rabbitmq掛了,恢復之後會自動讀取之前存儲的數據,一般數據不會丟失。除非極其罕見的情況,rabbitmq還沒來得及持久化自己就掛了,這樣可能導致一部分數據丟失。
C:消費端弄丟了數據 主要是因爲消費者消費時,剛消費到,還沒有處理,結果消費者就掛了,這樣你重啓之後,rabbitmq就認爲你已經消費過了,然後就丟了數據。

6.2 如何防止消息丟失

A:生產者丟失消息
①:可以選擇使用rabbitmq提供是事物功能,就是生產者在發送數據之前開啓事物,然後發送消息,如果消息沒有成功被rabbitmq接收到,那麼生產者會受到異常報錯,這時就可以回滾事物,然後嘗試重新發送;如果收到了消息,那麼就可以提交事物。

  channel.txSelect();//開啓事物
  try{
      //發送消息
  }catch(Exection e){
      channel.txRollback();//回滾事物
      //重新提交
  }
複製代碼

缺點: rabbitmq事物已開啓,就會變爲同步阻塞操作,生產者會阻塞等待是否發送成功,太耗性能會造成吞吐量的下降。

②:可以開啓confirm模式。在生產者哪裏設置開啓了confirm模式之後,每次寫的消息都會分配一個唯一的id,然後如何寫入了rabbitmq之中,rabbitmq會給你回傳一個ack消息,告訴你這個消息發送OK了;如果rabbitmq沒能處理這個消息,會回調你一個nack接口,告訴你這個消息失敗了,你可以進行重試。而且你可以結合這個機制知道自己在內存裏維護每個消息的id,如果超過一定時間還沒接收到這個消息的回調,那麼你可以進行重發。

    //開啓confirm
    channel.confirm();
    //發送成功回調
    public void ack(String messageId){
      
    }

    // 發送失敗回調
    public void nack(String messageId){
        //重發該消息
    }
複製代碼

二者不同 事務機制是同步的,你提交了一個事物之後會阻塞住,但是confirm機制是異步的,發送消息之後可以接着發送下一個消息,然後rabbitmq會回調告知成功與否。 一般在生產者這塊避免丟失,都是用confirm機制。
B:rabbitmq自己弄丟了數據 設置消息持久化到磁盤。設置持久化有兩個步驟:
①創建queue的時候將其設置爲持久化的,這樣就可以保證rabbitmq持久化queue的元數據,但是不會持久化queue裏面的數據。
②發送消息的時候講消息的deliveryMode設置爲2,這樣消息就會被設爲持久化方式,此時rabbitmq就會將消息持久化到磁盤上。 必須要同時開啓這兩個纔可以。

而且持久化可以跟生產的confirm機制配合起來,只有消息持久化到了磁盤之後,纔會通知生產者ack,這樣就算是在持久化之前rabbitmq掛了,數據丟了,生產者收不到ack回調也會進行消息重發。
C:消費者弄丟了數據 使用rabbitmq提供的ack機制,首先關閉rabbitmq的自動ack,然後每次在確保處理完這個消息之後,在代碼裏手動調用ack。這樣就可以避免消息還沒有處理完就ack。

7. 如何設置消息過期時間

請看https://blog.csdn.net/mytt_10566/article/details/90814609
摘錄:

1.設置隊列中所有消息過期時間

@Bean
public Queue msgQueue() {
    // 設置隊列消息過期時間
    Map<String, Object> arguments = new HashMap<>();
    arguments.put("x-message-ttl", 5000);
    return new Queue(msgQueue, true, false, false, arguments);
}

2.在發送時爲每條消息設置

String content = "test ttl message";
this.rabbitTemplate.convertAndSend(exchange, routingKey, content, message -> {
    MessageProperties messageProperties = message.getMessageProperties();
    // 設置這條消息的過期時間
    messageProperties.setExpiration("5000");
    return message;
});

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章