redis队列的三种分发模式(直联,主题,扇形)

直联config

package com.csrcb.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
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.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 Broker:它提供一种传输服务,它的角色就是维护一条从生产者到消费者的路线,保证数据能按照指定的方式进行传输, 
 Exchange:消息交换机,它指定消息按什么规则,路由到哪个队列。 
 Queue:消息的载体,每个消息都会被投到一个或多个队列。 
 Binding:绑定,它的作用就是把exchange和queue按照路由规则绑定起来. 
 Routing Key:路由关键字,exchange根据这个关键字进行消息投递。 
 vhost:虚拟主机,一个broker里可以有多个vhost,用作不同用户的权限分离。 
 Producer:消息生产者,就是投递消息的程序. 
 Consumer:消息消费者,就是接受消息的程序. 
 Channel:消息通道,在客户端的每个连接里,可建立多个channel.
 */
@Configuration
public class RabbitMqConfig {

    @Value("${spring.rabbitmq.host}")
    private String host;

    @Value("${spring.rabbitmq.port}")
    private int port;

    @Value("${spring.rabbitmq.username}")
    private String username;

    @Value("${spring.rabbitmq.password}")
    private String password;

    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHost;

    public static final String EXCHANGE_A = "my-mq-direct_exchange";
    public static final String EXCHANGE_B = "my-mq-exchange_B";
    public static final String EXCHANGE_C = "my-mq-exchange_C";


    public static final String QUEUE_A = "QUEUE_A";
    public static final String QUEUE_A_FAIL = "QUEUE_A_FAIL";
    public static final String QUEUE_B = "QUEUE_B";

    public static final String ROUTINGKEY_A = "spring-boot-routingKey_A";
    public static final String ROUTINGKEY_A_FAIL = "spring-boot-routingKey_A_FAIL";
    public static final String ROUTINGKEY_B = "spring-boot-routingKey_B";

    //建立一个连接容器,类型数据库的连接池
    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host,port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        connectionFactory.setPublisherConfirms(true);//确认机制
//        connectionFactory.setPublisherReturns(true);
        //发布确认,template要求CachingConnectionFactory的publisherConfirms属性设置为true
        return connectionFactory;
    }

    //RabbitMQ的使用入口
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    //必须是prototype类型
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate template = new RabbitTemplate(this.connectionFactory());
        template.setMessageConverter(this.jsonMessageConverter());
        template.setMandatory(true);
        return template;
    }


    //把交换机,队列,通过路由关键字进行绑定,写在RabbitConfig类当中
    /**
     * 针对消费者配置
     * 1. 设置交换机类型
     * 2. 将队列绑定到交换机
     FanoutExchange: 将消息分发到所有的绑定队列,无routingkey的概念
     HeadersExchange :通过添加属性key-value匹配
     DirectExchange:按照routingkey分发到指定队列
     TopicExchange:多关键字匹配
     */
    @Bean
    public DirectExchange testDirectExchange() {
        return new DirectExchange(EXCHANGE_A);
    }
    @Bean
    public DirectExchange testDirectExchangeB() {
        return new DirectExchange(EXCHANGE_B);
    }
    @Bean
    public DirectExchange testDirectExchangeC() {
        return new DirectExchange(QUEUE_B);
    }

    /**
     * 获取队列A
     * durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
     * exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
     * autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
     * return new Queue("TestDirectQueue",true,true,false);
     * 一般设置一下队列的持久化就好,其余两个就是默认false
     * @return
     */
    @Bean
    public Queue queueA() {
        return new Queue(QUEUE_A, true); //队列持久
    }
    @Bean
    public Queue queueB() {
        return new Queue(QUEUE_B, true); //队列持久
    }

    @Bean
    public Binding bindingB() {
        return BindingBuilder.bind(queueB()).to(testDirectExchangeB()).with(ROUTINGKEY_B);
    }

    @Bean
    public Queue queueAFail(){
        return new Queue(QUEUE_A_FAIL, true);//队列持久
    }

    //将队列和交换机绑定, 并设置用于匹配键:spring-boot-routingKey_A
    @Bean
    public Binding binding() {
        return BindingBuilder.bind(queueA()).to(testDirectExchange()).with(ROUTINGKEY_A);
    }

    @Bean
    public Binding bindingAFail() {
        return BindingBuilder.bind(queueAFail()).to(testDirectExchange()).with(RabbitMqConfig.ROUTINGKEY_A_FAIL);
    }

    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }

    //一个交换机可以绑定多个消息队列,也就是消息通过一个交换机,可以分发到不同的队列当中去。
//    /**
//     * 获取队列B
//     * @return
//     */
//    @Bean
//    public Queue queueB() {
//        return new Queue(QUEUE_B, true); //队列持久
//    }

//    @Bean
//    public Binding bindingB(){
//        return BindingBuilder.bind(queueB()).to(defaultExchange()).with(ROUTINGKEY_B);
//    }

//    @Bean
//    public CharacterEncodingFilter characterEncodingFilter() {
//        CharacterEncodingFilter filter = new CharacterEncodingFilter();
//        filter.setEncoding("UTF-8");
//        filter.setForceEncoding(true);
//        return filter;
//    }
}

 主题config

package com.csrcb.config;

import com.csrcb.common.Constants;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by michael on 2019-04-16.
 */
@Configuration
public class RabbitMQOfTopicConfig {

    @Bean
    public Queue firstQueue() {
        return new Queue(Constants.Topic.MAN);
    }

    @Bean
    public Queue secondQueue() {
        return new Queue(Constants.Topic.WOMAN);
    }

    @Bean
    public TopicExchange exchange() {
        return new TopicExchange(Constants.Topic.EXCHANGE);
    }

    /**
     * 将firstQueue和topicExchange绑定,而且绑定的键值为 topic.man
     * 这样只要是消息携带的路由键是 topic.man, 才会分发到该队列
     *
     * @return
     */
    @Bean
    public Binding bindingExchangeMessage() {
        return BindingBuilder.bind(firstQueue()).to(exchange()).with(Constants.Topic.MAN);
    }

    /**
     * 将secondQueue和topicExchange绑定,而且绑定的键值为用上通配路由键规则topic.#
     * 这样只要是消息携带的路由键是以topic.开头,都会分发到该队列
     *
     * @return
     */
    @Bean
    public Binding bindingExchangeMessage2() {
        return BindingBuilder.bind(secondQueue()).to(exchange()).with(Constants.Topic.ROUTING_KEY);
    }
}

扇形config

package com.csrcb.config;

import com.csrcb.common.Constants;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by michael on 2019-04-19.
 */
@Configuration
public class RabbitMQOfFanoutConfig {

    /**
     * 创建三个队列 :fanout.A   fanout.B  fanout.C
     * 将三个队列都绑定在交换机 fanoutExchange 上
     * 因为是扇型交换机, 路由键无需配置, 配置也不起作用
     */

    @Bean
    public Queue queueAA() {
        return new Queue(Constants.Fanout.QUEUE_A);
    }

    @Bean
    public Queue queueBB() {
        return new Queue(Constants.Fanout.QUEUE_B);
    }

    @Bean
    public Queue queueCC() {
        return new Queue(Constants.Fanout.QUEUE_C);
    }

    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange(Constants.Fanout.EXCHANGE);
    }

    @Bean
    public Binding bindingExchangeA() {
        return BindingBuilder.bind(queueAA()).to(fanoutExchange());
    }

    @Bean
    public Binding bindingExchangeB() {
        return BindingBuilder.bind(queueBB()).to(fanoutExchange());
    }

    @Bean
    public Binding bindingExchangeC() {
        return BindingBuilder.bind(queueCC()).to(fanoutExchange());
    }
}

 静态变量

package com.csrcb.common;

/**
 * 常量
 * <p>
 * Created by michael on 2019-04-16.
 */
public class Constants {

    public static final int MESSAGE_NUMBER = 3;// 消息数量
    public static String DELAY_QUEUE="delayqueue";
    public static String IMMEDIATE_QUEUE="immediateqqueue";
    public static String IMMEDIATE_EXCHANGE="immediatequeue";
    public static String IMMEDIATE_ROUTING_KEY="immediaterouring";
    public static String DELAY_ROUTING_KEY="delayrouting";
    public static String DEAD_LETTER_EXCHANGE="dead_letter_exchange";





    /**
     * 直接
     */
    public interface Direct {
        // 交换机名称
        String EXCHANGE = "TestDirectExchange";
        String EXCHANGE_SPECIAL = "specialDirectExchange";
        // 队列名称
        String QUEUE = "TestDirectQueue";
        // 绑定「队列」与「交换机」名称
        String ROUTING_KEY = "TestDirectRouting";
    }

    /**
     * 主题
     */
    public interface Topic {
        // 交换机名称
        String EXCHANGE = "TestTopicExchange";
        // 绑定键
        String MAN = "topic.man";
        String WOMAN = "topic.woman";
        // 绑定「队列」与「交换机」名称
        String ROUTING_KEY = "topic.#";
    }

    /**
     * 扇形
     */
    public interface Fanout {
        // 交换机名称
        String EXCHANGE = "TestFanoutExchange";
        // 队列名称
        String QUEUE_A = "fanout.A";
        String QUEUE_B = "fanout.B";
        String QUEUE_C = "fanout.C";
    }
}

 生产者controler

package com.csrcb.controller;

import com.csrcb.common.Constants;
import com.csrcb.config.RabbitMqConfig;
import com.csrcb.service.QueueMessageService;
import com.sun.jmx.snmp.Timestamp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Stream;

/**
 * @Classname ProvoderSend
 * @Description 生产者路由
 * @Date 2020/8/4 17:24
 * @Created by gangye
 */
@RestController
@RequestMapping(value = "/provider")
@Slf4j
public class ProvoderSend {
    @Autowired
    private QueueMessageService queueMessageService;

    @PostMapping("/sendA")
    public String sendMessage() {
        try {
            Map<String, Object> messageMap = new HashMap<>();
            messageMap.put("messageId", UUID.randomUUID().toString());
            messageMap.put("messageData", "测试信息");
            messageMap.put("createTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            queueMessageService.send(messageMap, RabbitMqConfig.EXCHANGE_A, RabbitMqConfig.ROUTINGKEY_A);
            return "success";
        } catch (Exception e) {
            log.error("" + e);
            return "error";
        }
    }

    @PostMapping("/sendB")
    public String sendMessageB() {
        try {
            Map<String, Object> messageMap = new HashMap<>();
            messageMap.put("messageId", UUID.randomUUID().toString());
            messageMap.put("messageData", "测试信息");
            messageMap.put("createTime", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
            queueMessageService.send(messageMap, RabbitMqConfig.EXCHANGE_B, RabbitMqConfig.ROUTINGKEY_B);
            return "success";
        } catch (Exception e) {
            log.error("" + e);
            return "error";
        }
    }

    /**
     * topic
     */
    @PostMapping("/sendtopicman")
    public List<Map<String, Object>> sendMessageForMan() {
        List<Map<String, Object>> mapList = new ArrayList<>();
        Stream.iterate(0, i -> i + 1).limit(Constants.MESSAGE_NUMBER).forEach(i -> {
            String msgId = String.valueOf(UUID.randomUUID());
            String msgData = "hello, I come from topic exchange「man」.";
            String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            Map<String, Object> map = new HashMap<>();
            map.put("msgId", msgId);
            map.put("msgData", msgData);
            map.put("createTime", createTime);
            queueMessageService.convertAndSend(Constants.Topic.EXCHANGE, Constants.Topic.MAN, map);
            mapList.add(map);
        });
        return mapList;
    }

    @PostMapping("/sendtopicwoman")
    public List<Map<String, Object>> sendMessageForWoman() {
        List<Map<String, Object>> mapList = new ArrayList<>();
        Stream.iterate(0, i -> i + 1).limit(Constants.MESSAGE_NUMBER).forEach(i -> {
            String msgId = String.valueOf(UUID.randomUUID());
            String msgData = "hello, I come from topic exchange「woman」.";
            String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
            Map<String, Object> map = new HashMap<>();
            map.put("msgId", msgId);
            map.put("msgData", msgData);
            map.put("createTime", createTime);
            queueMessageService.convertAndSend(Constants.Topic.EXCHANGE, Constants.Topic.WOMAN, map);
            mapList.add(map);
        });
        return mapList;
    }

    @PostMapping("/sendfanout")
    public Map<String, Object> sendMessagefanout() {
        String msgId = String.valueOf(UUID.randomUUID());
        String msgData = "message: test Fanout Message ";
        String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        Map<String, Object> map = new HashMap<>();
        map.put("msgId", msgId);
        map.put("msgData", msgData);
        map.put("createTime", createTime);
        queueMessageService.convertAndSend(Constants.Fanout.EXCHANGE, null, map);
        return map;
    }

    @PostMapping("/senddeadqueue")
    public void sendMessagefanout11111() {
      this.queueMessageService.senddelayqueue("fa song si xin dui lie",20000l);
    }
}

 

package com.csrcb.service.impl;

import com.csrcb.common.Constants;
import com.csrcb.service.QueueMessageService;
import com.sun.jmx.snmp.Timestamp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.UUID;

/**
 * @Classname QueueMessageServiceImpl
 * @Date 2020/8/5 15:55
 * @Created by gangye
 */
@Service
@Slf4j
public class QueueMessageServiceImpl implements QueueMessageService {

    //由于配置类RabbitMqConfig配置类中的rabbitTemplate的scope属性设置为ConfigurableBeanFactory.SCOPE_PROTOTYPE
    private RabbitTemplate rabbitTemplate;

    @Autowired
    public QueueMessageServiceImpl(RabbitTemplate rabbitTemplate){
        this.rabbitTemplate = rabbitTemplate;
        //设置回调为当前类对象
        rabbitTemplate.setConfirmCallback(this);
    }

    @Override
    public void send(Object message, String exchange, String queueRoutingKey) throws Exception {
        //构建回调id为uuid
        String callBackId = UUID.randomUUID().toString();
        CorrelationData correlationId = new CorrelationData(callBackId);
        log.info("开始发送消息内容:{}",message.toString());
        //发送消息到消息队列
        rabbitTemplate.convertAndSend(exchange, queueRoutingKey, message, correlationId);
        log.info("发送定制的回调id:{}",callBackId);
    }

    @Override
    public void convertAndSend(String exchange, String man, Map<String, Object> map) {
        //构建回调id为uuid
        String callBackId = UUID.randomUUID().toString();
        CorrelationData correlationId = new CorrelationData(callBackId);
        rabbitTemplate.convertAndSend(exchange, man, map,correlationId);
        log.info("发送定制的回调id:{}",callBackId);

    }


    public void senddelayqueue(String user,Long time) {
        log.info("消息已经发送,时间为:{}",new Timestamp(System.currentTimeMillis()));
        String callBackId = UUID.randomUUID().toString();
        CorrelationData correlationId = new CorrelationData(callBackId);
        this.rabbitTemplate.convertAndSend(
                Constants.DEAD_LETTER_EXCHANGE,
                // routingKey
                Constants.DELAY_ROUTING_KEY,
                user,
                message -> {
                    // 设置延迟毫秒值
                    message.getMessageProperties().setExpiration(String.valueOf(time));
                    return message;




                },correlationId);
    }


    /**
     * 消息回调确认方法
     * @param correlationData 请求数据对象
     * @param ack 是否发送成功
     * @param s
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String s) {
        log.info(" 回调id:" + correlationData.getId());
        if (ack) {
            log.info("消息发送成功");
        } else {
            log.info("消息发送失败:" + s);
        }
    }
}

消费者处理消息

package com.csrcb.service;

import com.csrcb.common.Constants;
import com.csrcb.config.RabbitMqConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.util.Map;

import static com.csrcb.common.Constants.Topic.MAN;
import static com.csrcb.common.Constants.Topic.WOMAN;


/**
 * @Classname ConsumerService
 * @Date 2020/8/7 14:36
 * @Created by gangye
 */
@Component
@Slf4j
public class ConsumerService {

    @RabbitListener(queues = RabbitMqConfig.QUEUE_A)
    @RabbitHandler
    public void consumeMessage(Message message){
        log.info("收到的消息:{}",message);
    }

    @RabbitListener(queues = RabbitMqConfig.QUEUE_B)
    @RabbitHandler
    public void consumeMessageC(Message message){
        log.info("收到的消息B:{}",message);
    }
    @RabbitListener(queues = WOMAN)
    @RabbitHandler
    public void consumeMessaged(Message message){
        log.info("收到的消息WOMAN:{}",message);
    }
    @RabbitListener(queues = MAN)
    @RabbitHandler
    public void consumeMessageMan(Message message){
        log.info("收到的消息MAN:{}",message);
    }
    @RabbitListener(queues = Constants.Fanout.QUEUE_A)
    @RabbitHandler
    public void consumeMessageA(Message message){
        log.info("收到的消息fouantA:{}",message);
    }
    @RabbitListener(queues = Constants.Fanout.QUEUE_B)
    @RabbitHandler
    public void consumeMessageB(Message message){
        log.info("收到的消息fouantB:{}",message);
    }
    @RabbitListener(queues = Constants.Fanout.QUEUE_C)
    @RabbitHandler
    public void consumeMessageC1(Message message){
        log.info("收到的消息fouantC:{}",message);
    }

    @RabbitListener(queues = "immediateqqueue")
    @RabbitHandler
    public void consumeMessagedelaymessage(Message message){
        log.info("收到的消息fouantC:{}",message);
    }


}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值