RabbitMQ项目实战(二)

文章目录

项目改造

以前把任务提交到线程池,然后在线程池提交中编写处理程序的代码,线程池内排队。

如果程序中断了,任务就没了,就丢了。

改造后的流程:

  1. 把任务提交改为向队列发送消息
  2. 写一个专门接收消息的程序,处理任务
  3. 如果程序中断了,消息未被确认,还会重发
  4. 现在,消息全部集中发送到消息队列,你可以部署多个后端,都从同一个地方取任务,从而实现了分布式负载均衡

实现步骤

1)创建交换机和队列

2)将线程池中的执行代码移到消费者类中

3)根据消费者的需求来确认消息的格式(chartId)

4)将提交线程池改造为发送消息到队列

注意:如果程序中断了,没有ack,也没有nack(服务中断,没有任何响应),那么这条消息会被重新放到消息队列中,从而实现了每个任务都会执行

记得在项目启动前创建队列和交换机

java 复制代码
package com.yupi.springbootinit.bimq;

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

/**
 * 用于创建测试程序用到的交换机和队列(只用在程序启动前执行一次)
 */
public class BiMqInitMain {
    public static void main(String[] args) {
        try{
            ConnectionFactory factory  = new ConnectionFactory();
            factory.setHost("localhost");
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
            channel.exchangeDeclare(BiMqConstant.BI_EXCHANGE_NAME,"direct");
            String queueName = BiMqConstant.BI_QUEUE_NAME;
            channel.queueDeclare(queueName,true,false,false,null);
            channel.queueBind(queueName,BiMqConstant.BI_EXCHANGE_NAME,BiMqConstant.BI_ROUTING_KEY);
        }catch (Exception e){

        }
    }
}

生产者:

java 复制代码
package com.yupi.springbootinit.bimq;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class BiMessageProducer {
    @Resource
    private RabbitTemplate rabbitTemplate;

    public void sendMessage(String message){
        // 使用rabbitTemplate的convertAndSend方法将消息发送到指定的交换机和路由键
        rabbitTemplate.convertAndSend(BiMqConstant.BI_EXCHANGE_NAME,BiMqConstant.BI_ROUTING_KEY,message);
    }
}

消费者:

java 复制代码
package com.yupi.springbootinit.bimq;

import cn.hutool.core.text.StrBuilder;
import com.rabbitmq.client.Channel;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.manager.AiManager;
import com.yupi.springbootinit.model.entity.Chart;
import com.yupi.springbootinit.service.ChartService;
import com.yupi.springbootinit.utils.ExcelUtils;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import javax.annotation.RegEx;
import javax.annotation.Resource;

@Component
@Slf4j
public class BiMessageConsumer {

    @Resource
    private ChartService chartService;


    @Resource
    private AiManager aiManager;

    //使用@SneakyThrows注解简化异常处理
    //使得你可以在不声明抛出异常的方法中抛出受检异常,而无需捕获它们。这在一些特定情况下可能会很有用,但通常不建议频繁使用,因为它可能会破坏代码的可读性和健壮性。
    @SneakyThrows
    //使用@RabbitListener注解指定要监听的队列名称为"code_queue",并设置消息的确认机制为手动确认
    @RabbitListener(queues = {BiMqConstant.BI_QUEUE_NAME},ackMode = "MANUAL")
    // // 在RabbitMQ中,每条消息都会被分配一个唯一的投递标签,用于标识该消息在通道中的投递状态和顺序。通过使用@Header(AmqpHeaders.DELIVERY_TAG)注解,可以从消息头中提取出该投递标签,并将其赋值给long deliveryTag参数。
    public void reciveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliverttag){
        log.info("receive message = {}" + message);
        if (StringUtils.isBlank(message)) {
            //拒绝消息
            channel.basicNack(deliverttag,false,false);
            throw new BusinessException(ErrorCode.SYSTEM_ERROR,"消息为空");
        }
        long chartId = Long.parseLong(message);
        Chart chart = chartService.getById(chartId);
        if(chart == null){
            channel.basicNack(deliverttag,false,false);
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR,"图表为空");
        }
        Chart updateChart = new Chart();
        updateChart.setId(chart.getId());
        updateChart.setStatus("running");
        boolean b = chartService.updateById(updateChart);
        if(!b){
            handleChartUpdateError(chart.getId(),"更新图表执行中状态失败");
            return;
        }
        String result = aiManager.doChat(CommonConstant.BI_MODEL_ID, getUserInput(chart));
        String[] splits = result.split("【【【【【");
        if(splits.length < 3){
            handleChartUpdateError(chart.getId(),"AI生成错误");
            return;
        }
        String genChart = splits[1];
        String genResult = splits[2];
        Chart updateChart2 = new Chart();
        updateChart2.setId(chart.getId());
        updateChart2.setStatus("succeed");
        updateChart2.setGenChart(genChart);
        updateChart2.setGenResult(genResult);
        boolean b1 = chartService.updateById(updateChart2);
        if(!b1){
            handleChartUpdateError(chart.getId(),"更新图表成功状态失败");
            return;
        }
        //确认消息
        channel.basicAck(deliverttag,false);
    }

    /**
     * 根据chart获取用户的输入
     * @param chart
     * @param
     */
    public String getUserInput(Chart chart){
        String goal = chart.getGoal();
        String chartType = chart.getChartType();
        String CSVData = chart.getChartData();
        StrBuilder userInput = new StrBuilder();
        userInput.append("分析需求:").append("\n");
        String userGoal = goal;
        if(StringUtils.isNotBlank(chartType)){
            //指定了图表类型,就在目标上拼接请使用,图表类型
            userGoal += "请使用,"  + chartType;
        }
        userInput.append(CSVData).append("\n");
        return userInput.toString();
    }
    public void handleChartUpdateError(long chartId,String execMessage){
        Chart chart = new Chart();
        chart.setId(chartId);
        chart.setStatus("failed");
        chart.setExecMessage(execMessage);
        boolean b = chartService.updateById(chart);
        if(!b){
            log.error("更新图表失败状态失败" + chartId + "," + execMessage);
        }
    }
}
相关推荐
茶杯梦轩3 天前
从零起步学习RabbitMQ || 第三章:RabbitMQ的生产者、Broker、消费者如何保证消息不丢失(可靠性)详解
分布式·后端·面试
回家路上绕了弯5 天前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
用户8307196840825 天前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
用户8307196840827 天前
RabbitMQ vs RocketMQ 事务大对决:一个在“裸奔”,一个在“开挂”?
后端·rabbitmq·rocketmq
初次攀爬者8 天前
RabbitMQ的消息模式和高级特性
后端·消息队列·rabbitmq
初次攀爬者10 天前
ZooKeeper 实现分布式锁的两种方式
分布式·后端·zookeeper
让我上个超影吧11 天前
消息队列——RabbitMQ(高级)
java·rabbitmq
塔中妖11 天前
Windows 安装 RabbitMQ 详细教程(含 Erlang 环境配置)
windows·rabbitmq·erlang
断手当码农11 天前
Redis 实现分布式锁的三种方式
数据库·redis·分布式
初次攀爬者11 天前
Redis分布式锁实现的三种方式-基于setnx,lua脚本和Redisson
redis·分布式·后端