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;
            }
        });
    }
}
### RabbitMQ 使用指南和教程 #### 安装 RabbitMQ RabbitMQ 是一种基于 AMQP 协议的消息中间件,用于实现分布式系统的可靠消息传递。以下是安装 RabbitMQ 的基本流程: 1. **安装 Erlang** RabbitMQ 基于 Erlang 编程语言开发,因此需要先安装 Erlang 运行环境。可以通过包管理器或者下载官方二进制文件完成安装。 2. **安装 RabbitMQ Server** 下载并安装 RabbitMQ Server 软件包。对于 Linux 用户,可以使用以下命令: ```bash sudo apt-get install rabbitmq-server ``` 3. **启动服务** 启动 RabbitMQ 服务后,默认监听端口为 `5672`(AMQP 协议),Web 管理界面默认运行在 `15672` 端口上。 ```bash sudo systemctl start rabbitmq-server ``` 4. **启用 Web 管理插件** 可通过以下命令启用 RabbitMQ 提供的 Web 管理工具: ```bash sudo rabbitmq-plugins enable rabbitmq_management ``` --- #### 配置用户与权限 为了安全访问 RabbitMQ 实例,通常需要创建自定义用户并分配相应权限。 - 创建新用户: ```bash rabbitmqctl add_user rabbitmq 211314 ``` 此操作会新增名为 `rabbitmq` 的用户,并将其密码设为 `211314`[^1]。 - 设置用户角色: ```bash rabbitmqctl set_user_tags rabbitmq administrator ``` 将该用户的标签设定为管理员角色,使其拥有完全控制权[^1]。 - 授予用户权限: ```bash rabbitmqctl set_permissions -p "/" rabbitmq ".*" ".*" ".*" ``` 上述命令授予用户对根虚拟主机 `/` 中所有资源的操作权限[^1]。 - 查看现有用户及其角色: ```bash rabbitmqctl list_users ``` --- #### 集群配置 RabbitMQ 支持多种集群模式来提升可用性和性能。主要分为两类:普通模式和镜像模式。 - **普通模式** 在这种模式下,各节点独立存储队列中的数据和其他元信息(如交换机)。当客户端尝试消费某个不在当前连接节点上的消息时,目标节点会被请求转发所需的数据[^2]。 - **镜像模式** 对比之下,在镜像模式中,指定队列的内容将在多个节点间保持一致副本。即使部分成员失效,剩余存活节点仍能继续提供完整的服务功能[^2]。 > 注意事项:尽管镜像模式提高了可靠性,但也带来了额外开销——网络流量增加以及写入延迟上升等问题需被充分考虑进去。 --- #### 应用集成示例 假设要在一个 Java 或 Python 应用程序里利用 RabbitMQ 来发送/接收消息,则可能涉及以下几个步骤: 1. **声明交换器 (Exchange)** 和绑定关系: ```java channel.exchangeDeclare(exchangeName, "direct", true); channel.queueDeclare(queueName, true, false, false, null); channel.queueBind(queueName, exchangeName, routingKey); ``` 如此一来便完成了持久化队列及路由键关联工作[^3]。 2. 发布一条测试消息至上述已建立好的通道路径之中; 3. 订阅对应主题下的事件流以便实时捕获最新动态更新情况; --- #### 总结 以上涵盖了从基础安装到高级特性使用的整个过程概述。希望这些指导能够帮助您快速掌握如何部署与维护属于自己的 RabbitMQ 平台实例! 问题
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值