学习RabbitMQ(3、实战)
引入依赖
由于是Spring Boot项目,我们使用Spring Boot RabbitMQ Starter框架来实现。
去maven仓库,找到对应的版本。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.7.2</version>
</dependency>
yml里配置,默认用户名和密码都是guest
rabbitmq:
host: localhost
port: 5672
创建消息队列
只需要执行一次。
public class MqInitMain {
public static void main(String[] args) {
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String EXCHANGE_NAME = "test_exchange";
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 创建队列,随机分配一个队列名称
String queueName = "test_queue";
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, EXCHANGE_NAME, "test_routingKey");
} catch (Exception e) {
}
}
}
在控制台可以看到:
生产者代码
@Component
public class TestProducer {
@Resource
private RabbitTemplate rabbitTemplate;
public void sendMessage(String exchange, String routingKey, String message) {
rabbitTemplate.convertAndSend(exchange, routingKey, message);
}
}
消费者代码
@Component
@Slf4j
public class TestConsumer {
// 指定程序监听的消息队列和确认机制
@RabbitListener(queues = {"code_queue"}, ackMode = "MANUAL")
public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws IOException {
log.info("receiveMessage message = {}", message);
channel.basicAck(deliveryTag, false);
}
}
测试
启动Spring Boot项目,成功启动RabbitMQ。
test类代码:
@SpringBootTest
class TestProducerTest {
@Resource
private TestProducer testProducer;
@Test
void sendMessage() {
testProducer.sendMessage("test_exchange","test_routingKey", "测试一下哦!");
}
}
运行,控制台接收到了消息。
至此测试成功。后面要对项目进行改造。
实战
- 向AI提问的方法改为向队列发送消息。
- 写一个专门的接收消息程序
- 消息全部发送到消息队列,实现了分布式
1、创建队列、交换机
也只需要创建一次。
public class BiInitMain {
public static void main(String[] args) {
try {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
String EXCHANGE_NAME = BiMqConstant.BI_EXCHANGE_NAME;
channel.exchangeDeclare(EXCHANGE_NAME, "direct");
// 创建队列,随机分配一个队列名称
String queueName = BiMqConstant.BI_QUEUE_NAME;
channel.queueDeclare(queueName, true, false, false, null);
channel.queueBind(queueName, EXCHANGE_NAME, BiMqConstant.BI_ROUTING_KEY);
} catch (Exception e) {
}
}
}
BiMqConstant 代码:
public interface BiMqConstant {
String BI_EXCHANGE_NAME = "bi_exchange";
String BI_QUEUE_NAME = "bi_queue";
String BI_ROUTING_KEY = "bi_routingKey";
}
生产者
@Component
public class BiMessageProducer {
@Resource
private RabbitTemplate rabbitTemplate;
/**
* 发送消息
* @param message
*/
public void sendMessage(String message) {
rabbitTemplate.convertAndSend(BiMqConstant.BI_EXCHANGE_NAME, BiMqConstant.BI_ROUTING_KEY, message);
}
}
消费者
@SneakyThrows
@RabbitListener(queues = {BiMqConstant.BI_QUEUE_NAME}, ackMode = "MANUAL")
public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
log.info("receiveMessage message = {}", message);
if (StringUtils.isBlank(message)) {
// 如果失败,消息拒绝
channel.basicNack(deliveryTag, false, false);
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "消息为空");
}
long chartId = Long.parseLong(message);
Chart chart = chartService.getById(chartId);
if (chart == null) {
// 如果失败,消息拒绝
channel.basicNack(deliveryTag, 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) {
channel.basicNack(deliveryTag, false, false);
handleChartUpdateError(chart.getId(), "更新图表执行中状态失败");
return;
}
// 调用 AI
String result = aiManager.doChat(CommonConstant.BI_MODEL_ID, buildUserInput(chart));
String[] splits = result.split(" ");
if (splits.length < 3) {
channel.basicNack(deliveryTag, false, false);
handleChartUpdateError(chart.getId(), "AI 生成错误");
return;
}
String genChart = splits[1].trim();
String genResult = splits[2].trim();
Chart updateChartResult = new Chart();
updateChartResult.setId(chart.getId());
updateChartResult.setGenChart(genChart);
updateChartResult.setGenResult(genResult);
// todo 建议定义状态为枚举值
updateChartResult.setStatus("succeed");
boolean updateResult = chartService.updateById(updateChartResult);
if (!updateResult) {
channel.basicNack(deliveryTag, false, false);
handleChartUpdateError(chart.getId(), "更新图表成功状态失败");
}
// 消息确认
channel.basicAck(deliveryTag, false);
}
其实代码就已经完成了。如果中间失败了,消费者还是会去消息队列取消息,保证了不丢失。