最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

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

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

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

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

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

1、定時(shí)任務(wù)

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

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

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

缺點(diǎn):

  • 時(shí)間不夠精確。因?yàn)閽呙枋怯虚g隔的,但卻隨時(shí)會(huì)產(chǎn)生過(guò)期的訂單,所以可能會(huì)導(dǎo)致有些訂單已經(jīng)過(guò)期了一段時(shí)間后才被掃描到。
  • 增加了數(shù)據(jù)庫(kù)的壓力。頻繁的訪問(wèn)數(shù)據(jù)庫(kù),當(dāng)數(shù)據(jù)越來(lái)越多時(shí),訪問(wèn)數(shù)據(jù)庫(kù)的成本也會(huì)增加。

2、JDK延遲隊(duì)列 DelayQueue

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

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

(1)定義實(shí)現(xiàn)Delayed接口的實(shí)體類(lèi)

需要實(shí)現(xiàn)Delayed接口的兩個(gè)方法: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;  //訂單編號(hào)
    @JsonFormat(locale = "zh", timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss")
    private Long time;   //過(guò)期時(shí)間
    @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)將延時(shí)任務(wù)放入隊(duì)列

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) {
                /**
                 * 取隊(duì)列頭部元素是否過(guò)期
                 */
                //DelayQueue的put/add方法是線程安全的,因?yàn)閜ut/add方法內(nèi)部使用了ReentrantLock鎖進(jìn)行線程同步。
                // DelayQueue還提供了兩種出隊(duì)的方法 poll() 和 take() ,
                // poll() 為非阻塞獲取,沒(méi)有到期的元素直接返回null;
                // take() 阻塞方式獲取,沒(méi)有到期的元素線程將會(huì)等待。
                MyDelay order = delayDelayQueue.poll();
                if(order!=null) {
                    System.out.println("訂單編號(hào):"+order.getOrderNumber()+",超時(shí)取消!");
                }
                Thread.sleep(1000);
        }
    }
}

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

缺點(diǎn):因?yàn)镈elayQueue是基于JVM的,如果放入的訂單過(guò)多,會(huì)造成JVM溢出。如果JVM重啟了,那所有的數(shù)據(jù)就丟失了。

3、redis過(guò)期監(jiān)聽(tīng)

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

在redis.conf中配置

配置notify-keyspace-events "Ex" 即可開(kāi)啟此功能。

springboot 項(xiàng)目集成redis配置過(guò)期監(jiān)聽(tīng)

在pom中引入依賴(lài)

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

在yml中配置redis源

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

編寫(xiě)redis配置類(lèi)

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());
    }
}

編寫(xiě)redis工具類(lèi)

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;
    /**
     * 緩存基本的對(duì)象,Integer、String、實(shí)體類(lèi)等
     *
     * @param key 緩存的鍵值
     * @param value 緩存的值
     */
    public <T> void setCacheObject(final String key, final T value)
    {
        redisTemplate.opsForValue().set(key, value);
    }
    /**
     * 緩存基本的對(duì)象,Integer、String、實(shí)體類(lèi)等
     *
     * @param key 緩存的鍵值
     * @param value 緩存的值
     * @param timeout 時(shí)間
     * @param timeUnit 時(shí)間顆粒度
     */
    public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit)
    {
        redisTemplate.opsForValue().set(key, value, timeout, timeUnit);
    }
    /**
     * 設(shè)置有效時(shí)間
     *
     * @param key Redis鍵
     * @param timeout 超時(shí)時(shí)間
     * @return true=設(shè)置成功;false=設(shè)置失敗
     */
    public boolean expire(final String key, final long timeout)
    {
        return expire(key, timeout, TimeUnit.SECONDS);
    }
    /**
     * 設(shè)置有效時(shí)間
     *
     * @param key Redis鍵
     * @param timeout 超時(shí)時(shí)間
     * @param unit 時(shí)間單位
     * @return true=設(shè)置成功;false=設(shè)置失敗
     */
    public boolean expire(final String key, final long timeout, final TimeUnit unit)
    {
        return redisTemplate.expire(key, timeout, unit);
    }
    /**
     * 獲得緩存的基本對(duì)象。
     *
     * @param key 緩存鍵值
     * @return 緩存鍵值對(duì)應(yīng)的數(shù)據(jù)
     */
    public <T> T getCacheObject(final String key)
    {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }
    /**
     * 刪除單個(gè)對(duì)象
     *
     * @param key
     */
    public boolean deleteObject(final String key)
    {
        return redisTemplate.delete(key);
    }
    /**
     * 刪除集合對(duì)象
     *
     * @param collection 多個(gè)對(duì)象
     * @return
     */
    public long deleteObject(final Collection collection)
    {
        return redisTemplate.delete(collection);
    }
    /**
     * 緩存List數(shù)據(jù)
     *
     * @param key 緩存的鍵值
     * @param dataList 待緩存的List數(shù)據(jù)
     * @return 緩存的對(duì)象
     */
    public <T> long setCacheList(final String key, final List<T> dataList)
    {
        Long count = redisTemplate.opsForList().rightPushAll(key, dataList);
        return count == null ? 0 : count;
    }
    /**
     * 獲得緩存的list對(duì)象
     *
     * @param key 緩存的鍵值
     * @return 緩存鍵值對(duì)應(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ù)的對(duì)象
     */
    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中的對(duì)象
     */
    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);
    }
    /**
     * 獲取多個(gè)Hash中的數(shù)據(jù)
     *
     * @param key Redis鍵
     * @param hKeys Hash鍵集合
     * @return Hash對(duì)象集合
     */
    public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys)
    {
        return redisTemplate.opsForHash().multiGet(key, hKeys);
    }
    /**
     * 獲得緩存的基本對(duì)象列表
     *
     * @param pattern 字符串前綴
     * @return 對(duì)象列表
     */
    public Collection<String> keys(final String pattern)
    {
        return redisTemplate.keys(pattern);
    }
}

編寫(xiě)監(jiān)控類(lèi)

在代碼中繼承KeyspaceEventMessageListener ,實(shí)現(xiàn)onMessage就可以監(jiān)聽(tīng)過(guò)期的數(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("訂單{}過(guò)期了", expiredKey);
    }
}

測(cè)試

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è)置過(guò)期時(shí)間
        String key = "0001";
        RedisCache redisCache = new RedisCache();
        redisCache.setCacheObject(key,"訂單過(guò)期了");
        redisCache.expire(key,expire);
    }
}

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

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

4、Redisson分布式延遲隊(duì)列

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

引入pom依賴(lài),yml中配置redis連接

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

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

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

創(chuàng)建延時(shí)隊(duì)列消費(fèi)者

import org.redisson.api.RDelayedQueue;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 * 延遲隊(duì)列消費(fèi)者
 */
@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);
            }
        }
    }
}

測(cè)試

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

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

缺點(diǎn):需要依賴(lài)redis

5、RocketMQ延遲消息

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

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

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

引入依賴(lài)

 <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ā)送延遲消息,延遲級(jí)別為16,對(duì)應(yīng)延遲時(shí)間為delay
        rocketMQTemplate.syncSend(topic, message, delay, 16);
    }
}

創(chuàng)建消息消費(fèi)者

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);
         //檢查訂單是否支付
    }
}

測(cè)試

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ā)送訂單編號(hào)到rocketMQ
        long delay = 3000;
        producerService.sendMessage(topic, message, delay);
        return "消息發(fā)送成功";
    }
}

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

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

6、RabbitMQ死信隊(duì)列

       當(dāng)RabbitMQ中的一條正常信息,因?yàn)檫^(guò)了存活時(shí)間(ttl過(guò)期)、隊(duì)列長(zhǎng)度超限等原因無(wú)法被消費(fèi)時(shí),就會(huì)被當(dāng)成一條死信消息,投遞到死信隊(duì)列?;谶@樣的機(jī)制,我們可以給消息設(shè)置一個(gè)ttl ,等消息過(guò)期就會(huì)進(jìn)入死信隊(duì)列,我們?cè)傧M(fèi)死信隊(duì)列即可,這樣,就可以達(dá)到和RocketMQ一樣的效果。springboot集成rabbitMQ的步驟如下:

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

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

引入依賴(lài)

    <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 隊(duì)列和交換機(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);
    }
    // 死信隊(duì)列
    @Bean
    public Queue deadLetterQueue() {
        return new Queue(DEAD_LETTER_QUEUE);
    }
    // 綁定死信隊(duì)列和死信交換機(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);
    }
    // 正常隊(duì)列,設(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);
    }
    // 綁定正常隊(duì)列和正常交換機(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)建消息消費(fèi)者

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)
    }
}

測(cè)試

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"; //訂單編號(hào)
        orderMessageProducer.sendOrderMessage(message);
        return "訂單消息已發(fā)送,30分鐘后處理";
    }
}

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

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

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

相關(guān)文章

  • 解決mybatis映射mapper.xml文件不編譯的問(wèn)題

    解決mybatis映射mapper.xml文件不編譯的問(wèn)題

    這篇文章主要介紹了解決mybatis映射mapper.xml文件不編譯的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-06-06
  • mybatisplus解除分頁(yè)限制的實(shí)現(xiàn)

    mybatisplus解除分頁(yè)限制的實(shí)現(xiàn)

    這篇文章主要介紹了mybatisplus解除分頁(yè)限制的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • Mybatis源碼解析之mapper接口的代理模式詳解

    Mybatis源碼解析之mapper接口的代理模式詳解

    這篇文章主要介紹了Mybatis源碼解析之mapper接口的代理模式詳解,在mybatis中執(zhí)行sql時(shí)有兩種方式,一種是基于statementId,也就是直接調(diào)用SqlSession的方法,需要的朋友可以參考下
    2023-12-12
  • 快速校驗(yàn)實(shí)體類(lèi)時(shí),@Valid,@Validated,@NotNull注解無(wú)效的解決

    快速校驗(yàn)實(shí)體類(lèi)時(shí),@Valid,@Validated,@NotNull注解無(wú)效的解決

    這篇文章主要介紹了快速校驗(yàn)實(shí)體類(lèi)時(shí),@Valid,@Validated,@NotNull注解無(wú)效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • Java中的定時(shí)任務(wù)調(diào)度Quartz舉例詳解

    Java中的定時(shí)任務(wù)調(diào)度Quartz舉例詳解

    這篇文章主要介紹了Java中的定時(shí)任務(wù)調(diào)度Quartz的相關(guān)資料,Quartz是Java平臺(tái)上的一個(gè)強(qiáng)大且靈活的任務(wù)調(diào)度庫(kù),廣泛應(yīng)用于企業(yè)級(jí)應(yīng)用中,本文介紹了Quartz的基本概念、核心組件、使用步驟和示例,幫助你更好地利用Quartz進(jìn)行定時(shí)任務(wù)調(diào)度,需要的朋友可以參考下
    2024-12-12
  • java使用文件流實(shí)現(xiàn)查看下載次數(shù)

    java使用文件流實(shí)現(xiàn)查看下載次數(shù)

    這篇文章主要為大家詳細(xì)介紹了java使用文件流實(shí)現(xiàn)查看下載次數(shù),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • 如何運(yùn)行SpringBoot項(xiàng)目的方法

    如何運(yùn)行SpringBoot項(xiàng)目的方法

    這篇文章主要介紹了如何運(yùn)行SpringBoot項(xiàng)目的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • Java實(shí)現(xiàn)線程的暫停和恢復(fù)的示例詳解

    Java實(shí)現(xiàn)線程的暫停和恢復(fù)的示例詳解

    這幾天的項(xiàng)目中,客戶(hù)給了個(gè)需求,希望我可以開(kāi)啟一個(gè)任務(wù),想什么時(shí)候暫停就什么時(shí)候暫停,想什么時(shí)候開(kāi)始就什么時(shí)候開(kāi)始,所以本文小編給大家介紹了Java實(shí)現(xiàn)線程的暫停和恢復(fù)的示例,需要的朋友可以參考下
    2023-11-11
  • SpringBoot使用AOP記錄接口操作日志詳解

    SpringBoot使用AOP記錄接口操作日志詳解

    這篇文章主要為大家詳細(xì)介紹了SpringBoot使用AOP記錄接口操作日志,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 使用Java實(shí)現(xiàn)文件大小過(guò)濾功能(附源碼)

    使用Java實(shí)現(xiàn)文件大小過(guò)濾功能(附源碼)

    在實(shí)際開(kāi)發(fā)中,經(jīng)常需要對(duì)大量文件進(jìn)行批量處理,對(duì)于這些場(chǎng)景,開(kāi)發(fā)者往往需要根據(jù)文件的大小進(jìn)行篩選,本文就來(lái)利用Java實(shí)現(xiàn)文件大小過(guò)濾功能,有需要的可以了解下
    2025-06-06

最新評(píng)論

肇庆市| 于都县| 额尔古纳市| 肃南| 屯留县| 金平| 铜陵市| 辽源市| 青海省| 公安县| 海阳市| 偏关县| 大冶市| 平谷区| 常宁市| 承德市| 望奎县| 雅江县| 深泽县| 忻州市| 杭锦旗| 祁门县| 延津县| 宜宾县| 亚东县| 盈江县| 宁晋县| 大名县| 綦江县| 宁阳县| 蕲春县| 嘉定区| 镇原县| 北碚区| 招远市| 开阳县| 辰溪县| 江川县| 甘洛县| 剑阁县| 虞城县|