六.消息的交换:direct类型的Exchange(通过消息的routing key比较queue的key)

本文介绍如何使用RabbitMQ的Direct Exchange实现不同级别的日志消息路由,通过配置不同的routing key来精确控制消息流向哪些消费者。具体展示了生产者如何发送带有特定routing key的消息,并分别实现了两种类型的消费者:一种仅接收info级别的日志,另一种则同时接收info和error级别的日志。

根据routingKey匹配消息到符合的消费者消费消息

例:消费者1只消费info级别的日志,消费者2即消费info级别也消费error级别

生产者和消费者的pom.xml和上一章一样

一.生产者Producer
1.发送Exchange类型为direct的消息的类:LogReceiveDirect.java

package com.rabbit.exchange;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class LogSenderDirect {


    private Logger logger = LoggerFactory.getLogger(LogSenderFanout.class);

    //ConnectionFactory和Connection在正式开发时需要设置成单例
    private ConnectionFactory connectionFactory;
    private Connection connection;
    private Channel channel;

    /**
     * 在构造函数中获取连接
     */
    public LogSenderDirect(){
        super();
        try {
            connectionFactory = new ConnectionFactory();
            connectionFactory.setHost("127.0.0.1");
            connection = connectionFactory.newConnection();
            channel = connection.createChannel();
        } catch (Exception e) {
            logger.error("获取连接时出错...");
        }
    }

    /**
     * 关闭连接的方法
     */
    public boolean closeAll(){
        try {
            this.channel.close();
            this.connection.close();
        } catch (Exception e) {
            logger.error("关闭连接时异常...");
            return false;
        }
        return true;
    }

    /**
     * 发送消息到交换中心
     */
    public void sendMessage(String message,String routingKey){
        try {
            //声明一个exchange,名字为logs,类型为direct
            channel.exchangeDeclare("logs", "direct");
            //发布消息到exchange上
            /**
             * 1.指定exchange的名字
             * 2.direct类型
             * 3.null...
             * 3.发送的消息
             */
            channel.basicPublish("logs", routingKey, null, message.getBytes());
            logger.debug("发送direct类型的消息"+message+"到exchange交换中心.");
        } catch (Exception e) {
            logger.error("消息发送失败:"+e);
        }
    }

}

2.启动测试的main方法:ExchangeDirectMain.java

package com.rabbit.main;

import com.rabbit.exchange.LogSenderDirect;

public class ExchangeDirectMain {

    public static void main(String[] args) throws InterruptedException {
        LogSenderDirect logSender = new LogSenderDirect();
        //轮流每一秒发送info和error的消息(让消费者1接受info和error级别消息,消费者2只接受info级别消息)
        int i = 0;
        while (true) {
            if(i%2 == 0){
                logSender.sendMessage("hello tiglle"+i+":info","info");
            }else{
                logSender.sendMessage("hello tiglle"+i+":error","error");
            }
            Thread.sleep(1000);
            i++;
        }
    }

}

二.只消费info级别的Consumer
1.消费消息的类:LogReceiveDirect.java

package com.rabbit.exchange;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class LogReceiveDirect {

    private Logger logger = LoggerFactory.getLogger(LogReceiveDirect.class);

    //正式开发ConnectionFactory和Connection应该设置为单例
    private ConnectionFactory connectionFactory;
    private Connection connection;
    private Channel channel;

    /**
     * 在构造函数中获取连接
     */
    public LogReceiveDirect(){
        super();//Objece为其父类...
        try {
            connectionFactory = new ConnectionFactory();
            connection  = connectionFactory.newConnection();
            channel = connection.createChannel();
            //声明exchange,防止生成者没启动的时候,exchange不存在(生成者和消费者总有一个要先声明)
            channel.exchangeDeclare("logs", "direct");
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    /**
     * 关闭连接的方法
     */
    public boolean closeAll(){
        try {
            this.channel.close();
            this.connection.close();
        } catch (Exception e) {
            logger.error("关闭连接异常:"+e);
            return false;
        }
        return true;
    }

    /**
     * 消费消息
     */
    public void messageReceive(){
        try {
            //获取临时列队:自己声明队列是比较麻烦的,
            //因此,RabbitMQ提供了简便的获取临时队列的方法,该队列会在连接断开后销毁
            String queueName = channel.queueDeclare().getQueue();
            //把获取的临时列队绑定到logs这个exchange交换中心,只接受info级别日志
            /**
             * 1.列队名称
             * 2.交换中心的名称
             * 3.routingKey和生产者发布消息的时候指定的一样
             */
            channel.queueBind(queueName, "logs", "info");
            //定义一个Consumer消费logs的消息
            Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body) throws IOException {
                    // TODO Auto-generated method stub
                    String message = new String(body,"UTF-8");
                    logger.debug("我是打印日志的消费者:"+message);
                }
            };
            //自动确认为true,接收到消息后该消息就销毁了
            channel.basicConsume(queueName, true, consumer);
        } catch (Exception e) {
            logger.error("消费消息时异常:"+e);
        }
    }
}

2.启动监听的main方法:ExchangeDirectMain.java

package com.rabbit.main;

import com.rabbit.exchange.LogReceiveDirect;

public class ExchangeDirectMain {

    public static void main(String[] args) {
        LogReceiveDirect logReceive = new LogReceiveDirect();
        logReceive.messageReceive();
    }

}

三.同时消费info和error级别消息的消费者
1.消费消息的类:LogReceiveDirect.java

package com.rabbit.exchange;

import java.io.IOException;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;

public class LogReceiveDirect {

    private Logger logger = LoggerFactory.getLogger(LogReceiveDirect.class);

    //正式开发ConnectionFactory和Connection应该设置为单例
    private ConnectionFactory connectionFactory;
    private Connection connection;
    private Channel channel;

    /**
     * 在构造函数中获取连接
     */
    public LogReceiveDirect(){
        super();//Objece为其父类...
        try {
            connectionFactory = new ConnectionFactory();
            connection  = connectionFactory.newConnection();
            channel = connection.createChannel();
            //声明exchange,防止生成者没启动的时候,exchange不存在(生成者和消费者总有一个要先声明)
            channel.exchangeDeclare("logs", "direct");
        } catch (Exception e) {
            // TODO: handle exception
        }
    }

    /**
     * 关闭连接的方法
     */
    public boolean closeAll(){
        try {
            this.channel.close();
            this.connection.close();
        } catch (Exception e) {
            logger.error("关闭连接异常:"+e);
            return false;
        }
        return true;
    }

    /**
     * 消费消息
     */
    public void messageReceive(){
        try {
            //获取临时列队:自己声明队列是比较麻烦的,
            //因此,RabbitMQ提供了简便的获取临时队列的方法,该队列会在连接断开后销毁
            String queueName = channel.queueDeclare().getQueue();
            //把获取的临时列队绑定到logs这个exchange交换中心,绑定两个routingKey(同时接受info和error级别日志),不会覆盖
            /**
             * 1.列队名称
             * 2.交换中心的名称
             * 3.routingKey和生产者发布消息的时候指定的一样
             */
            channel.queueBind(queueName, "logs", "info");
            channel.queueBind(queueName, "logs", "error");
            //定义一个Consumer消费logs的消息
            Consumer consumer = new DefaultConsumer(channel) {
                @Override
                public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,byte[] body) throws IOException {
                    // TODO Auto-generated method stub
                    String message = new String(body,"UTF-8");
                    logger.debug("我是写硬盘的消费者:"+message);
                }
            };
            //自动确认为true,接收到消息后该消息就销毁了
            channel.basicConsume(queueName, true, consumer);
        } catch (Exception e) {
            logger.error("消费消息时异常:"+e);
        }
    }
}

2.启动监听的main方法:ExchangeDirectMain.java

package com.rabbit.main;

import com.rabbit.exchange.LogReceiveDirect;

public class ExchangeDirectMain {

    public static void main(String[] args) {
        LogReceiveDirect logReceive = new LogReceiveDirect();
        logReceive.messageReceive();
    }

}

这时,提供者的info级别日志会被消费者1和消费者2同时消费,error级别的日志只会被消费者2消费

package org.jeecg.boot.starter.rabbitmq.client; import cn.hutool.core.util.ObjectUtil; import lombok.extern.slf4j.Slf4j; import org.jeecg.boot.starter.rabbitmq.event.EventObj; import org.jeecg.boot.starter.rabbitmq.event.JeecgRemoteApplicationEvent; import org.jeecg.boot.starter.rabbitmq.exchange.DelayExchangeBuilder; import org.jeecg.common.annotation.RabbitComponent; import org.jeecg.common.base.BaseMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.amqp.core.*; import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cloud.bus.BusProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationEventPublisher; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.annotation.Resource; import java.lang.reflect.Method; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; /** * 消息队列客户端 */ @Slf4j @Configuration public class RabbitMqClient { private static final Logger logger = LoggerFactory.getLogger(RabbitMqClient.class); private final RabbitAdmin rabbitAdmin; private final RabbitTemplate rabbitTemplate; @Resource private SimpleMessageListenerContainer messageListenerContainer; @Resource BusProperties busProperties; @Resource private ApplicationEventPublisher publisher; @Resource private ApplicationContext applicationContext; @Bean public void initQueue() { Map<String, Object> beansWithRqbbitComponentMap = this.applicationContext.getBeansWithAnnotation(RabbitComponent.class); Class<? extends Object> clazz = null; for (Map.Entry<String, Object> entry : beansWithRqbbitComponentMap.entrySet()) { log.info("初始化队列............"); //获取到实例对象的class信息 clazz = entry.getValue().getClass(); Method[] methods = clazz.getMethods(); RabbitListener rabbitListener = clazz.getAnnotation(RabbitListener.class); if (ObjectUtil.isNotEmpty(rabbitListener)) { createQueue(rabbitListener); } for (Method method : methods) { RabbitListener methodRabbitListener = method.getAnnotation(RabbitListener.class); if (ObjectUtil.isNotEmpty(methodRabbitListener)) { createQueue(methodRabbitListener); } } } } /** * 初始化队列 * * @param rabbitListener */ private void createQueue(RabbitListener rabbitListener) { String[] queues = rabbitListener.queues(); DirectExchange directExchange = createExchange(DelayExchangeBuilder.DELAY_EXCHANGE); //创建交换机 rabbitAdmin.declareExchange(directExchange); if (ObjectUtil.isNotEmpty(queues)) { for (String queueName : queues) { Queue queue = new Queue(queueName); addQueue(queue); Binding binding = BindingBuilder.bind(queue).to(directExchange).with(queueName); rabbitAdmin.declareBinding(binding); log.info("队列创建成功:" + queueName); } } } private Map sentObj = new HashMap<>(); @Autowired public RabbitMqClient(RabbitAdmin rabbitAdmin, RabbitTemplate rabbitTemplate) { this.rabbitAdmin = rabbitAdmin; this.rabbitTemplate = rabbitTemplate; } /** * 发送远程事件 * * @param handlerName * @param baseMap */ public void publishEvent(String handlerName, BaseMap baseMap) { EventObj eventObj = new EventObj(); eventObj.setHandlerName(handlerName); eventObj.setBaseMap(baseMap); publisher.publishEvent(new JeecgRemoteApplicationEvent(eventObj, busProperties.getId())); } /** * 转换Message对象 * * @param messageType 返回消息类型 MessageProperties类中常量 * @param msg * @return */ public Message getMessage(String messageType, Object msg) { MessageProperties messageProperties = new MessageProperties(); messageProperties.setContentType(messageType); Message message = new Message(msg.toString().getBytes(), messageProperties); return message; } /** * 有绑定KeyExchange发送 * * @param routingKey * @param msg */ public void sendMessageToExchange(TopicExchange topicExchange, String routingKey, Object msg) { Message message = getMessage(MessageProperties.CONTENT_TYPE_JSON, msg); rabbitTemplate.send(topicExchange.getName(), routingKey, message); } /** * 没有绑定KEYExchange发送 * * @param exchange * @param msg */ public void sendMessageToExchange(TopicExchange topicExchange, AbstractExchange exchange, String msg) { addExchange(exchange); logger.info("RabbitMQ send " + exchange.getName() + "->" + msg); rabbitTemplate.convertAndSend(topicExchange.getName(), msg); } /** * 发送消息 * * @param queueName 队列名称 * @param params 消息内容map */ public void sendMessage(String queueName, Object params) { log.info("发送消息到mq"); try { rabbitTemplate.convertAndSend(DelayExchangeBuilder.DELAY_EXCHANGE, queueName, params, message -> { return message; }); } catch (Exception e) { e.printStackTrace(); } } /** * 发送消息 * * @param queueName 队列名称 */ public void sendMessage(String queueName) { this.send(queueName, this.sentObj, 0); this.sentObj.clear(); } public RabbitMqClient put(String key, Object value) { this.sentObj.put(key, value); return this; } /** * 延迟发送消息 * * @param queueName 队列名称 * @param params 消息内容params * @param expiration 延迟时间 单位毫秒 */ public void sendMessage(String queueName, Object params, Integer expiration) { this.send(queueName, params, expiration); } private void send(String queueName, Object params, Integer expiration) { Queue queue = new Queue(queueName); addQueue(queue); CustomExchange customExchange = DelayExchangeBuilder.buildExchange(); rabbitAdmin.declareExchange(customExchange); Binding binding = BindingBuilder.bind(queue).to(customExchange).with(queueName).noargs(); rabbitAdmin.declareBinding(binding); SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); log.debug("发送时间:" + sf.format(new Date())); messageListenerContainer.setQueueNames(queueName); /* messageListenerContainer.setMessageListener(new MqListener<Message>() { @Override public void onMessage(Message message, Channel channel) { MqListener messageListener = SpringContextHolder.getHandler(queueName + "Listener", MqListener.class); if (ObjectUtil.isNotEmpty(messageListener)) { messageListener.onMessage(message, channel); } } });*/ rabbitTemplate.convertAndSend(DelayExchangeBuilder.DEFAULT_DELAY_EXCHANGE, queueName, params, message -> { if (expiration != null && expiration > 0) { message.getMessageProperties().setHeader("x-delay", expiration); } return message; }); } /** * 给queue发送消息 * * @param queueName */ public String receiveFromQueue(String queueName) { return receiveFromQueue(DirectExchange.DEFAULT, queueName); } /** * 给direct交换机指定queue发送消息 * * @param directExchange * @param queueName */ public String receiveFromQueue(DirectExchange directExchange, String queueName) { Queue queue = new Queue(queueName); addQueue(queue); Binding binding = BindingBuilder.bind(queue).to(directExchange).withQueueName(); rabbitAdmin.declareBinding(binding); String messages = (String) rabbitTemplate.receiveAndConvert(queueName); System.out.println("Receive:" + messages); return messages; } /** * 创建Exchange * * @param exchange */ public void addExchange(AbstractExchange exchange) { rabbitAdmin.declareExchange(exchange); } /** * 删除一个Exchange * * @param exchangeName */ public boolean deleteExchange(String exchangeName) { return rabbitAdmin.deleteExchange(exchangeName); } /** * 声明其名称自动命名的队列。它是用exclusive=true、autoDelete=true和 durable = false * * @return Queue */ public Queue addQueue() { return rabbitAdmin.declareQueue(); } /** * 创建一个指定的Queue * * @param queue * @return queueName */ public String addQueue(Queue queue) { return rabbitAdmin.declareQueue(queue); } /** * 删除一个队列 * * @param queueName the name of the queue. * @param unused true if the queue should be deleted only if not in use. * @param empty true if the queue should be deleted only if empty. */ public void deleteQueue(String queueName, boolean unused, boolean empty) { rabbitAdmin.deleteQueue(queueName, unused, empty); } /** * 删除一个队列 * * @param queueName * @return true if the queue existed and was deleted. */ public boolean deleteQueue(String queueName) { return rabbitAdmin.deleteQueue(queueName); } /** * 绑定一个队列到一个匹配型交换器使用一个routingKey * * @param queue * @param exchange * @param routingKey */ public void addBinding(Queue queue, TopicExchange exchange, String routingKey) { Binding binding = BindingBuilder.bind(queue).to(exchange).with(routingKey); rabbitAdmin.declareBinding(binding); } /** * 绑定一个Exchange到一个匹配型Exchange 使用一个routingKey * * @param exchange * @param topicExchange * @param routingKey */ public void addBinding(Exchange exchange, TopicExchange topicExchange, String routingKey) { Binding binding = BindingBuilder.bind(exchange).to(topicExchange).with(routingKey); rabbitAdmin.declareBinding(binding); } /** * 去掉一个binding * * @param binding */ public void removeBinding(Binding binding) { rabbitAdmin.removeBinding(binding); } /** * 创建交换器 * * @param exchangeName * @return */ public DirectExchange createExchange(String exchangeName) { return new DirectExchange(exchangeName, true, false); } } 解读
最新发布
09-23
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值