欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java中實現(xiàn)訂單超時自動取消功能(最新推薦)

 更新時間:2025年02月25日 14:25:49   作者:cookie@0x3f3f3f3f  
本文介紹了Java中實現(xiàn)訂單超時自動取消功能的幾種方法,包括定時任務(wù)、JDK延遲隊列、Redis過期監(jiān)聽、Redisson分布式延遲隊列、RocketMQ延遲消息和RabbitMQ死信隊列,每種方法都有其優(yōu)缺點,可以根據(jù)具體需求選擇合適的方法,感興趣的朋友一起看看吧

        在開發(fā)中,我們會遇到需要延時任務(wù)的業(yè)務(wù)場景,例如:用戶下單之后未在規(guī)定的時間內(nèi)支付成功,該訂單會自動取消; 用戶注冊成功15分鐘后,發(fā)消息通知用戶;還有比如到期自動收貨,超時自動退款等都是類似的延時任務(wù)的業(yè)務(wù)問題。

這里主要介紹一下幾種方法:

  • 1、定時任務(wù)
  • 2、JDK延遲隊列 DelayQueue
  • 3、redis過期監(jiān)聽
  • 4、Redisson分布式延遲隊列
  • 5、RocketMQ延遲消息
  • 6、RabbitMQ死信隊列

1、定時任務(wù)

        寫一個定時任務(wù),定期掃描數(shù)據(jù)庫中的訂單,如果時間過期,就取消這個訂單。這種實現(xiàn)方法成本低、實現(xiàn)容易。這里使用@Scheduled注解實現(xiàn),也可以用Quartz框架實現(xiàn)定時任務(wù)。

@Scheduled(cron = "30 * * * * ?")
public void scanOrder(){
   orderService.scanOrder(); //每30秒掃描數(shù)據(jù)庫 找出過期未支付的訂單,取消該訂單
}

優(yōu)點:實現(xiàn)容易,成本低,不依賴其他組件。

缺點:

  • 時間不夠精確。因為掃描是有間隔的,但卻隨時會產(chǎn)生過期的訂單,所以可能會導(dǎo)致有些訂單已經(jīng)過期了一段時間后才被掃描到。
  • 增加了數(shù)據(jù)庫的壓力。頻繁的訪問數(shù)據(jù)庫,當(dāng)數(shù)據(jù)越來越多時,訪問數(shù)據(jù)庫的成本也會增加。

2、JDK延遲隊列 DelayQueue

      DelayQueue是JDK提供的一個無界隊列,它的本質(zhì)是封裝了一個PriorityQueue(優(yōu)先隊列), PriorityQueue內(nèi)部使用完全二叉堆來實現(xiàn)隊列排序,在向隊列中插入元素時,需要給出這個元素的Delay時間,也就是過期時間,隊列中最小的元素會被放在隊首,隊列中的元素只有到了Delay時間才允許從隊列中取出。

   具體的實現(xiàn)思路就是:首先創(chuàng)建一個實體類實現(xiàn)Delay接口,然后將它放入DelayQueue隊列中。

(1)定義實現(xiàn)Delayed接口的實體類

需要實現(xiàn)Delayed接口的兩個方法:getDelay()和compareTo()

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class MyDelay implements Delayed {
    private String orderNumber;  //訂單編號
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Long time;   //過期時間
    @Override
    public long getDelay(TimeUnit timeUnit) {
        return time - System.currentTimeMillis();
    }
    @Override
    public int compareTo(Delayed delayed) {
        MyDelay myDelay = (MyDelay)delayed;
        return this.time.compareTo(myDelay.getTime());
    }
}

 (2)將延時任務(wù)放入隊列

package com.demo;
import com.demo.config.MyDelay;
import java.util.concurrent.DelayQueue;
public class demo {
    public static void main(String[] args) throws InterruptedException {
        MyDelay myDelay1 = new MyDelay("0001", 5L);
        MyDelay myDelay2 = new MyDelay("0002", 10L);
        MyDelay myDelay3 = new MyDelay("0003", 15L);
        DelayQueue<MyDelay> delayDelayQueue = new DelayQueue<MyDelay>();
        delayDelayQueue.add(myDelay1);
        delayDelayQueue.add(myDelay2);
        delayDelayQueue.add(myDelay3);
        while (delayDelayQueue.size()!=0) {
                /**
                 * 取隊列頭部元素是否過期
                 */
                //DelayQueue的put/add方法是線程安全的,因為put/add方法內(nèi)部使用了ReentrantLock鎖進(jìn)行線程同步。
                // DelayQueue還提供了兩種出隊的方法 poll() 和 take() ,
                // poll() 為非阻塞獲取,沒有到期的元素直接返回null;
                // take() 阻塞方式獲取,沒有到期的元素線程將會等待。
                MyDelay order = delayDelayQueue.poll();
                if(order!=null) {
                    System.out.println("訂單編號:"+order.getOrderNumber()+",超時取消!");
                }
                Thread.sleep(1000);
        }
    }
}

優(yōu)點:不依賴任何第三方組件,實現(xiàn)方便。

缺點:因為DelayQueue是基于JVM的,如果放入的訂單過多,會造成JVM溢出。如果JVM重啟了,那所有的數(shù)據(jù)就丟失了。

3、redis過期監(jiān)聽

redis是一個高性能的key,value數(shù)據(jù)庫,除了用作緩存之外,它還提供了過期監(jiān)聽的功能。

在redis.conf中配置

配置notify-keyspace-events "Ex" 即可開啟此功能。

springboot 項目集成redis配置過期監(jiān)聽

在pom中引入依賴

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

在yml中配置redis源

redis:
  #數(shù)據(jù)庫索引
  database: 0
  host: 127.0.0.1
  port: 6379
  password: 123456
  jedis:
    pool:
      #最大連接數(shù)
      max-active: 15
      #最大阻塞等待時間(負(fù)數(shù)表示沒限制)
      max-wait: -1
      #最大空閑
      max-idle: 15
      #最小空閑
      min-idle: 0
      #連接超時時間
  timeout: 10000

編寫redis配置類

package com.example.study_demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
/**
 * Redis配置
 */
@Configuration
public class RedisConfig {
    @Autowired
    private RedisConnectionFactory redisConnectionFactory;
    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer() {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        return redisMessageListenerContainer;
    }
    @Bean
    public KeyExpiredListener keyExpiredListener() {
        return new KeyExpiredListener(this.redisMessageListenerContainer());
    }
}

編寫redis工具類

package com.example.study_demo.utils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundSetOperations;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Component
public class RedisCache
{
    @Autowired
    public RedisTemplate redisTemplate;
    /**
     * 緩存基本的對象,Integer、String、實體類等
     *
     * @param key 緩存的鍵值
     * @param value 緩存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 緩存基本的對象,Integer、String、實體類等
     *
     * @param key 緩存的鍵值
     * @param value 緩存的值
     * @param timeout 時間
     * @param timeUnit 時間顆粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }
    /**
     * 設(shè)置有效時間
     *
     * @param key Redis鍵
     * @param timeout 超時時間
     * @return true=設(shè)置成功;false=設(shè)置失敗
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }
    /**
     * 設(shè)置有效時間
     *
     * @param key Redis鍵
     * @param timeout 超時時間
     * @param unit 時間單位
     * @return true=設(shè)置成功;false=設(shè)置失敗
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }
    /**
     * 獲得緩存的基本對象。
     *
     * @param key 緩存鍵值
     * @return 緩存鍵值對應(yīng)的數(shù)據(jù)
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }
    /**
     * 刪除單個對象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }
    /**
     * 刪除集合對象
     *
     * @param collection 多個對象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }
    /**
     * 緩存List數(shù)據(jù)
     *
     * @param key 緩存的鍵值
     * @param dataList 待緩存的List數(shù)據(jù)
     * @return 緩存的對象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }
    /**
     * 獲得緩存的list對象
     *
     * @param key 緩存的鍵值
     * @return 緩存鍵值對應(yīng)的數(shù)據(jù)
     */
    public <T> List<T> getCacheList(final String key)
    {
        return redisTemplate.opsForList().range(key, 0, -1);
    }
    /**
     * 緩存Set
     *
     * @param key 緩存鍵值
     * @param dataSet 緩存的數(shù)據(jù)
     * @return 緩存數(shù)據(jù)的對象
     */
    public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet)
    {
        BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key);
        Iterator<T> it = dataSet.iterator();
        while (it.hasNext())
        {
            setOperation.add(it.next());
        }
        return setOperation;
    }
    /**
     * 獲得緩存的set
     *
     * @param key
     * @return
     */
    public <T> Set<T> getCacheSet(final String key)
    {
        return redisTemplate.opsForSet().members(key);
    }
    /**
     * 緩存Map
     *
     * @param key
     * @param dataMap
     */
    public <T> void setCacheMap(final String key, final Map<String, T> dataMap)
    {
        if (dataMap != null) {
            redisTemplate.opsForHash().putAll(key, dataMap);
        }
    }
    /**
     * 獲得緩存的Map
     *
     * @param key
     * @return
     */
    public <T> Map<String, T> getCacheMap(final String key)
    {
        return redisTemplate.opsForHash().entries(key);
    }
    /**
     * 往Hash中存入數(shù)據(jù)
     *
     * @param key Redis鍵
     * @param hKey Hash鍵
     * @param value 值
     */
    public <T> void setCacheMapValue(final String key, final String hKey, final T value)
    {
        redisTemplate.opsForHash().put(key, hKey, value);
    }
    /**
     * 獲取Hash中的數(shù)據(jù)
     *
     * @param key Redis鍵
     * @param hKey Hash鍵
     * @return Hash中的對象
     */
    public <T> T getCacheMapValue(final String key, final String hKey)
    {
        HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash();
        return opsForHash.get(key, hKey);
    }
    /**
     * 刪除Hash中的數(shù)據(jù)
     * 
     * @param key
     * @param hkey
     */
    public void delCacheMapValue(final String key, final String hkey)
    {
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(key, hkey);
    }
    /**
     * 獲取多個Hash中的數(shù)據(jù)
     *
     * @param key Redis鍵
     * @param hKeys Hash鍵集合
     * @return Hash對象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    /**
     * 獲得緩存的基本對象列表
     *
     * @param pattern 字符串前綴
     * @return 對象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}

編寫監(jiān)控類

在代碼中繼承KeyspaceEventMessageListener ,實現(xiàn)onMessage就可以監(jiān)聽過期的數(shù)據(jù)量

package com.example.study_demo.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.Message;
import org.springframework.data.redis.listener.KeyExpirationEventMessageListener;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
@Slf4j
public class KeyExpiredListener extends KeyExpirationEventMessageListener {
    public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) {
        super(listenerContainer);
    }
    @Override
    public void onMessage(Message message, byte[] pattern) {
        String expiredKey = message.toString();
        log.info("訂單{}過期了", expiredKey);
    }
}

測試

package com.demo;
import com.demo.config.MyDelay;
import java.util.concurrent.DelayQueue;
public class demo {
    public static void main(String[] args) throws InterruptedException {
        long expire = 5L; //設(shè)置過期時間
        String key = "0001";
        RedisCache redisCache = new RedisCache();
        redisCache.setCacheObject(key,"訂單過期了");
        redisCache.expire(key,expire);
    }
}

優(yōu)點:由于redis的高性能,所以在設(shè)置以及消費key時的速度可以保證。

缺點: 由于redis的key過期策略的原因,當(dāng)一個key過期時,無法立刻保證將其刪除,自然我們監(jiān)聽事件也無法第一時間消費到這個key,所以會存在一定的延遲。 此外,在redis5.0之前,訂閱發(fā)布消息并沒有被持久化,自然也沒有所謂的確認(rèn)機(jī)制,所以一旦消費信息過程中我們的客戶端發(fā)生了宕機(jī),這條消息就徹底丟失了。

4、Redisson分布式延遲隊列

       Redisson是一個基于redis實現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)絡(luò),它不僅提供了一系列的分布式Java常用對象,還提供了許多分布式服務(wù)。Redisson除了提供我們常用的分布式鎖外,還提供了一個分布式延遲隊列RDelayedQueue ,它是一種基于zset結(jié)構(gòu)實現(xiàn)的延遲隊列,其實現(xiàn)類是RedissonDelayedQueue,在springboot中整合使用Redisson分布式延遲隊列的步驟如下:

引入pom依賴,yml中配置redis連接

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.10.5</version>
</dependency>

創(chuàng)建延時隊列生產(chǎn)者

import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 延遲隊列生產(chǎn)者
 */
@Service
public class RDelayQueueProducer {
    @Autowired
    private RedissonClient redissonClient;
    public void addTask(String taskId, long delayTime){
        //創(chuàng)建一個延遲隊列
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("my_delayQueue"));
        //將任務(wù)添加到延遲隊列,指定延遲時間
        delayedQueue.offer(taskId,delayTime,java.util.concurrent.TimeUnit.SECONDS);
    }
}

創(chuàng)建延時隊列消費者

import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 延遲隊列消費者
 */
@Service
public class RDelayQueueConsumer {
    @Autowired
    private RedissonClient redissonClient;
    public void consumeTask(){
        RDelayedQueue<String> delayedQueue = redissonClient.getDelayedQueue(redissonClient.getQueue("my_delayQueue"));
        while (true){
            String poll = delayedQueue.poll();
            if(poll!=null){
                //收到消息進(jìn)行處理
                System.out.println("收到消息:"+poll);
            }
        }
    }
}

測試

@PostMapping("/test")
    public void test(){
        rDelayQueueProducer.addTask("0001",5);
        rDelayQueueProducer.addTask("0002",10);
        rDelayQueueProducer.addTask("0003",15);
    }

優(yōu)點:使用簡單,并且其實現(xiàn)類中大量使用lua腳本保證其原子性,不會有并發(fā)重復(fù)問題。

缺點:需要依賴redis

5、RocketMQ延遲消息

       RocketMQ是阿里巴巴開源的一款分布式消息中間件,基于高可用分布式集群技術(shù),提供低延遲的、可靠的消息發(fā)布與訂閱服務(wù)。下面是在springboot中集成RocketMQ延遲消息的步驟:

安裝并啟動 RocketMQ 服務(wù)

       可參考RocketMQ 官方文檔進(jìn)行安裝和啟動

引入依賴

 <dependency>
        <groupId>org.apache.rocketmq</groupId>
        <artifactId>rocketmq-spring-boot-starter</artifactId>
        <version>2.2.2</version>
  </dependency>

配置RocketMQ

spring:
  rocketmq:
    name-server: 127.0.0.1:9876 # RocketMQ NameServer地址
    producer:
      group: my-group # 生產(chǎn)者組名

創(chuàng)建消息生產(chǎn)者

import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class RocketMQProducerService {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    public void sendMessage(String topic, String message,long delay) {
        // 發(fā)送延遲消息,延遲級別為16,對應(yīng)延遲時間為delay
        rocketMQTemplate.syncSend(topic, message, delay, 16);
    }
}

創(chuàng)建消息消費者

import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Service;
@Service
@RocketMQMessageListener(topic = "test-topic", consumerGroup = "my-consumer-group")
public class RocketMQConsumerService implements RocketMQListener<String> {
    @Override
    public void onMessage(String message) {
        System.out.println("接收到消息: " + message);
         //檢查訂單是否支付
    }
}

測試

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class RocketMQTestController {
    @Autowired
    private RocketMQProducerService producerService;
    @GetMapping("/sendMessage")
    public String sendMessage() {
        String topic = "test-topic";
        String message = "0001"; //發(fā)送訂單編號到rocketMQ
        long delay = 3000;
        producerService.sendMessage(topic, message, delay);
        return "消息發(fā)送成功";
    }
}

優(yōu)點:系統(tǒng)之間完全解耦,只需要關(guān)注生產(chǎn)及消費即可。其吞吐量極高。

缺點:RocketMQ是重量級的組件,引入后,隨之而來的消息丟失等問題都增加了系統(tǒng)的復(fù)雜度。

6、RabbitMQ死信隊列

       當(dāng)RabbitMQ中的一條正常信息,因為過了存活時間(ttl過期)、隊列長度超限等原因無法被消費時,就會被當(dāng)成一條死信消息,投遞到死信隊列。基于這樣的機(jī)制,我們可以給消息設(shè)置一個ttl ,等消息過期就會進(jìn)入死信隊列,我們再消費死信隊列即可,這樣,就可以達(dá)到和RocketMQ一樣的效果。springboot集成rabbitMQ的步驟如下:

安裝并啟動 RabbitMQ 服務(wù)

       可參考RabbitMQ官方文檔進(jìn)行安裝和啟動

引入依賴

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

配置RabbitMQ

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest

配置 RabbitMQ 隊列和交換機(jī)

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class RabbitMQConfig {
    public static final String ORDER_EXCHANGE = "order.exchange";
    public static final String ORDER_QUEUE = "order.queue";
    public static final String ORDER_ROUTING_KEY = "order.routing.key";
    public static final String DEAD_LETTER_EXCHANGE = "dead.letter.exchange";
    public static final String DEAD_LETTER_QUEUE = "dead.letter.queue";
    public static final String DEAD_LETTER_ROUTING_KEY = "dead.letter.routing.key";
    // 死信交換機(jī)
    @Bean
    public DirectExchange deadLetterExchange() {
        return new DirectExchange(DEAD_LETTER_EXCHANGE);
    }
    // 死信隊列
    @Bean
    public Queue deadLetterQueue() {
        return new Queue(DEAD_LETTER_QUEUE);
    }
    // 綁定死信隊列和死信交換機(jī)
    @Bean
    public Binding deadLetterBinding() {
        return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with(DEAD_LETTER_ROUTING_KEY);
    }
    // 正常交換機(jī)
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange(ORDER_EXCHANGE);
    }
    // 正常隊列,設(shè)置死信交換機(jī)和路由鍵,以及消息TTL為30分鐘(1800000毫秒)
    @Bean
    public Queue orderQueue() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);
        args.put("x-dead-letter-routing-key", DEAD_LETTER_ROUTING_KEY);
        args.put("x-message-ttl", 1800000);
        return new Queue(ORDER_QUEUE, true, false, false, args);
    }
    // 綁定正常隊列和正常交換機(jī)
    @Bean
    public Binding orderBinding() {
        return BindingBuilder.bind(orderQueue()).to(orderExchange()).with(ORDER_ROUTING_KEY);
    }
}

創(chuàng)建消息生產(chǎn)者

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class OrderMessageProducer {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    public void sendOrderMessage(String message) {
        rabbitTemplate.convertAndSend(RabbitMQConfig.ORDER_EXCHANGE, RabbitMQConfig.ORDER_ROUTING_KEY, message);
    }
}

創(chuàng)建消息消費者

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
@Service
public class OrderMessageConsumer {
    @RabbitListener(queues = RabbitMQConfig.DEAD_LETTER_QUEUE)
    public void receiveOrderMessage(String message) {
        System.out.println("收到訂單: " + message);
        // 模擬檢查訂單支付狀態(tài)
    }
}

測試

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderMessageController {
    @Autowired
    private OrderMessageProducer orderMessageProducer;
    @GetMapping("/sendOrderMessage")
    public String sendOrderMessage() {
        String message = "0001"; //訂單編號
        orderMessageProducer.sendOrderMessage(message);
        return "訂單消息已發(fā)送,30分鐘后處理";
    }
}

優(yōu)點:同RocketMQ一樣可以使業(yè)務(wù)解耦。

缺點:RabbitMQ 的 TTL 是基于隊列的,而不是基于單個消息的精確時間控制。當(dāng)隊列中有多個消息時,即使某個消息的 TTL 已經(jīng)過期,也需要等待前面的消息被處理完才能進(jìn)入死信隊列,導(dǎo)致消息的實際處理時間可能會有一定的延遲,無法保證精確的延遲時間。

到此這篇關(guān)于Java中如何實現(xiàn)訂單超時自動取消功能的文章就介紹到這了,更多相關(guān)Java訂單超時自動取消內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中的自定義異常實現(xiàn)方式

    Java中的自定義異常實現(xiàn)方式

    這篇文章主要介紹了Java中的自定義異常實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • Java實現(xiàn)批量查找與替換Excel文本的思路詳解

    Java實現(xiàn)批量查找與替換Excel文本的思路詳解

    在 Java 中,可以通過find和replace的方法來查找和替換單元格的數(shù)據(jù),下面小編將以Excel文件為例為大家介紹如何實現(xiàn)Excel文件內(nèi)容的批量替換,感興趣的朋友跟隨小編一起看看吧
    2023-10-10
  • SpringBoot Actuator未授權(quán)訪問漏洞的排查和解決方法

    SpringBoot Actuator未授權(quán)訪問漏洞的排查和解決方法

    Spring Boot Actuator 是開發(fā)和管理生產(chǎn)級 Spring Boot 應(yīng)用程序的重要工具,它可以幫助你確保應(yīng)用程序的穩(wěn)定性和性能,本文給大家介紹了SpringBoot Actuator未授權(quán)訪問漏洞的排查和解決方法,需要的朋友可以參考下
    2024-05-05
  • Spring + Spring Boot + MyBatis + MongoDB的整合教程

    Spring + Spring Boot + MyBatis + MongoDB的整合教程

    這篇文章主要給大家介紹了關(guān)于Spring + Spring Boot + MyBatis + MongoDB的整合教程,文中通過圖文以及示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起看看吧。
    2017-12-12
  • 學(xué)好Java?MyBatis攔截器,提高工作效率

    學(xué)好Java?MyBatis攔截器,提高工作效率

    這篇文章主要介紹了Java中的?MyBatis攔截器,??Mybatis攔截器設(shè)計的初衷就是為了供用戶在某些時候可以實現(xiàn)自己的邏輯而不必去動Mybatis固有的邏輯。詳細(xì)內(nèi)容需要的小伙伴可以參考下面文章內(nèi)容,希望對你有所幫助
    2022-02-02
  • 詳解如何用Java實現(xiàn)對m3u8直播流抽幀

    詳解如何用Java實現(xiàn)對m3u8直播流抽幀

    抽幀(frame extraction)是指從視頻流中提取一些特定的幀,通常是關(guān)鍵幀或者隨機(jī)幀,以供后續(xù)處理。這篇文章主要為大家介紹了如何用Java實現(xiàn)對m3u8直播流抽幀,需要的可以參考一下
    2023-03-03
  • Mybatis通過攔截器實現(xiàn)單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換

    Mybatis通過攔截器實現(xiàn)單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換

    這篇文章主要為大家詳細(xì)介紹了Mybatis如何通過攔截器實現(xiàn)單數(shù)據(jù)源內(nèi)多數(shù)據(jù)庫切換,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-12-12
  • SpringMVC的簡單傳值(實現(xiàn)代碼)

    SpringMVC的簡單傳值(實現(xiàn)代碼)

    下面小編就為大家?guī)硪黄猄pringMVC的簡單傳值(實現(xiàn)代碼)。小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-05-05
  • Java對Map進(jìn)行按value排序的幾種常見方法

    Java對Map進(jìn)行按value排序的幾種常見方法

    在日常開發(fā)中,Map 是我們經(jīng)常使用的數(shù)據(jù)結(jié)構(gòu)之一,盡管 Map 是按鍵 (key) 存儲和檢索數(shù)據(jù)的,但有時我們需要根據(jù) value 進(jìn)行排序,這篇博客將詳細(xì)探討如何在 Java 中對 Map 進(jìn)行按 value 排序的幾種常見方法,并分析它們的優(yōu)缺點,需要的朋友可以參考下
    2025-03-03
  • IDEA 自動跳出括號的快捷鍵分享

    IDEA 自動跳出括號的快捷鍵分享

    這篇文章主要介紹了IDEA 自動跳出括號的快捷鍵分享,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02

最新評論