rabbitmq學習及簡單運用

前言

本文結合springboot學習rabbitmq(springboot版本2.2.2)

demo地址:https://github.com/foxiaotao/springboot-rabbitmq-demo

rabbitmq就是生產者把消息放到Exchange,Exchange根據規則把消息路由到Queue,消費者從Queue消費消息。

一、RabbitMq 構成

1、Exchange(交換器)

生產者將消息發佈出去,消息首先就得Exchange來接受,發送消息時,不用考慮Queue,消息一經生產者發送,那麼消息就在Exchange中(Exchange並不具備儲存消息,這裏便於理解才這樣說)。

1)、Exchange主要類型介紹

direct(默認)

headers

fanout

topic

其中headers交換器允許你匹配AMQP消息的header而非路由鍵,除此之外headers交換器和direct交換器完全一致,但性能卻很差,幾乎用不到,所以我們本文也不做講解。

注意:fanout、topic交換器是沒有歷史數據的,也就是說對於中途創建的隊列,獲取不到之前的消息。

2)、direct 模式

Exchange 和 Queue 是1:1關聯,關聯就叫做路由鍵(RoutingKey),需要申明Exchange和Queue,以及Exchange和Queue的關聯RoutingKey。申明綁定關係。

圖1:mq-direct
    @Bean("dlxExchangeBean")
    public DirectExchange dlxExchangeBean() {
        return new DirectExchange(dlxExchange);
    }

    @Bean("dlxQueueBean")
    public Queue dlxQueueBean() {
        return new Queue(dlxQueue);
    }


    @Bean
    public Binding binding(@Qualifier("dlxExchangeBean") DirectExchange dlxExchange, @Qualifier("dlxQueueBean") Queue dlxQueue) {
        return BindingBuilder.bind(dlxQueue).to(dlxExchange).with(dlxRoutingKey);
    }

這種方式只是定義在java 代碼中,如果在rabbitmq上沒有創建過,還需要通過後臺手動創建Exchange和Queue,並通過RoutingKey綁定。

每次發送消息需要指定Exchange 和 RoutingKey

例如:圖1:中發送消息指定Exchange 和 RoutingKey=rk1,那麼就只有Queue1收到消息,Queue2和Queue3不會有消息。只有Consumer1 消費到了消息。

3)、fanout模式

這種模式(可以參考廣播模式),只需要將Exchange與Queue申明綁定,Exchange會把消息發佈到所有Queue,這種不需要路由鍵。

圖2:fanout
    @Bean(name="userExchange")
    public FanoutExchange userFanoutExchange() {
        return new FanoutExchange(userRegisterExchange, true, false);
    }
    @Bean(name = "userRegisterQueue")
    public Queue userRegisterQueue() {  //還款的queue
        return new Queue(userRegisterQueue);
    }

首次需要在rabbitmq後臺創建並綁定,綁定不需要RoutingKey。如圖2,消息只有發送Exchange,所有Queue都會用相同消息。

4)、topic模式

簡單的說是前置匹配模式。路由鍵必須必須包含xx.* 或者xx.#

*表示一個字符

#表示任意個字符

圖3:topic

如圖3關係所示:

如果發送RoutingKey=odd.a,那麼收到消息的有Queue1、Queue2;

如果發送RoutingKey=odd.age,那麼收到消息的有Queue1;

2、Queue(隊列)

隊列就是消息的載體,車行駛在公路上,那麼消息就放在隊列中。通過Exchange的介紹,已經引入Queue,Queue是消息存放和通行的地方。Queue和Exchange的需要通過綁定建立關係。這種關係是長久存在的,不會因消息的改變而變化。就是定義Exchange和Queue的路由規則,消息攜帶的規則在總的Exchange中匹配,找到匹配的就將消息路由到Queue。

消費者監聽指定隊列,只要隊列中有消息,監聽程序獲取到消息。供消費者使用。

3、broker(消息體)

就是Exchange 和Queue 全部的集合

二、消息的發送與接收

1、消息發送

消息發送到mq,在springboot中引入import org.springframework.amqp.rabbit.core.RabbitTemplate類,

rabbitTemplate.convertAndSend完成發送消息。

定義RabbitTemplate:

    @Bean("delayCachingConnectionFactory")
    public CachingConnectionFactory delayCachingConnectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
        connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CONNECTION);
        connectionFactory.setChannelCacheSize(1024);
        connectionFactory.setConnectionCacheSize(1024);
        connectionFactory.setUsername(user);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(kaLoanVirtualHost);
        connectionFactory.setPublisherReturns(false);
        connectionFactory.setPublisherConfirms(false);
        return connectionFactory;
    }

    @Bean(name = "delayRabbitTemplate")
    public RabbitTemplate delayRabbitTemplate(@Qualifier("delayCachingConnectionFactory") ConnectionFactory connectionFactory) {
        //RabbitTemplate rabbitTemplate = techRabbitBuilder.createRabbitTemplate(connectionFactory);
        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
        // 指定默認exchange
        rabbitTemplate.setExchange(delayExchange);
        // 指定默認路由鍵
        rabbitTemplate.setRoutingKey(delayRoutingKey);
        return rabbitTemplate;
    }

    /**
     * 申明exchange
     * @return
     */
    @Bean("delayExchange")
    public DirectExchange delayExchangeBean() {
        return new DirectExchange(delayExchange);
    }

    @Bean("delayQueue")
    public Queue orderQueueBean() {
        return new Queue(delayQueue, true, false, false, null);
    }

    @Bean
    public Binding orderBinding(@Qualifier("delayExchange") DirectExchange delayExchange, @Qualifier("delayQueue") Queue delayQueue) {
        return BindingBuilder.bind(delayQueue).to(delayExchange).with(delayRoutingKey);
    }

在config中申明RabbitTemplate delayRabbitTemplate,指定Bean.name,要是在程序中沒有多個RabbitTemplate可以不用指定。@Bean的方法名稱也同樣是Bean的默認name。

定義完RabbitTemplate,就可以發送消息到mq 了。

package cn.quantgroup.clf.api.delayretry.service.senddelay;

import cn.quantgroup.clf.api.delayretry.model.DelayEventBody;
import cn.quantgroup.clf.util.JSONTools;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

/**
 * @description: 消息發送
 * @author:tao
 * @create: 2019-12-18 14:20
 */
@Slf4j
@Service
public class SendDelayEventMqServiceImpl implements SendDelayEventService {

    @Resource(name = "delayRabbitTemplate")
    private RabbitTemplate rabbitTemplate;

    @Override
    public void sendDelayEvent(DelayEventBody delayEventBody) {
        rabbitTemplate.convertAndSend(JSONTools.serialize(delayEventBody));
    }
}

顯然可以看出Exchange類型是Direct,這裏發送消息的時候並沒有指定exchange和routingKey,是因爲在RabbitTemplate定義的時候設置了默認的exchange和routingKey,當然也可以在每次發送消息的時候指定Exchange和routingKey,指定方法爲,對應的exchange和routingKey的name。那麼這樣消息就發送到mq中了。

如圖所示,在隊列ka.dlx.queue中, 有5條消息。Features:D消息持久化,DLX、DLK死信;State:idle空閒、running運行。Ready、Unacked是消息的狀態,Ready準備好的消息數量,Unacked:消費者還沒有ack確認的消息數量。

當消息還在隊列中的時候,可以通過Get Message 查詢消息。

消息的發送只與Exchange和routingKey相關,不需要定義Queue。

2、消息接受

消息的接受,如果只是純粹的接受消息,與Exchange和routingKey無關,消費者監聽Queue即可。

package cn.quantgroup.cashloanflow.config.mq;

import cn.quantgroup.clf.api.delayretry.service.consumer.ConsumeDelayEventService;
import cn.quantgroup.tech.brave.service.ITechRabbitBuilder;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerEndpoint;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

/**
 * @author: amen
 * @date: 2019-01-08
 */

@Configuration
public class DelayMqConfig {

    @Value("${ka.rabbitmq.connection.host}")
    private String host;
    @Value("${ka.rabbitmq.connection.port}")
    private Integer port;
    @Value("${ka.rabbitmq.connection.user}")
    private String user;
    @Value("${ka.rabbitmq.connection.password}")
    private String password;
    @Value("${ka.rabbitmq.connection.virtual-host}")
    private String kaLoanVirtualHost;


    @Value("${ka.rabbitmq.delay.queue:ka.delay.queue}")
    private String delayQueue;
    @Value("${ka.rabbitmq.delay.exchange:ka.delay.exchange}")
    private String delayExchange;
    @Value("${ka.rabbitmq.delay.routingKey:ka.delay.routingKey}")
    private String delayRoutingKey;

    @Value("${ka.rabbitmq.dlx.queue:ka.dlx.queue}")
    private String dlxQueue;
    @Value("${ka.rabbitmq.dlx.exchange:ka.dlx.exchange}")
    private String dlxExchange;
    @Value("${ka.rabbitmq.dlx.routingKey:ka.dlx.routingKey}")
    private String dlxRoutingKey;



    @Autowired
    @Qualifier( "techRabbitBuilder" )
    private ITechRabbitBuilder techRabbitBuilder;

    @Bean("delayCachingConnectionFactory")
    public CachingConnectionFactory delayCachingConnectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
        connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CONNECTION);
        connectionFactory.setChannelCacheSize(1024);
        connectionFactory.setConnectionCacheSize(1024);
        connectionFactory.setUsername(user);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(kaLoanVirtualHost);
        connectionFactory.setPublisherReturns(false);
        connectionFactory.setPublisherConfirms(false);
        return connectionFactory;
    }


    /**
     * 申明死信隊列
     * @return
     */
    @Bean("dlxExchangeBean")
    public DirectExchange dlxExchangeBean() {
        return new DirectExchange(dlxExchange);
    }

    @Bean("dlxQueueBean")
    public Queue dlxQueueBean() {
        return new Queue(dlxQueue);
    }


    @Bean
    public Binding binding(@Qualifier("dlxExchangeBean") DirectExchange dlxExchange, @Qualifier("dlxQueueBean") Queue dlxQueue) {
        return BindingBuilder.bind(dlxQueue).to(dlxExchange).with(dlxRoutingKey);
    }


    /**
     * 申明延時隊列
     * @return
     */
    @Bean("delayExchange")
    public DirectExchange delayExchangeBean() {
        return new DirectExchange(delayExchange);
    }

    @Bean("delayQueue")
    public Queue orderQueueBean() {
        Map<String, Object> arguments = new HashMap<>(4);
        // 綁定該隊列到死信交換機
        arguments.put("x-dead-letter-exchange", dlxExchange);
        arguments.put("x-dead-letter-routing-key", dlxRoutingKey);
        return new Queue(delayQueue, true, false, false, arguments);
    }

    @Bean
    public Binding orderBinding(@Qualifier("delayExchange") DirectExchange delayExchange, @Qualifier("delayQueue") Queue delayQueue) {
        return BindingBuilder.bind(delayQueue).to(delayExchange).with(delayRoutingKey);
    }




    /**
     * 監聽死信隊列
     * @param consumeDelayEventService
     * @param cachingConnectionFactory
     * @param dlxQueue
     * @return
     */
    @Bean("delaySimpleMessageListenerContainer")
    public SimpleMessageListenerContainer delaySimpleMessageListenerContainer(@Qualifier("consumeMqDelayEventService") ConsumeDelayEventService consumeDelayEventService,
                                                                                         @Qualifier("delayCachingConnectionFactory") CachingConnectionFactory cachingConnectionFactory,
                                                                                         @Qualifier("dlxQueueBean") Queue dlxQueue) {
        SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory = techRabbitBuilder
                .createSimpleRabbitListenerContainerFactory(cachingConnectionFactory);
        SimpleRabbitListenerEndpoint simpleRabbitListenerEndpoint = new SimpleRabbitListenerEndpoint();
        simpleRabbitListenerEndpoint.setQueues(dlxQueue);
        simpleRabbitListenerEndpoint.setMessageListener(new MessageListenerAdapter(consumeDelayEventService, "consumeMessage"));
        SimpleMessageListenerContainer container = simpleRabbitListenerContainerFactory.createListenerContainer(simpleRabbitListenerEndpoint);
        container.setAcknowledgeMode(AcknowledgeMode.AUTO);
        container.setConcurrentConsumers(10);
        container.start();
        return container;

    }


    @Bean(name = "delayRabbitTemplate")
    public RabbitTemplate delayRabbitTemplate(@Qualifier("delayCachingConnectionFactory") ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = techRabbitBuilder.createRabbitTemplate(connectionFactory);
        rabbitTemplate.setExchange(delayExchange);
        rabbitTemplate.setRoutingKey(delayRoutingKey);
        return rabbitTemplate;
    }
}

 

package cn.quantgroup.clf.api.delayretry.service.consumer;

/**
 * @description: 消費延遲隊列消息
 * @author:tao
 * @create: 2019-12-23 11:49
 */
public interface ConsumeDelayEventService {

    void consumeMessage(String message);
}

實現ConsumeDelayEventService接口,即可收到消息。

發佈了39 篇原創文章 · 獲贊 1 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章