RabbitMq

RabbitMQ介绍

百度百科:

RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件)。RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开放电信平台框架上的。所有主要的编程语言均有与代理接口通讯的客户端库。

AMQP协议:

AMQP
img

Erlang:Erlang是一种通用的面向并发的编程语言,Erlang充分发挥CPU的性能,延迟特别低,相比其他的MQ(Kafka,RocketMQ)延迟是最低的。

RabbitMQ支持多种语言通讯:Java,Python…………都有响应的API

RabbitMQ支持海量的插件去实现一些特殊功能,RabbitMQ自带了一款图形化界面,操作异常的简单。

RabbitMQ安装

Docker安装RabbitMQ (安装带有管理控制台的镜像):

docker run -d --name rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 -v /opt/rabbitmq:/var/lib/rabbitmq rabbitmq:management

端口说明:

4369, 25672 (Erlang发现&集群端口)
5672, 5671 (AMQP端口)
15672 (web管理后台端口)
61613, 61614 (STOMP协议端口)
1883, 8883 (MQTT协议端口)

RabbitMQ架构

完整架构
在这里插入图片描述

RabbitMQ通讯方式

RabbitMQ提供的通讯方式

构建Connection工具类

  • 导入依赖:amqp-client,junit
<dependencies>
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.10.0</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
  • 构建工具类
public class RabbitMqConnectionUtil {
    public static final String RABBITMQ_HOST = "127.0.0.1";
    public static final int RABBITMQ_PORT = 5672;
    public static final String RABBITMQ_USERNAME = "guest";
    public static final String RABBITMQ_PASSWORD = "guest";
    public static final String RABBITMQ_VIRTUAL_HOST = "/";

    public static Connection getConnection() throws Exception {
        //1.创建连接工厂
        ConnectionFactory factory = new ConnectionFactory();
        //2.设置连接信息
        factory.setHost(RABBITMQ_HOST);
        factory.setPort(RABBITMQ_PORT);
        factory.setUsername(RABBITMQ_USERNAME);
        factory.setPassword(RABBITMQ_PASSWORD);
        factory.setVirtualHost(RABBITMQ_VIRTUAL_HOST);
        //3.创建连接
        Connection connection = factory.newConnection();
        return connection;
    }
}

Hello World

通讯方式
在这里插入图片描述
  • 生产者:
public class Publisher {

    public static final String QUEUE_NAME = "hello";

    @Test
    public void publish() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //4.发布消息
        channel.basicPublish("",QUEUE_NAME,null,"Hello World".getBytes());
    }
}
  • 消费者
public class Consumer {
    public static final String QUEUE_NAME = "hello";

    @Test
    public void consume() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //4.监听消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
                System.out.println("消费者获取到消息:" + new String(bytes,"UTF-8"));
            }
        };
        channel.basicConsume(QUEUE_NAME,true,consumer);
        System.in.read();
    }
}

Work Queues

WorkQueues需要学习的内容
在这里插入图片描述
  • 生产者:生产者和Hello World的形式是一样的,都是将消息推送到默认交换机。
  • 消费者:让消费者关闭自动ack,并且设置消息的流控,最终实现消费者可以尽可能去多消费消息
public class Consumer {
    public static final String QUEUE_NAME = "workqueues";

    @Test
    public void consume1() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //4.设置消息的流控
        channel.basicQos(3);
        //5.监听消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
                System.out.println("消费者1号获取到消息:" + new String(bytes,"UTF-8"));
                //6.关闭自动ack
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume(QUEUE_NAME,true,consumer);
        System.in.read();
    }

    @Test
    public void consume2() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建队列
        channel.queueDeclare(QUEUE_NAME,false,false,false,null);
        //4.设置消息的流控
        channel.basicQos(1);
        //5.监听消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
                System.out.println("消费者2号获取到消息:" + new String(bytes,"UTF-8"));
                //6.关闭自动ack
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume(QUEUE_NAME,true,consumer);
        System.in.read();
    }
}

Publish/Subscribe

自定义一个交换机
在这里插入图片描述
public class Publisher {

    public static final String EXCHANGE_NAME = "pubsub";
    public static final String QUEUE_NAME1 = "pubsub-one";
    public static final String QUEUE_NAME2 = "pubsub-two";

    @Test
    public void publish() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.FANOUT);
        //4.构建队列
        channel.queueDeclare(QUEUE_NAME1,false,false,false,null);
        channel.queueDeclare(QUEUE_NAME2,false,false,false,null);
        //5.绑定队列
        channel.queueBind(QUEUE_NAME1,EXCHANGE_NAME,"");
        channel.queueBind(QUEUE_NAME2,EXCHANGE_NAME,"");
        //6.发送消息到交换机
        channel.basicPublish(EXCHANGE_NAME,"",null,"publish/subscribe!".getBytes());
    }
}

Routing

DIRECT类型Exchange
在这里插入图片描述
  • 生产者:在绑定Exchange和Queue时,需要指定好routingKey,同时在发送消息时,也指定routingKey,只有routingKey一致时,才会把指定的消息路由到指定的Queue
  • 消费者同上
public class Publisher {

    public static final String EXCHANGE_NAME = "routing";
    public static final String QUEUE_NAME1 = "routing-one";
    public static final String QUEUE_NAME2 = "routing-two";

    @Test
    public void publish() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
        //4.构建队列
        channel.queueDeclare(QUEUE_NAME1,false,false,false,null);
        channel.queueDeclare(QUEUE_NAME2,false,false,false,null);
        //5.绑定队列
        channel.queueBind(QUEUE_NAME1,EXCHANGE_NAME,"orange");
        channel.queueBind(QUEUE_NAME2,EXCHANGE_NAME,"black");
        channel.queueBind(QUEUE_NAME2,EXCHANGE_NAME,"white");
        //6.发送消息到交换机
        channel.basicPublish(EXCHANGE_NAME,"orange",null,"大橙子".getBytes());
        channel.basicPublish(EXCHANGE_NAME,"black",null,"大黑狗".getBytes());
        channel.basicPublish(EXCHANGE_NAME,"white",null,"大白狗".getBytes());
    }
}

Topic

Topic模式
在这里插入图片描述
  • 生产者:TOPIC类型可以编写带有特殊意义的routingKey的绑定方式
public class Publisher {

    public static final String EXCHANGE_NAME = "topic";
    public static final String QUEUE_NAME1 = "topic-one";
    public static final String QUEUE_NAME2 = "topic-two";

    @Test
    public void publish() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建交换机
        channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.TOPIC);
        //4.构建队列
        channel.queueDeclare(QUEUE_NAME1,false,false,false,null);
        channel.queueDeclare(QUEUE_NAME2,false,false,false,null);
        //5. 绑定交换机和队列,
        // TOPIC类型的交换机在和队列绑定时,需要以aaa.bbb.ccc..方式编写routingkey
        // 其中有两个特殊字符:*(相当于占位符),#(相当通配符)
        channel.queueBind(QUEUE_NAME1,EXCHANGE_NAME,"*.orange.*");
        channel.queueBind(QUEUE_NAME2,EXCHANGE_NAME,"*.*.black");
        channel.queueBind(QUEUE_NAME2,EXCHANGE_NAME,"white.#");
        //6.发送消息到交换机
        channel.basicPublish(EXCHANGE_NAME,"big.orange.yellow",null,"黄色大橙子".getBytes());
        channel.basicPublish(EXCHANGE_NAME,"lazy.big.black",null,"大懒黑狗".getBytes());
        channel.basicPublish(EXCHANGE_NAME,"white.small.dog",null,"白色小狗".getBytes());
    }
}

RPC

因为两个服务在交互时,可以尽量做到Client和Server的解耦,通过RabbitMQ进行解耦操作
需要让Client发送消息时,携带两个属性:
replyTo告知Server将相应信息放到哪个队列
correlationId告知Server发送相应消息时,需要携带位置标示来告知Client响应的信息

Topic模式
在这里插入图片描述
  • 客户端
public class Publisher {

    public static final String QUEUE_PUBLISHER = "rpc_publisher";
    public static final String QUEUE_CONSUMER = "rpc_consumer";

    @Test
    public void publish() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建队列
        channel.queueDeclare(QUEUE_PUBLISHER,false,false,false,null);
        channel.queueDeclare(QUEUE_CONSUMER,false,false,false,null);
        //4.发送消息
        String uuid = UUID.randomUUID().toString();
        String message = "hello rpc";
        AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
                .replyTo(QUEUE_CONSUMER)
                .correlationId(uuid)
                .build();
        channel.basicPublish("",QUEUE_PUBLISHER,basicProperties,message.getBytes());

        //5.消费消息
        channel.basicConsume(QUEUE_CONSUMER,false,new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
                String id = basicProperties.getCorrelationId();
                if (id != null && id.equals(uuid)){
                    System.out.println("接收到服务端的响应:" + new String(bytes,"UTF-8"));
                }
                //关闭自动ack
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        });
        System.in.read();
    }
}
  • 服务端
public class Consumer {
    public static final String QUEUE_PUBLISHER = "rpc_publisher";
    public static final String QUEUE_CONSUMER = "rpc_consumer";

    @Test
    public void consume() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建队列
        channel.queueDeclare(QUEUE_PUBLISHER,false,false,false,null);
        channel.queueDeclare(QUEUE_CONSUMER,false,false,false,null);
        //4.监听消息
        DefaultConsumer consumer = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String s, Envelope envelope, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
                System.out.println("消费者获取到消息:" + new String(bytes,"UTF-8"));
                String resp = "获取到了client发出的请求,这里是响应的信息";
                String respQueueName = basicProperties.getReplyTo();
                String uuid = basicProperties.getCorrelationId();
                AMQP.BasicProperties props = new AMQP.BasicProperties()
                        .builder()
                        .correlationId(uuid)
                        .build();
                channel.basicPublish("",respQueueName,props,resp.getBytes());
                //关闭自动ack
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        channel.basicConsume(QUEUE_PUBLISHER,true,consumer);
        System.in.read();
    }
}

SpringBoot操作RabbitMQ

SpringBoot声明信息

  • 创建项目

  • 导入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
  • 配置RabbitMQ信息
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /
  • 声明交换机&队列
@Configuration
public class RabbitMqConfig {

    public static final String EXCHANGE = "boot-exchange";
    public static final String QUEUE = "boot-queue";
    public static final String ROUTING_KEY = "*.black.*";

    /**
     * 创建交换机
     * @return
     */
    @Bean
    public Exchange bootExchange(){
        return ExchangeBuilder.topicExchange(EXCHANGE).build();
    }

    /**
     * 创建队列
     * @return
     */
    @Bean
    public Queue bootQueue(){
        return QueueBuilder.durable(QUEUE).build();
    }

    /**
     * 绑定交换机队列
     * @return
     */
    @Bean
    public Binding bootBinding(){
        return BindingBuilder.bind(bootQueue()).to(bootExchange()).with(ROUTING_KEY).noargs();
    }

}

生产者

@SpringBootTest
public class PublisherTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void publish(){
        rabbitTemplate.convertAndSend(RabbitMqConfig.EXCHANGE, "big.black.dog", "大黑狗", new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setCorrelationId("123456789");
                return message;
            }
        });
    }
}

消费者

@Component
public class ConsumeListener {

    /**
     * 监听队列
     */
    @RabbitListener(queues = {RabbitMqConfig.QUEUE})
    public void consume(Channel channel, Message message) throws Exception{
        System.out.println("获取消息:"+ new String(message.getBody()));
        String correlationId = message.getMessageProperties().getCorrelationId();
        System.out.println("唯一标识为:" + correlationId);
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
}

RabbitMQ保证消息可靠性

保证消息一定送达到Exchange

  • Confirm机制
    可以通过Confirm效果保证消息一定送达到Exchange,官方提供了三种方式,选择了对于效率影响最低的异步回调的效果
//开启confirms
channel.confirmSelect();

//设置confirms的异步回调
channel.addConfirmListener(new ConfirmListener() {
    @Override
    public void handleAck(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("消息成功的发送到Exchange!");
    }

    @Override
    public void handleNack(long deliveryTag, boolean multiple) throws IOException {
        System.out.println("消息没有发送到Exchange,尝试重试,或者保存到数据库做其他补偿操作!");
    }
});

保证消息可以路由到Queue

  • Return机制
// 设置Return回调,确认消息是否路由到了Queue
channel.addReturnListener(new ReturnListener() {
    @Override
    public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException {
        System.out.println("消息没有路由到指定队列,做其他的补偿措施!!");
    }
});
// 在发送消息时,将basicPublish方法参数中的mandatory设置为true,即可开启Return机制,当消息没有路由到队列中时,就会执行return回调
channel.basicPublish(EXCHANGE_NAME,"big.orange.yellow",true,null,"黄色大橙子".getBytes());

保证Queue可以持久化消息

  • DeliveryMode设置消息持久化

  • DeliveryMode设置为2代表持久化,如果设置为1,就代表不会持久化

//7. 设置消息持久化
AMQP.BasicProperties props = new AMQP.BasicProperties()
    .builder()
    .deliveryMode(2)
    .build();
channel.basicPublish(EXCHANGE_NAME,"big.orange.yellow",true,props,"黄色大橙子".getBytes());    

保证消费者可以正常消费消息

  • 消费者开启手动ack

SpringBoot实现上述操作

  • 配置
spring:
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest
    virtual-host: /
    publisher-confirm-type: correlated #开启confirm
    publisher-returns: true #开启returns
  • 代码实现
@SpringBootTest
public class PublisherTest {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void publish(){
        //开启confirm
        rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                if(ack){
                    System.out.println("消息已经送达到交换机!!");
                }else{
                    System.out.println("消息没有送达到Exchange,需要做一些补偿操作!!retry!!!");
                }
            }
        });
        //开启return
        rabbitTemplate.setReturnsCallback(new RabbitTemplate.ReturnsCallback() {
            @Override
            public void returnedMessage(ReturnedMessage returned) {
                String msg = new String(returned.getMessage().getBody());
                System.out.println("消息:" + msg + "路由队列失败!!做补救操作!!");
            }
        });
        rabbitTemplate.convertAndSend(RabbitMqConfig.EXCHANGE, "big.black.dog", "大黑狗", new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setCorrelationId("123456789");
                //设置消息持久化
                message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                return message;
            }
        });
    }
}

RabbitMQ死信队列&延迟交换机

死信介绍

死信&死信队列
在这里插入图片描述
  • 死信队列的应用:
    1.基于死信队列在队列消息已满的情况下,消息也不会丢失。
    2.实现延迟消费的效果。比如:下订单时,有15分钟的付款时间。

实现死信队列

  • 准备Exchange&Queue
@Configuration
public class DeadLetterConfig {

    public static final String NORMAL_EXCHANGE = "normal-exchange";
    public static final String NORMAL_QUEUE = "normal-queue";
    public static final String NORMAL_ROUTING_KEY = "normal.key";

    public static final String DEAD_EXCHANGE = "dead-exchange";
    public static final String DEAD_QUEUE = "dead-queue";
    public static final String DEAD_ROUTING_KEY = "dead.key";

    @Bean
    public Exchange normalExchange(){
        return ExchangeBuilder.topicExchange(NORMAL_EXCHANGE).build();
    }

    /**
     * 构建普通队列,同时将队列通过死信key绑定死信交换机
     * @return
     */
    @Bean
    public Queue normalQueue(){
        return QueueBuilder.durable(NORMAL_QUEUE).deadLetterExchange(DEAD_EXCHANGE)
                .deadLetterRoutingKey(DEAD_ROUTING_KEY).build();
    }

    @Bean
    public Binding normalBinding(){
        return BindingBuilder.bind(normalQueue()).to(normalExchange()).with(NORMAL_ROUTING_KEY).noargs();
    }

    @Bean
    public Exchange deadExchange(){
        return ExchangeBuilder.topicExchange(DEAD_EXCHANGE).build();
    }

    @Bean
    public Queue deadQueue(){
        return QueueBuilder.durable(DEAD_QUEUE).build();
    }

    @Bean
    public Binding deadBinding(){
        return BindingBuilder.bind(deadQueue()).to(deadExchange()).with(DEAD_ROUTING_KEY).noargs();
    }
    
}
  • 实现
  1. 基于消费者进行reject或者nack实现死信效果
@Component
public class DeadListener {

    @RabbitListener(queues = {DeadLetterConfig.NORMAL_QUEUE})
    public void consume(Channel channel, Message message) throws Exception{
        //reject
        channel.basicReject(message.getMessageProperties().getDeliveryTag(),false);
        //nack
        channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,false);
    }
}
  1. 消息的生存时间
    给消息设置生存时间
@SpringBootTest
public class DeadPublishTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void publishExpire(){
        String msg = "dead letter expire";
        rabbitTemplate.convertAndSend(DeadLetterConfig.NORMAL_EXCHANGE, "normal.key", msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration("5000");
                return message;
            }
        });
    }
}

给队列设置消息的生存时间

@Bean
public Queue normalQueue(){
    return QueueBuilder.durable(NORMAL_QUEUE)
            .deadLetterExchange(DEAD_EXCHANGE)
            .deadLetterRoutingKey(NORMAL_ROUTING_KEY)
            .ttl(10000)
            .build();
}
  1. 设置Queue中的消息最大长度
@Bean
public Queue normalQueue(){
    return QueueBuilder.durable(NORMAL_QUEUE)
            .deadLetterExchange(DEAD_EXCHANGE)
            .deadLetterRoutingKey(NORMAL_ROUTING_KEY)
            .maxLength(1)
            .build();
}

只要Queue中已经有一个消息,如果再次发送一个消息,这个消息会变为死信!

延迟交换机

  • 下载地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/download/3.10.2/rabbitmq_delayed_message_exchange-3.10.2.ez

1)将下载的插件复制到容器中
docker cp rabbitmq_delayed_message_exchange-3.10.2.ez rabbitmq:/opt/rabbitmq/plugins
进入容器:docker exec -it rabbitmq bash
2)到/opt/rabbitmq/sbin下
rabbitmq-plugins rabbitmq_delayed_message_exchange
3)退出容器:exit,重启容器:docker restart rabbitmq

在这里插入图片描述

  • 延迟交换机队列配置
@Configuration
public class DelayedConfig {

    public static final String DELAYED_EXCHANGE = "delayed-exchange";
    public static final String DELAYED_QUEUE = "delayed-queue";
    public static final String DELAYED_ROUTING_KEY = "delayed.key";

    /**
     * 构建延迟队列
     * @return
     */
    @Bean
    public Exchange delayedExchange(){
        Map<String,Object> arguments = new HashMap<>();
        arguments.put("x-delayed-type","topic");
        Exchange exchange = new CustomExchange(DELAYED_EXCHANGE,"x-delayed-message",true,false,arguments);
        return exchange;
    }

    @Bean
    public Queue delayedQueue(){
        return QueueBuilder.durable(DELAYED_QUEUE).build();
    }

    @Bean
    public Binding delayedBinding(){
        return BindingBuilder.bind(delayedQueue()).to(delayedExchange()).with(DELAYED_ROUTING_KEY).noargs();
    }
}
  • 延迟消息发送
@SpringBootTest
public class DelayedPublishTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void publishExpire(){
        String msg = "this is delayed message";
        rabbitTemplate.convertAndSend(DelayedConfig.DELAYED_EXCHANGE, DelayedConfig.DELAYED_ROUTING_KEY, msg, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //设置延迟时间
                message.getMessageProperties().setDelay(2000);
                return message;
            }
        });
    }
}

RabbitMQ其他内容

Headers类型Exchange

headers就是一个基于key-value的方式,让Exchange和Queue绑定的到一起的一种规则
相比Topic形式,可以采用的类型更丰富。

headers绑定方式
在这里插入图片描述
  • 原生
public class Publisher {

    public static final String HEADER_EXCHANGE = "header-exchange";
    public static final String HEADER_QUEUE = "header-queue";

    @Test
    public void publish() throws Exception{
        //1.获取连接
        Connection connection = RabbitMqConnectionUtil.getConnection();
        //2.构建channel
        Channel channel = connection.createChannel();
        //3.构建交换机
        channel.exchangeDeclare(HEADER_EXCHANGE, BuiltinExchangeType.HEADERS);
        //4.构建队列
        channel.queueDeclare(HEADER_QUEUE,false,false,false,null);
        Map<String,Object> args = new HashMap<>();
        // 多个header的key-value只要可以匹配上一个就可以
        // args.put("x-match","any");
        // 多个header的key-value要求全部匹配上!
        args.put("x-match","all");
        args.put("name","jack");
        args.put("age","23");
        //5.绑定队列
        channel.queueBind(HEADER_QUEUE,HEADER_EXCHANGE,"",args);
        //6.发送消息到交换机
        String msg = "header测试消息!";
        Map<String, Object> headers = new HashMap<>();
        headers.put("name","jack");
        headers.put("age","23");
        AMQP.BasicProperties props = new AMQP.BasicProperties()
                .builder()
                .headers(headers)
                .build();
        channel.basicPublish(HEADER_EXCHANGE,"",true,props,msg.getBytes());
    }
}
  • springboot
  1. 构建交换机队列
@Configuration
public class HeadersConfig {

    public static final String HEADERS_EXCHANGE = "headers-exchange";
    public static final String HEADERS_QUEUE = "headers-queue";

    /**
     * 构建队列
     * @return
     */
    @Bean
    public HeadersExchange headersExchange(){
        return ExchangeBuilder.headersExchange(HEADERS_EXCHANGE).build();
    }

    @Bean
    public Queue headersQueue(){
        return QueueBuilder.durable(HEADERS_QUEUE).build();
    }

    @Bean
    public Binding headersBinding(){
        Map<String,Object> headerKeys = new HashMap<>();
        headerKeys.put("name","jack");
        headerKeys.put("age",23);
        //BindingBuilder.bind(headersQueue()).to(headersExchange()).whereAny(headerKeys).match();
        return BindingBuilder.bind(headersQueue()).to(headersExchange()).whereAll(headerKeys).match();
    }
}
  1. 消息生产
@SpringBootTest
public class HeadersPublishTest {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void publish(){
        String msg = "this is headers message";
        //Message message = MessageBuilder.withBody(msg.getBytes()).setHeader("","").build();
        MessageProperties properties = new MessageProperties();
        properties.setHeader("name","jack");
        properties.setHeader("age",23);
        Message message = new Message(msg.getBytes(),properties);
        rabbitTemplate.convertAndSend(HeadersConfig.HEADERS_EXCHANGE, "", message, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);
                return message;
            }
        });
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值