RabbitMq实践----shock wave 2

178 阅读3分钟

环境说明

centos8+springcloudalibaba+rabbitmq

安装

下载安装并使用
rabbitmq-plugins enable rabbitmq_management
启动管理后台

安装完成

微信截图_20210728105038.png

搭建

pom.xml

<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>

yml配置

server:
  port: 8601
spring:
  application:
    name: client1-service
  cloud:
    nacos:
      #192.168.82.83:8848
      server-addr: 127.0.0.1:8848
  datasource:
    #新com.mysql.cj.jdbc.Driver   老com.mysql.jdbc.Driver
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3310/client1_service?serverTimezone=GMT%2B8&useSSL=true
    username: root
    password: 123456
  rabbitmq:
    host: 127.0.0.1
    port: 5672
    username: guest
    password: guest

简单普通消息队列

注入消息队列配置

直接创建消息队列,或使用默认交换机,具体详见rabbitmq解析

微信截图_20210728110326.png

@Configuration
public class RabbitMqConfig {
/**
 * 简单消息队列
 */
public static final String SIMPLEMSG = "clent1.simple";
@Bean
public Queue simpleMsgQueue() {
    Queue queue = new Queue(SIMPLEMSG);
    // 是否持久化
    queue.isDurable();
    // 仅创建者可以使用的私有队列,断开后自动删除
    queue.isExclusive();
    // 当所有消费客户端连接断开后,是否自动删除队列
    queue.isAutoDelete();
    return queue;
}
}
发送者
@Component
@Slf4j
public class Sender {
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendSimpleMsg(String message) {
        Message messageObject = MessageBuilder.withBody(message.getBytes()).setMessageId(String.valueOf(123)).build();
        //amqpTemplate.convertAndSend(RabbitMqConfig.SIMPLEMSG,message);
        amqpTemplate.convertAndSend(RabbitMqConfig.SIMPLEMSG,messageObject);
    }
}
消费者
@Component
public class Receiver {
    @RabbitListener(queues = {RabbitMqConfig.SIMPLEMSG})
    public void simpleQueue(Message message) {
        String s = new String(message.getBody());
        System.out.println("接收简单消息队列消息:"+s);
        System.out.println("消息id为:"+message.getMessageProperties().getMessageId());
    }
}

延时消息队列

配置
package org.example.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * @author shock wave 2
 * @version 1.0
 * @description: TODO
 * @date 2021/7/26 15:32
 */
@Configuration
public class RabbitMqConfig {
    /**
     * 简单消息队列
     */
    public static final String SIMPLEMSG = "clent1.simple#";
    /**
     * 延时消息队列
     */
    public static final String DELAY_SIMPLEMSG = "clent1.simple.delay";
    public static final String SIMPLEMSG_QUEUE = "simple.queue";
    /**
     * 交换机名
     */
    public static final String DELAY_SIMPLEMSG_EXCHANGE = "clent1.simple.delay.exchange";
    /**
     * 死信路由key
     */
    public static final String DELAY_SIMPLEMSG_ROUTER_KEY = "simple.delay.key";
    /**
     * 普通路由key
     */
    public static final String SIMPLEMSG_ROUTER_KEY = "delay.key";
    /**
     * @description: TODO 普通家谱换季
     */
    public static final String SIMPLEMSG_EXCHANGE = "simple.exchange";
    @Bean
    public Queue simpleMsgQueue() {
        Queue queue = new Queue(SIMPLEMSG);
        // 是否持久化
        queue.isDurable();
        // 仅创建者可以使用的私有队列,断开后自动删除
        queue.isExclusive();
        // 当所有消费客户端连接断开后,是否自动删除队列
        queue.isAutoDelete();
        return queue;
    }
    @Bean
    public Queue delaySimpleMsgQueue() {
        Queue queue = new Queue(SIMPLEMSG_QUEUE,true);
        return queue;
    }

    /**
     * 延迟队列配置
     * <p>
     * 1、params.put("x-message-ttl", 5 * 1000);
     * 第一种方式是直接设置 Queue 延迟时间 但如果直接给队列设置过期时间,这种做法不是很灵活,(当然二者是兼容的,默认是时间小的优先)
     * 2、rabbitTemplate.convertAndSend(book, message -> {
     * message.getMessageProperties().setExpiration(2 * 1000 + "");
     * return message;
     * });
     * 第二种就是每次发送消息动态设置延迟时间,这样我们可以灵活控制
     **/
    @Bean
    public Queue delayQueue() {
        Map<String, Object> params = new HashMap<>();
        // x-dead-letter-exchange 声明了队列里的死信转发到的DLX名称,
        params.put("x-dead-letter-exchange", SIMPLEMSG_EXCHANGE);
        // x-dead-letter-routing-key 声明了这些死信在转发时携带的 routing-key 名称。
        params.put("x-dead-letter-routing-key", SIMPLEMSG_ROUTER_KEY);
        return new Queue(DELAY_SIMPLEMSG, true, false, false, params);
    }
    /**
     * 需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配。
     * 这是一个完整的匹配。如果一个队列绑定到该交换机上要求路由键 “dog”,则只有被标记为“dog”的消息才被转发,
     * 不会转发dog.puppy,也不会转发dog.guard,只会转发dog。
     * @return DirectExchange
     */
    @Bean
    public DirectExchange orderDelayExchange() {
        //无使用自动删除
        return new DirectExchange(DELAY_SIMPLEMSG_EXCHANGE,true,false);
    }
    @Bean
    public Binding dlxBinding() {
        return BindingBuilder.bind(delayQueue()).to(orderDelayExchange()).with(DELAY_SIMPLEMSG_ROUTER_KEY);
    }
    /**
     * 将路由键和某模式进行匹配。此时队列需要绑定要一个模式上。
     * 符号“#”匹配一个或多个词,符号“*”匹配不多不少一个词。因此“audit.#”能够匹配到“audit.irs.corporate”,但是“audit.*” 只会匹配到“audit.irs”。
     **/
    @Bean
    public TopicExchange orderTopicExchange() {
        return new TopicExchange(SIMPLEMSG_EXCHANGE,true,false);
    }

    @Bean
    public Binding orderBinding() {
        // TODO 如果要让延迟队列之间有关联,这里的 routingKey 和 绑定的交换机很关键
        return BindingBuilder.bind(delaySimpleMsgQueue()).to(orderTopicExchange()).with(SIMPLEMSG_ROUTER_KEY);
    }
}
发送者
package org.example.rabbitmq;

import lombok.extern.slf4j.Slf4j;
import org.example.config.RabbitMqConfig;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Date;

/**
 * @author shock wave 2
 * @version 1.0
 * @description: TODO
 * @date 2021/7/26 15:26
 */
@Component
@Slf4j
public class Sender {
    @Autowired
    private AmqpTemplate amqpTemplate;

    public void sendSimpleMsg(String message) {
        Message messageObject = MessageBuilder.withBody(message.getBytes()).setMessageId(String.valueOf(123)).build();
        //amqpTemplate.convertAndSend(RabbitMqConfig.SIMPLEMSG,message);
        amqpTemplate.convertAndSend(RabbitMqConfig.SIMPLEMSG,messageObject);
    }
    /**
     * 红包延时消息队列
     * @Param
     * @return
     */
    public void sendSimpleDelayMsg(String message) {
        amqpTemplate.convertAndSend(RabbitMqConfig.DELAY_SIMPLEMSG_EXCHANGE, RabbitMqConfig.DELAY_SIMPLEMSG_ROUTER_KEY, message, msg -> {
            msg.getMessageProperties().setExpiration("3000");
            return msg;
        });
    }
}
消费者
package org.example.rabbitmq;

import groovy.util.logging.Log;
import groovy.util.logging.Slf4j;
import org.example.config.RabbitMqConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

/**
 * @author shock wave 2
 * @version 1.0
 * @description: TODO
 * @date 2021/7/26 15:26
 */
@Component
public class Receiver {
    @RabbitListener(queues = {RabbitMqConfig.SIMPLEMSG})
    public void simpleQueue(Message message) {
        String s = new String(message.getBody());
        System.out.println("接收简单消息队列消息:"+s);
        System.out.println("消息id为:"+message.getMessageProperties().getMessageId());
    }
    @RabbitListener(queues = {RabbitMqConfig.SIMPLEMSG_QUEUE})
    public void simpleDelayQueue(Message message) {
        String s = new String(message.getBody());
        System.out.println("接收延时消息队列消息:"+s);
        System.out.println("消息id为:"+message.getMessageProperties().getMessageId());
    }
}

其他补充

1、配置文件自动刷新

如果使用springcloudnetflix开发微服务(springcloudalibaba可以直接使用@RefreshScope,自动刷新配置),结合@RefreshScope+springcloud Acturator可以刷新配置文件,但是还是需要调用acturator接口刷新配置,这里可以使用springcloudbus结合rabbitmq,实现自动刷新,实际就是一个订阅/通知

具体可以参考spring.io/projects/sp…

2、spring-cloud-stream使用rabbitmq

springcloudnetflix可以使用spring-cloud-stream连接rabbitmq,Spring Cloud Stream是用于构建消息驱动的微服务应用程序的框架。Spring Cloud Stream在Spring Boot的基础上创建了独立的生产级Spring应用程序,并使用Spring Integration提供了到消息代理的连接。它提供了来自多家供应商的中间件的合理配置,并介绍了持久性发布-订阅语义,使用者组和分区的概念。具体参考spring.io/projects/sp…