rabbitMq简单应用

7 阅读3分钟

 1、配置连接

@Configuration
public class RabbitConfig {

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

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

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

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

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

    @Bean
    public CachingConnectionFactory cachingConnectionFactory() {
        CachingConnectionFactory factory = new CachingConnectionFactory();
        factory.setHost(host);
        factory.setPort(Integer.parseInt(port));
        factory.setUsername(userName);
        factory.setPassword(password);
        factory.setVirtualHost(virtualHost);
        // 可以缓存的通道数
        factory.setChannelCacheSize(25); 
        return factory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(CachingConnectionFactory connectionFactory){
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        // 自定义消息反序列化
        rabbitTemplate.setMessageConverter(new MqMessageConverter());
        rabbitTemplate.setConnectionFactory(connectionFactory);
        //设置开启Mandatory,才能触发回调函数,无论消息推送结果怎么样都强制调用回调函数
        rabbitTemplate.setMandatory(true);

        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            System.out.println("ConfirmCallback:"+"相关数据:"+correlationData);
            System.out.println("ConfirmCallback:"+"确认情况:"+ack);
            System.out.println("ConfirmCallback:"+"原因:"+cause);
        });

        rabbitTemplate.setReturnsCallback(returnedMessage -> {
            System.out.println("ReturnCallback:   " + "消息:" + JSONUtil.toJsonStr(returnedMessage));
        });

        return rabbitTemplate;
    }


    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer(CachingConnectionFactory connectionFactory) {
        SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory);
        container.setConcurrentConsumers(4);
        container.setMaxConcurrentConsumers(8);
        // RabbitMQ默认是自动确认,这里改为手动确认消息
        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return container;
    }

    @Bean
    public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
        // 创建RabbitAdmin并绑定连接工厂
        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);
        // 启用自动声明(可选,确保队列/交换机声明生效)
        rabbitAdmin.setAutoStartup(true);
        return rabbitAdmin;
    }
}

2、自定义序列化

public class MqMessageConverter implements MessageConverter {

    private final Jackson2JsonMessageConverter converter;

    public MqMessageConverter() {
        ObjectMapper mapper = new ObjectMapper();
        // 注册JavaTimeModule避免LocalDateTime时间序列化失效
        mapper.registerModule(new JavaTimeModule());
        mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        this.converter = new Jackson2JsonMessageConverter(mapper);
    }

    @NotNull
    @Override
    public Message toMessage(@NotNull Object object, @NotNull MessageProperties messageProperties) {
        return converter.toMessage(object, messageProperties);
    }

    @NotNull
    @Override
    public Object fromMessage(@NotNull Message message) {
        return converter.fromMessage(message);
    }

}

3、配置交换机与消息队列

    @Configuration
    public class DirectRabbitConfig {

        private static final Map<String, Object> arguments = getArguments();

        private static Map<String, Object> getArguments() {
            Map<String, Object> arguments = new HashMahMap<>();
            // 设置超时时间60秒,避免队列堆积
            arguments.put("x-dead-message-ttl", 60000);
            // 设置死信交换机
            arguments.put("x-dead-letter-exchange", "directExchange");
            // 设置死信路由键
            arguments.put("x-dead-letter-routing-key", "directKeyMessage");
            return arguments;
        }

        // ==================== 配置交换机 ====================

        /**
         * topic交换机 交换所有topic类型的消息
         */
        @Bean
        public TopicExchange topicExchange() {
            return new TopicExchange("topicExchange", true, false);
        }

        @Bean
        public DirectExchange directExchange() {
            return new DirectExchange("directExchange", true, false);
        }

        // ==================== 配置队列 ====================

        /**
         * 创建业务队列
         */
        @Bean
        public Queue bizQueue() {
            return new Queue("biz", true, false, false, arguments);
        }

        /**
         * 创建死信队列 用于接收所有级别的失败消息
         */
        @Bean
        public Queue directQueue() {
            Map<String, Object> args = new HashMap<>();
            // 设置超时时间
            args.put("x-dead-message-ttl", 60000);
            return new Queue(RabbitmqConstant."directQueue", true, false, false, args);
        }

        // ==================== 绑定关系配置 ====================

        /**
         * 绑定队列
         */
        @Bean
        public Binding bizBinding() {
            return BindingBuilder.bind(bizQueue()).to(topicExchange()).with("biz");
        }

        /**
         * 绑定死信队列
         */
        @Bean
        public Binding directBinding() {
            return BindingBuilder.bind(directQueue()).to(directExchange()).with("directKeyMessage";
        }

    }

4、配置消费者

    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class BizConsumer {

        @RabbitListener(queues = "biz")
        public void bizListen(Message message, Channel channel) throws Exception {
            long deliveryTag = message.getMessageProperties().getDeliveryTag();
            String body = new String(message.getBody(), StandardCharsets.UTF_8);
            JSONObject messageJson = JSONUtil.parseObj(body);
            try {
                // todo 业务处理
            } catch (Exception e) {
                // todo 异常处理

                // 抛出信息到死信队列中
                if (MAX_RETRY_COUNT <= (retryCount + 1)) {
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
                log.error("❌ 消息处理失败,正在重试,当前重试次数为:{}, retryCount);
                throw new RuntimeException(e);
            }
        }

    }

5、配置死信消费者

    @Slf4j
    @Component
    @RequiredArgsConstructor
    public class DeadLetterConsumer {

        /**
         * 监听死信队列 接收所有进入死信队列的消息
         */
        @RabbitListener(queues = "directQueue")
        public void deadLetterListen(Message message, Channel channel) throws IOException {
            log.error("[{}] \uD83D\uDC80 死信队列接收到消息 ", LocalDateTime.now());
            log.error("消息内容: {}", RabbitMqUtil.getMessage(message));
            log.error("🔔 等待人工处理");
            // 抛弃消息
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        }

    }

6、发送消息

    @Slf4j
    public class RabbitMqUtil {

        private static final RabbitTemplate rabbitTemplate = SpringUtil.getBean(RabbitTemplate.class);

        /**
         * 将amqp协议消息转化为传递实体信息
         * @param clazz
         * @param message
         * @return
         * @param <T>
         */
        public static <T> T getMessage(Class<T> clazz, Message message) {
            String body = new String(message.getBody(), StandardCharsets.UTF_8);
            return JSONUtil.toBean(body, clazz);
        }

        /**
         * 将amqp协议传递消息转化为JSON
         * @param message
         * @return
         */
        public static JSONObject getMessage(Message message) {
            String body = new String(message.getBody(), StandardCharsets.UTF_8);
            return JSONUtil.parseObj(body);
        }

        /**
         * 发送消息给mq执行
         */
        public static <T> void sendMqMessage(String topic, T t) {
            rabbitTemplate.convertAndSend(RabbitmqConstant.TOPIC_EXCHANGE, topic, new JSONObject(t));
        }

        /**
         * 发送消息给mq执行
         */
        public static void sendMqJsonMessage(String topic, JSONObject jsonObject) {
            rabbitTemplate.convertAndSend(RabbitmqConstant.TOPIC_EXCHANGE, topic, jsonObject);
        }

    }