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万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章