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);
        }
    }
}
相关推荐
爱吃泡芙的小白白1 小时前
爬虫学习——使用HTTP服务代理、redis使用、通过Scrapy实现分布式爬取
redis·分布式·爬虫·http代理·学习记录
大新新大浩浩6 小时前
arm64适配系列文章-第六章-arm64环境上rabbitmq-management的部署,构建cluster-operator
rabbitmq·arm
躺不平的理查德7 小时前
General Spark Operations(Spark 基础操作)
大数据·分布式·spark
talle20217 小时前
Zeppelin在spark环境导出dataframe
大数据·分布式·spark
渣渣盟7 小时前
大数据开发环境的安装,配置(Hadoop)
大数据·hadoop·分布式
Angindem8 小时前
SpringClound 微服务分布式Nacos学习笔记
分布式·学习·微服务
电脑玩家粉色男孩11 小时前
2、Ubuntu 环境下安装RabbitMQ
linux·rabbitmq
龙仔72516 小时前
离线安装rabbitmq全流程
分布式·rabbitmq·ruby
〆、风神19 小时前
Spring Boot 整合 Lock4j + Redisson 实现分布式锁实战
spring boot·分布式·后端