Enregistrez l'utilisation de rabbit mq pour gérer les délais de commande

Avant-propos: Au cours des derniers jours, dans le projet de clôture, j'ai constaté que les heures supplémentaires n'étaient pas suffisamment détaillées (la commande ne serait pas valide si la commande n'était pas payée après 15 minutes de passation de la commande). Délai d'expiration des commandes pour les opérations de mise à jour de la base de données, mais cela consomme trop de performances du serveur, alors pensez à d'autres solutions

Bien sûr, il y a quelques options. Personnellement, je connais trois options

  1. C'est la méthode que nous utilisons maintenant, stockons la commande dans le cache redis, puis interroge de manière floue la liste des ID de commande non payée, filtre la liste des ID et met à jour la liste des ID filtrée dans la base de données par lots.
  2. L'ID de commande est également stocké dans le cache redis. La différence est que lorsque redis est stocké, l'heure d'expiration est définie en même temps, puis la fonction de rappel d'expiration redis est activée. Reportez-vous au lien: www.cnblogs.com/NJM-F/p/104 ... , l'article sur le fonctionnement réel : Blog.csdn.net/weixin_4386 ... mais le problème est:
    Insérez la description de l'image ici
  3. Utilisez mq pour consommer, mq définit le délai d'expiration du message, après expiration du message, le message de commande est envoyé à la file d'attente, le programme écoute la file d'attente et effectue des opérations de base de données
  • En résumé, la troisième méthode devrait être la plus appropriée pour nous, la prochaine étape est l'opération spécifique

1. Rejoignez la dépendance
<!--消息队列相关依赖-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--lombok依赖-->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

复制代码
2. Modifiez le fichier de configuration application.yml
rabbitmq:
    host: localhost # rabbitmq的连接地址
    port: 5672 # rabbitmq的连接端口号
    virtual-host: /demo # rabbitmq的虚拟host
    username: root # rabbitmq的用户名
    password: root # rabbitmq的密码
    publisher-confirms: true #如果对异步消息需要回调必须设置为true
复制代码
3. Ajoutez la classe de configuration d'énumération QueueEnum de la file d'attente de messages
- 用于延迟消息队列及处理取消订单消息队列的常量定义,包括交换机名称、队列名称、路由键名称。 
复制代码
package com.xxx.xxx.xxx.enume;

import lombok.Getter;

/**
 * 消息队列枚举配置
 * @author demo
 */
@Getter
public enum QueueEnum {
    /**
     * 消息通知队列
     */
    QUEUE_ORDER_CANCEL("demo.order.direct", "demo.order.cancel", "demo.order.cancel"),
    /**
     * 消息通知ttl队列
     */
    QUEUE_TTL_ORDER_CANCEL("demo.order.direct.ttl", "demo.order.cancel.ttl", "demo.order.cancel.ttl");

    /**
     * 交换名称
     */
    private String exchange;
    /**
     * 队列名称
     */
    private String name;
    /**
     * 路由键
     */
    private String routeKey;

    QueueEnum(String exchange, String name, String routeKey) {
        this.exchange = exchange;
        this.name = name;
        this.routeKey = routeKey;
    }
}

复制代码
4. Ajoutez la classe de configuration RabbitMqConfig
package com.xx.xx.xx.config;

import com.xx.xx.xx.xx.QueueEnum;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 消息队列配置
 * @author demo
 */
@Configuration
public class RabbitMqConfig {

    /**
     * 订单消息实际消费队列所绑定的交换机
     */
    @Bean
    DirectExchange orderDirect() {
        return (DirectExchange) ExchangeBuilder
                .directExchange(QueueEnum.QUEUE_ORDER_CANCEL.getExchange())
                .durable(true)
                .build();
    }

    /**
     * 订单延迟队列队列所绑定的交换机
     */
    @Bean
    DirectExchange orderTtlDirect() {
        return (DirectExchange) ExchangeBuilder
                .directExchange(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange())
                .durable(true)
                .build();
    }

    /**
     * 订单实际消费队列
     */
    @Bean
    public Queue orderQueue() {
        return new Queue(QueueEnum.QUEUE_ORDER_CANCEL.getName());
    }

    /**
     * 订单延迟队列(死信队列)
     */
    @Bean
    public Queue orderTtlQueue() {
        return QueueBuilder
                .durable(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getName())
                .withArgument("x-dead-letter-exchange", QueueEnum.QUEUE_ORDER_CANCEL.getExchange())//到期后转发的交换机
                .withArgument("x-dead-letter-routing-key", QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey())//到期后转发的路由键
                .build();
    }

    /**
     * 将订单队列绑定到交换机
     */
    @Bean
    Binding orderBinding(DirectExchange orderDirect,Queue orderQueue){
        return BindingBuilder
                .bind(orderQueue)
                .to(orderDirect)
                .with(QueueEnum.QUEUE_ORDER_CANCEL.getRouteKey());
    }

    /**
     * 将订单延迟队列绑定到交换机
     */
    @Bean
    Binding orderTtlBinding(DirectExchange orderTtlDirect,Queue orderTtlQueue){
        return BindingBuilder
                .bind(orderTtlQueue)
                .to(orderTtlDirect)
                .with(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey());
    }

}

复制代码
Description du commutateur et de la file d'attente
  • demo.order.direct (le commutateur lié à la file d'attente de messages d'annulation de commande): la file d'attente liée est demo.order.cancel. Une fois qu'un message est envoyé en utilisant demo.order.cancel comme clé de routage, il sera envoyé à cette file d'attente.
  • demo.order.direct.ttl (le commutateur lié à la file d'attente de messages de retard de commande): la file d'attente liée est demo.order.cancel.ttl. Une fois qu'un message est envoyé en utilisant demo.order.cancel.ttl comme clé de routage, il Transférer dans cette file d'attente et l'enregistrer dans cette file d'attente pendant un certain temps. Après expiration du délai, il enverra automatiquement le message à demo.order.cancel (annuler la file d'attente de consommation de messages de commande).
5. Ajoutez l'expéditeur du message retardé CancelOrderSender (utilisé pour envoyer des messages à la file d'attente des messages de retard de commande (demo.order.cancel.ttl))
package com.xx.xx.xx.component;


import com.xx.xx.xx.enume.QueueEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 取消订单消息的发出者
 * @author demo
 */
@Component
@Slf4j
public class CancelOrderSender {

    @Resource
    private AmqpTemplate amqpTemplate;

    public void sendMessage(String orderId,final long delayTimes){
        //给延迟队列发送消息
        amqpTemplate.convertAndSend(QueueEnum.QUEUE_TTL_ORDER_CANCEL.getExchange(),
                QueueEnum.QUEUE_TTL_ORDER_CANCEL.getRouteKey(), orderId, new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                //给消息设置延迟毫秒值
                message.getMessageProperties().setExpiration(String.valueOf(delayTimes));
                return message;
            }
        });
        log.info("send delay message orderId:{}",orderId);
    }
}


复制代码
6. Ajoutez le destinataire CancelOrderReceiver du message d'annulation de commande (pour recevoir des messages de la file d'attente de messages d'annulation de commande (demo.order.cancel))
package com.xx.xx.xx.component;


import com.xx.xx.main.service.DemoService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 取消订单消息的处理者
 * @author demo
 */
@Slf4j
@Component
@RabbitListener(queues = "demo.order.cancel")
public class CancelOrderReceiver {

    @Resource
    private DemoService demoService;

    @RabbitHandler
    public void handle(String orderId){
	try {

            demoService.cancelOverTimeOrder(orderId);
        } catch (BusinessException e) {

            log.info("receive delay message orderId:{},Exception:{}", orderId, e);
        }
    }
}
复制代码
7. Envoyer des messages dans une logique métier spécifique
package com.xx.xx.main.DemoService;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

/**
 * @author abm
 */
@Slf4j
@Service
public class DemoServiceImpl implements DemoService{
	@Override
    public CommonResult generateOrder(OrderParam orderParam) {
        //todo 执行一系类下单操作
        LOGGER.info("process generateOrder");
        //下单完成后开启一个延迟消息,用于当用户没有付款时取消订单(orderId应该在下单后生成)
        sendDelayMessageCancelOrder(orderId);
        return CommonResult.success(null, "下单成功");
    }

    @Override
    public void cancelOverTimeOrder(String orderId) {
        //todo 执行一系类取消订单操作
        LOGGER.info("process cancelOrder orderId:{}",orderId);
    }

    private void sendDelayMessageCancelOrder(String orderId) {
        //获取订单超时时间,假设为60分钟
        long delayTimes = 30 * 1000;
        //发送延迟消息
        cancelOrderSender.sendMessage(orderId, delayTimes);
    }

} 

复制代码

De cette façon, après plus de 15 minutes, l'ordre de la base de données sera automatiquement modifié à l'état annulé. Mais la question se pose toujours de savoir comment traiter les commandes d'heures supplémentaires en cas de problème avec mq. Faut-il un cluster mq ou écrire une fréquence de tâche planifiée n'a pas besoin d'être trop rapide? Cette question attend que je réponde la prochaine fois

Référence article

Je suppose que tu aimes

Origine juejin.im/post/5e951c176fb9a03c62167f44
conseillé
Classement