RabbitMQ アプリケーションの問題 - メッセージ補償メカニズムとコード例

RabbitMQ アプリケーションの問題 - メッセージ補償メカニズムとコード例

RabbitMQ アプリケーションの問題

  • メッセージの信頼性の保証
    • メッセージ補償メカニズム

ここに画像の説明を挿入します

詳細な説明

ここではデモのために簡単なコードを使用します。注文の消費者は書き込みを行いません。注文が消費されると同時に、ポイント増加メッセージがポイント キューに送信されます。

詳細な手順は途中で説明されます。

わかりやすくするために、コードをここに示します。

ここに画像の説明を挿入します

ギティアドレス

1. mq-manager 親プロジェクトを作成する

1.1 依存関係のインポート
 <packaging>pom</packaging>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.0</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

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

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>2.0.7</version>
    </dependency>

</dependencies>

2. mq-common サブモジュールの作成

2.1 依存関係のインポート
<dependencies>
    <!-- mybatis-plus -->
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.2</version>
    </dependency>

    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.2.6</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
2.2エンティティクラスの記述
2.2.1注文
package com.qf.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.math.BigDecimal;
import java.util.Date;
/*
  { "bizNo": "20200803173145877",
    "status": 1,
    "price": 34.12,
    "goodId": 1002,
    "userId": 100
  }

*/

//订单
@TableName("orders")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Order {
    
    

    @TableId(value = "id", type = IdType.AUTO)
    private Integer id;

    @TableField(value = "biz_no")
    private String bizNo; //业务编号

    @TableField(value = "status")
    private Integer status;

    @TableField(value = "price")
    private BigDecimal price;

    @TableField(value = "create_time")
    private Date createTime;

    @TableField(value = "pay_time")
    private Date payTime;

    @TableField(value = "good_id")
    private Integer goodId;

    @TableField(value = "user_id")
    private Integer userId;

    //exist = false:该属性不使用
    @TableField(value = "num", exist = false)
    private Integer num;
}
2.2.2積分
package com.qf.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

//积分
@TableName("integral")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Integral{
    
    

    @TableId(type = IdType.AUTO)
    private Integer id;
    @TableField("user_id")
    private Integer userId;
    private Long score;
    private String msg;

    @TableField("create_time")
    private Date createTime;
}
2.2.3メッセージ
package com.qf.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

//消息
@TableName("message")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Msg {
    
    

    @TableId(value = "id")
    private String id;

    @TableField(value = "exchange")
    private String exchange;

    @TableField(value = "routing_key")
    private String routingKey;

    @TableField(value = "content")
    private String content; // 消息的内容

    @TableField
    private Integer status; // 消息的状态

    @TableField(value = "try_count")
    private int tryCount; //尝试次数

    @TableField(value = "create_time")
    private Date createTime;
}
2.3 パブリックパラメータの書き込み
2.3.1整数定数
package com.qf.contant;

//设置系统中的参数
public class IntegralConstant {
    
    

    // 积分系统队列
    public final static String INTEGRAL_QUEUE = "integral_queue";

    // 积分系统交换机
    public final static String INTEGRAL_EXCHANGE = "integral_exchange";

    // 积分系统的 routing-key
    public final static String INTEGRAL_ROUTING_KEY= "integral_routing_key";
}
2.3.2デッド定数
package com.qf.contant;

//死信
public class DeadConstant {
    
    
    //死信交换机
    public static final String DEAD_LETTER_EXCHANGE = "dead_letter_exchange";
    //死信路由键
    public static final String DEAD_LETTER_ROUTING_KEY = "dead_letter_routing_key";
    //死信队列
    public static final String DEAD_LETTER_QUEUE = "dead_letter_queue";
}

3. mq-order サブモジュールを作成する

3.1 パブリックモジュールをインポートする
<dependencies>
    <dependency>
        <groupId>com.qf</groupId>
        <artifactId>mq-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
3.2設定ファイルの書き込み
server:
  port: 8080

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mq?serverTimezone=Asia/Shanghai&characterEncoding=utf8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

  rabbitmq:
    username: guest
    password: guest
    host: 192.168.25.129
    port: 5672
    publisher-confirm-type: simple
    publisher-returns: true

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #驼峰形式
    map-underscore-to-camel-case: true
3.3 スタートアップクラスの書き込み
package com.qf;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.qf.mapper")
public class OrderApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(OrderApplication.class,args);
    }
}
3.4OrderControllerの書き方
package com.qf.controller;

import com.qf.entity.Order;
import com.qf.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("order")
public class OrderController {
    
    

    @Autowired
    private OrderService orderService;

    @PostMapping("insertOrder")
    public String insertOrder(@RequestBody Order order){
    
    
        orderService.insertOrder(order);
        return "success";
    }

}
3.5 OrderServiceの記述
package com.qf.service;

import com.qf.entity.Order;

public interface OrderService {
    
    
    /**
     * 插入订单
     * @param order
     */
    void insertOrder(Order order);
}
3.6OrderServiceImpl の書き込み
package com.qf.service.impl;

import com.alibaba.fastjson.JSON;
import com.qf.contant.IntegralConstant;
import com.qf.entity.Integral;
import com.qf.entity.Msg;
import com.qf.entity.Order;
import com.qf.mapper.MsgMapper;
import com.qf.mapper.OrderMapper;
import com.qf.service.OrderService;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.UUID;

@Service
@Transactional
public class OrderServiceImpl implements OrderService {
    
    

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private MsgMapper msgMapper;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public void insertOrder(Order order) {
    
    
        //插入订单
        orderMapper.insert(order);

        //插入积分,是把积分信息发送到消息队列中
        Integral integral = new Integral();
        integral.setUserId(order.getUserId());//积分对应的用户id就是下订单的用户id
        integral.setScore(10L);
        integral.setMsg("购物积分");
        integral.setCreateTime(new Date());
        //把积分对象,转换为Json类型,发送到消息队列中
        String integralJson = JSON.toJSONString(integral);

        //创建消息对象,如果消息消费成功了,再去删除对应的消息
        Msg msg = new Msg();
        //分布式环境下,id必须是唯一的,解决方案:百度的uid-generator,美团开源项目Leaf
        String uuid = UUID.randomUUID().toString();
        msg.setId(uuid);
        msg.setExchange(IntegralConstant.INTEGRAL_EXCHANGE);//积分对应的交换机
        msg.setRoutingKey(IntegralConstant.INTEGRAL_ROUTING_KEY);//积分对象的路由的key
        msg.setContent(integralJson);//积分的Json对象
        msg.setStatus(-1);//状态
        msg.setTryCount(0);//尝试次数
        msg.setCreateTime(new Date());//时间
        //插入消息
        msgMapper.insert(msg);

        //发送消息,需要把Msg对象的id(就是uuid)传过来,一旦消息消费成功,还要去Msg对应表中把该消息删除
        CorrelationData correlationData = new CorrelationData(uuid);
        System.out.println("uuid:" + uuid);
        System.out.println("correlationData.getId():" + correlationData.getId());
        //发送
        rabbitTemplate.convertAndSend(
                IntegralConstant.INTEGRAL_EXCHANGE,
                IntegralConstant.INTEGRAL_ROUTING_KEY,
                buildMessage(integralJson,uuid),
                correlationData
        );
    }

    //构建消息
    private Message buildMessage(String body,String messageId){
    
    
        //获取MessagePropertiesBuilder对象
        MessagePropertiesBuilder messagePropertiesBuilder = MessagePropertiesBuilder.newInstance();
        //获取MessageProperties对象
        MessageProperties messageProperties = messagePropertiesBuilder.build();
        messageProperties.setMessageId(messageId);
        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);//消息持久化

        Message message = new Message(body.getBytes(),messageProperties);
        System.out.println("message传递的内容:" + new String(message.getBody()));
        System.out.println("message传递的uuid:" + message.getMessageProperties().getMessageId());

        return message;
    }
}
3.7OrderMapperの書き方
package com.qf.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qf.entity.Order;
import org.springframework.stereotype.Repository;

@Repository
public interface OrderMapper extends BaseMapper<Order> {
    
    
}
3.8 MsgMapperの記述
package com.qf.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qf.entity.Msg;
import org.springframework.stereotype.Repository;

@Repository
public interface MsgMapper extends BaseMapper<Msg> {
    
    
}
3.9構成クラスの作成
3.9.1デッドコンフィグ
package com.qf.config;

import com.qf.constant.DeadConstant;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DeadConfig {
    
    

    //创建队列
    @Bean
    public Queue createDeadQueue(){
    
    
        return new Queue(DeadConstant.DEAD_LETTER_QUEUE);
    }

    //创建交换机
    @Bean
    public DirectExchange createDeadExchange(){
    
    
        //交换机默认持久化
        return new DirectExchange(DeadConstant.DEAD_LETTER_EXCHANGE);
    }

    //绑定:交换机中的消息可以发送到不同的队列
    @Bean
    public Binding bindingDeadQueue(){
    
    
        //需要设置routingKey
        return BindingBuilder.bind(createDeadQueue()).to(createDeadExchange())
                .with(DeadConstant.DEAD_LETTER_ROUTING_KEY);//和发送消息时的routingKey一致
    }
}
3.9.2IntegralConfig
package com.qf.config;

import com.qf.constant.DeadConstant;
import com.qf.constant.IntegralConstant;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

//路由模式
@Configuration
public class IntegralConfig {
    
    

    //创建队列
    @Bean
    public Queue createIntegralQueue(){
    
    

        Map<String, Object> arguments = new HashMap<>();
        arguments.put("x-dead-letter-exchange", DeadConstant.DEAD_LETTER_EXCHANGE);
        arguments.put("x-dead-letter-routing-key", DeadConstant.DEAD_LETTER_ROUTING_KEY);

        return new Queue(IntegralConstant.INTEGRAL_QUEUE,true,false,false,arguments);
    }

    //创建交换机
    @Bean
    public DirectExchange createIntegralExchange(){
    
    
        //交换机默认持久化
        return new DirectExchange(IntegralConstant.INTEGRAL_EXCHANGE);
    }

    //绑定:交换机中的消息可以发送到不同的队列
    @Bean
    public Binding bindingIntegralQueue(){
    
    
        //需要设置routingKey
        return BindingBuilder.bind(createIntegralQueue()).to(createIntegralExchange())
                .with(IntegralConstant.INTEGRAL_ROUTING_KEY);//和发送消息时的routingKey一致
    }

}
3.9.3PublisherconfirmAndReturnConfig
package com.qf.config;

import com.qf.entity.Msg;
import com.qf.mapper.MsgMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Date;
import java.util.HashMap;


@Slf4j
@Configuration
public class PublisherConfirmAndReturnConfig implements
        RabbitTemplate.ConfirmCallback,RabbitTemplate.ReturnsCallback {
    
    

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private MsgMapper msgMapper;

    @PostConstruct
    public void init(){
    
    
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnsCallback(this);
    }

    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String s) {
    
    
        //判断
        if(ack){
    
    
            log.info("已到达broker");
            log.info("correlationData id is {}",correlationData.getId());
            //删除存到数据库中的消息
            //msgMapper.deleteById(correlationData.getId());//通过id删除
            //设置删除条件
            HashMap<String, Object> map = new HashMap<>();
            map.put("id",correlationData.getId());
            map.put("status",-1);
            //多条件删除
            msgMapper.deleteByMap(map);

        }else{
    
    
            log.info("没有到达broker,实际上消息已经保存到mysql中,也可以保存到redis中");
        }
    }

    //return机制,该方法比confirm先执行,只要未到达队列的时候才执行
    @Override
    public void returnedMessage(ReturnedMessage returnedMessage) {
    
    
        log.info("消息未到达队列");
        //如果消息到达队列不执行以下代码

        //消息已经从数据库被删除了
        //考虑人工干预,获取消息信息进行保存
        String exchange = returnedMessage.getExchange();
        String routingKey = returnedMessage.getRoutingKey();
        Message message = returnedMessage.getMessage();

        //创建消息对象,如果消息消费成功了,再去删除对应的消息
        Msg msg = new Msg();

        msg.setId(message.getMessageProperties().getMessageId());
        msg.setExchange(exchange);//积分对应的交换机
        msg.setRoutingKey(routingKey);//积分对象的路由的key
        msg.setContent(new String(message.getBody()));//积分的Json对象
        msg.setStatus(-2);//状态可以设置为和之前不一样
        msg.setTryCount(0);//尝试次数
        msg.setCreateTime(new Date());//时间
        //插入消息
        msgMapper.insert(msg);
        //做进一步处理:给管理员发邮件,发短信....

    }
}

4. mq-integral サブモジュールの作成

4.1 依存関係のインポート
<dependencies>
    <dependency>
        <groupId>com.qf</groupId>
        <artifactId>mq-common</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
    
    <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.4</version>
        </dependency>
</dependencies>
4.2設定ファイルの書き込み
server:
  port: 8081

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mq?serverTimezone=Asia/Shanghai&characterEncoding=utf8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

  rabbitmq:
    username: guest
    password: guest
    host: 192.168.25.134
    port: 5672
    listener:
      simple:
        retry:
          enabled: true #开启消息重试
          max-attempts: 3 #最大重试次数
          initial-interval: 2000ms #每次重试的时间间隔
          multiplier: 2 #每次重试时间乘以当前倍数
        #重试机制必须是自动ack,才能放到死信队列中
        acknowledge-mode: auto

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #驼峰形式
    map-underscore-to-camel-case: true
4.3スタートアップクラスの書き込み
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.qf.mapper")
public class IntegralApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(IntegralApplication.class,args);
    }
}
4.4 IntegralImplの記述
package com.qf.service.impl;

import com.alibaba.fastjson.JSON;
import com.qf.constant.IntegralConstant;
import com.qf.entity.Integral;
import com.qf.mapper.IntegralMapper;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Random;
import java.util.concurrent.TimeUnit;

@Service
public class IntegralImpl {
    
    

    @Autowired
    public IntegralMapper integralMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

//    //第一种方式    
//    @RabbitListener(
//            bindings = @QueueBinding(
//                    value = @Queue(name = IntegralConstant.INTEGRAL_QUEUE,
//                    durable = "true"
//            ),
//            key = {IntegralConstant.INTEGRAL_ROUTING_KEY},
//            exchange = @Exchange(name = IntegralConstant.INTEGRAL_EXCHANGE, durable = "true")
//    ))
//    public void insertIntegral(Message message){
    
    
//        //积分信息
//        String integralJson = new String(message.getBody());
//        System.out.println(integralJson);
//
//        //类型转换
//        Integral integral = JSON.parseObject(integralJson, Integral.class);
//        System.out.println(integral);
//        //插入数据库
//        integralMapper.insert(integral);
//    }

//    //第二种方式  
//    @RabbitListener(queues = IntegralConstant.INTEGRAL_QUEUE)
//    public void insertIntegral(Message message) throws InterruptedException {
    
    
//        //积分信息
//        String integralJson = new String(message.getBody());
//        System.out.println(integralJson);
//
//        //运行成功
//        //类型转换
//        Integral integral = JSON.parseObject(integralJson, Integral.class);
//        System.out.println(integral);
//        //插入数据库
//        integralMapper.insert(integral);
//
//        //测试消息重复消费
//        //直到方法执行完毕,才会ack
//        Thread.sleep(500000);
//
//        //测试消息重试
//        //System.out.println("当前系统时间:"+System.currentTimeMillis());
//        //运行失败
//        //throw new RuntimeException("消息消费异常...");
//    }

    
    @RabbitListener(queues = IntegralConstant.INTEGRAL_QUEUE)
    public void receiveIntegralMessage(Message message){
    
    
        //获取要被消息的消息id
        String messageId = message.getMessageProperties().getMessageId();
        //判断,如果redis中没有这个消息id的key,则是第一次消费该消息
        if(!stringRedisTemplate.hasKey(messageId)){
    
    
            //获取积分信息
            String integralJson = new String(message.getBody());
            //类型转换
            Integral integral = JSON.parseObject(integralJson, Integral.class);
            //插入到数据库
            integralMapper.insert(integral);
            //使用hutool工具类生成随机数
            int randomInt = RandomUtil.randomInt(10, 100);
            //往redis中也存一份
            stringRedisTemplate.opsForValue().setIfAbsent(messageId,
                    String.valueOf(randomInt),600, TimeUnit.SECONDS);

        }

}
4.5 IntegralMapperの記述
package com.qf.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qf.entity.Integral;
import org.springframework.stereotype.Repository;

@Repository
public interface IntegralMapper extends BaseMapper<Integral> {
    
    
}

5. mq-compensate サブプロジェクトの作成

5.1 依存関係のインポート
<dependencies>
    <dependency>
        <groupId>com.qf</groupId>
        <artifactId>mq-order</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>
4.2設定ファイルの書き込み
server:
  port: 8082

spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/mq?serverTimezone=Asia/Shanghai&characterEncoding=utf8
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

  rabbitmq:
    username: guest
    password: guest
    host: 192.168.25.134
    port: 5672
    publisher-confirm-type: simple
    publisher-returns: true

mybatis-plus:
  mapper-locations: classpath:mapper/*.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    #驼峰形式
    map-underscore-to-camel-case: true
4.3スタートアップクラスの書き込み
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class CompensateApplication {
    
    
    public static void main(String[] args) {
    
    
        SpringApplication.run(CompensateApplication.class,args);
    }
}
4.4ライティングマッサージ補償タスク
package com.qf.task;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.qf.contant.IntegralConstant;
import com.qf.entity.Msg;
import com.qf.mapper.MsgMapper;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.core.MessagePropertiesBuilder;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;

import javax.annotation.Resource;
import java.util.List;

/**
 *
 * 消息补偿:隔一段时间,去数据库查询未消费掉的消息,再次执行
 */

@Configuration
public class MassageCompensateTask {
    
    

    @Autowired
    private MsgMapper msgMapper;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Scheduled(cron = "10 * * * * ?")
    public void compensateTask(){
    
    
        //设置查询条件
        QueryWrapper<Msg> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("status","-2");
        queryWrapper.lt("try_count","3");
        //查询消息
        List<Msg> msgList = msgMapper.selectList(queryWrapper);
        //判断
        if(msgList.size() > 0){
    
    
            for(Msg msg : msgList){
    
    

                System.out.println("数据库中的消息id:" + msg.getId());
                //发送
                rabbitTemplate.convertAndSend(
                        msg.getExchange(),
                        msg.getRoutingKey(),
                        buildMessage(msg.getContent(),msg.getId()),
                        new CorrelationData(msg.getId())
                );

                //设置尝试次数
                msg.setTryCount(msg.getTryCount() + 1);
                //修改数据库中的消息
                msgMapper.updateById(msg);

            }
        }
    }

    //构建消息
    private Message buildMessage(String body, String messageId){
    
    
        //获取MessagePropertiesBuilder对象
        MessagePropertiesBuilder messagePropertiesBuilder = MessagePropertiesBuilder.newInstance();
        //获取MessageProperties对象
        MessageProperties messageProperties = messagePropertiesBuilder.build();
        messageProperties.setMessageId(messageId);
        messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);//消息持久化

        Message message = new Message(body.getBytes(),messageProperties);
        System.out.println("message传递的内容:" + new String(message.getBody()));
        System.out.println("message传递的uuid:" + message.getMessageProperties().getMessageId());

        return message;
    }
}

おすすめ

転載: blog.csdn.net/qq_52183856/article/details/130743647