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

SpringBoot文章定時發(fā)布的技術(shù)方案詳解(三種延遲任務(wù)方案)

 更新時間:2025年12月17日 10:00:27   作者:IT 劉工  
本文介紹了三種實現(xiàn)文章定時發(fā)布在SpringBoot項目中的技術(shù)方案:JDKDelayQueue、基于RabbitMQ延遲隊列以及基于Redis有序集合,每種方案都有其特點和適用場景,感興趣的朋友跟隨小編一起看看吧

在現(xiàn)代內(nèi)容管理系統(tǒng)中,文章定時發(fā)布是一個常見需求。

它允許作者在特定時間自動發(fā)布文章,而不需要手動操作。

在SpringBoot項目中,實現(xiàn)這種定時功能有多種技術(shù)方案,每種都有其適用場景和特點。

本文將詳細(xì)介紹三種實現(xiàn)文章定時發(fā)布的技術(shù)方案:

基于JDK DelayQueue、基于RabbitMQ延遲隊列,以及基于Redis有序集合。

我會為每種方案提供核心代碼實現(xiàn),并深入分析它們的優(yōu)劣,幫助你在實際項目中做出合適的技術(shù)選型。

方案一:JDK DelayQueue 實現(xiàn)

實現(xiàn)原理

DelayQueue是JDK提供的一個無界阻塞隊列,其中的元素只有在指定的延遲時間到達(dá)后才能被取出。這個特性使其天然適合實現(xiàn)延遲任務(wù)。

核心代碼實現(xiàn)

1. 定義延遲任務(wù)對象

import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
public class ArticleTask implements Delayed {
    private final long executeTime; // 執(zhí)行時間戳
    private final Article article; // 文章內(nèi)容
    public ArticleTask(Article article, long delay) {
        this.article = article;
        this.executeTime = System.currentTimeMillis() + delay;
    }
    @Override
    public long getDelay(TimeUnit unit) {
        return unit.convert(executeTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
    }
    @Override
    public int compareTo(Delayed o) {
        return Long.compare(this.executeTime, ((ArticleTask) o).executeTime);
    }
    // Getter方法
    public Article getArticle() {
        return article;
    }
}

2. 文章實體類

public class Article {
    private Long id;
    private String title;
    private String content;
    private Integer status; // 狀態(tài):0-草稿,1-已發(fā)布
    private Date publishTime; // 預(yù)定發(fā)布時間
    // 省略getter/setter
}

3. 延遲隊列服務(wù)

@Component
public class DelayQueueService {
    private final DelayQueue<ArticleTask> queue = new DelayQueue<>();
    @PostConstruct
    public void init() {
        // 啟動消費線程
        new Thread(this::consume).start();
    }
    /**
     * 添加定時發(fā)布任務(wù)
     * @param article 文章
     * @param delay 延遲時間(毫秒)
     */
    public void addTask(Article article, long delay) {
        queue.put(new ArticleTask(article, delay));
    }
    /**
     * 消費延遲隊列中的任務(wù)
     */
    private void consume() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                ArticleTask task = queue.take(); // 阻塞直到有任務(wù)到期
                publishArticle(task.getArticle());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }
    /**
     * 發(fā)布文章
     */
    private void publishArticle(Article article) {
        // 更新文章狀態(tài)為已發(fā)布
        article.setStatus(1);
        article.setPublishTime(new Date());
        System.out.println("發(fā)布時間:" + new Date() + ",發(fā)布文章: " + article.getTitle());
        // 這里可以加入實際的數(shù)據(jù)庫更新邏輯
        // articleService.updateById(article);
    }
}

4. 控制器示例

@RestController
@RequestMapping("/article")
public class ArticleController {
    @Autowired
    private DelayQueueService delayQueueService;
    @PostMapping("/schedule")
    public String schedulePublish(@RequestBody Article article, 
                                 @RequestParam long delayMillis) {
        delayQueueService.addTask(article, delayMillis);
        return "文章已安排定時發(fā)布";
    }
}

優(yōu)劣分析

優(yōu)點:

  • 實現(xiàn)簡單,不依賴外部組件
  • 延遲精度高,任務(wù)到期立即執(zhí)行
  • 性能較好,純內(nèi)存操作

缺點:

  • 任務(wù)存儲在內(nèi)存,應(yīng)用重啟會丟失
  • 不支持分布式集群部署
  • 任務(wù)過多時容易導(dǎo)致內(nèi)存溢出
  • 缺乏持久化機(jī)制和重試機(jī)制

方案二:RabbitMQ 延遲隊列

實現(xiàn)原理

RabbitMQ可以通過死信隊列(Dead Letter Exchange)實現(xiàn)延遲任務(wù)。當(dāng)消息在隊列中存活時間超過設(shè)定的TTL(Time To Live)后,會被轉(zhuǎn)發(fā)到死信交換機(jī),進(jìn)而路由到死信隊列供消費者處理。

核心代碼實現(xiàn)

1. RabbitMQ配置類

@Configuration
public class RabbitMQConfig {
    // 定義死信交換機(jī)和隊列
    @Bean
    public DirectExchange deadLetterExchange() {
        return new DirectExchange("dead.letter.exchange");
    }
    @Bean
    public Queue deadLetterQueue() {
        return QueueBuilder.durable("dead.letter.queue").build();
    }
    @Bean
    public Binding deadLetterBinding() {
        return BindingBuilder.bind(deadLetterQueue())
                .to(deadLetterExchange())
                .with("dead.letter");
    }
    // 定義實際業(yè)務(wù)隊列,并綁定死信交換機(jī)
    @Bean
    public Queue articleQueue() {
        return QueueBuilder.durable("article.delay.queue")
                .withArgument("x-dead-letter-exchange", "dead.letter.exchange")
                .withArgument("x-dead-letter-routing-key", "dead.letter")
                .build();
    }
}

2. 消息發(fā)送服務(wù)

@Service
public class ArticleMQService {
    @Autowired
    private RabbitTemplate rabbitTemplate;
    /**
     * 安排文章定時發(fā)布
     * @param article 文章
     * @param delayMillis 延遲時間(毫秒)
     */
    public void scheduleArticle(Article article, long delayMillis) {
        rabbitTemplate.convertAndSend("", "article.delay.queue", article, message -> {
            // 設(shè)置消息的過期時間
            message.getMessageProperties().setExpiration(String.valueOf(delayMillis));
            return message;
        });
        System.out.println("消息已發(fā)送,將在 " + delayMillis + " 毫秒后過期");
    }
}

3. 消息消費者

@Component
public class ArticleDelayListener {
    @RabbitListener(queues = "dead.letter.queue")
    public void processDelayedArticle(Article article) {
        // 此時文章已到預(yù)定發(fā)布時間,執(zhí)行發(fā)布邏輯
        article.setStatus(1);
        article.setPublishTime(new Date());
        System.out.println("發(fā)布時間:" + new Date() + ",發(fā)布文章: " + article.getTitle());
        // 實際業(yè)務(wù)中,這里應(yīng)該更新數(shù)據(jù)庫
        // articleService.updateById(article);
    }
}

4. 控制器示例

@RestController
@RequestMapping("/article")
public class ArticleController {
    @Autowired
    private ArticleMQService articleMQService;
    @PostMapping("/schedule")
    public String schedulePublish(@RequestBody Article article, 
                                 @RequestParam long delayMillis) {
        articleMQService.scheduleArticle(article, delayMillis);
        return "文章已安排定時發(fā)布";
    }
}

優(yōu)劣分析

優(yōu)點:

  • 任務(wù)持久化,服務(wù)重啟不會丟失
  • 支持分布式集群部署
  • 提供完善的ACK機(jī)制和重試機(jī)制
  • 具備良好的可靠性和可用性

缺點:

  • 需要額外維護(hù)RabbitMQ中間件
  • 基于死信隊列的方式存在"隊頭阻塞"問題
  • 配置相對復(fù)雜
  • TTL設(shè)置在消息級別時,后到期的消息可能阻塞先到期的消息

提示:RabbitMQ 3.8+版本提供了官方的延遲消息插件(rabbitmq_delayed_message_exchange),可以避免死信隊列的隊頭阻塞問題。

方案三:Redis 延遲任務(wù)

實現(xiàn)原理

Redis可以通過有序集合(ZSet)實現(xiàn)延遲任務(wù)。將任務(wù)執(zhí)行時間作為score,定期掃描已到期的任務(wù)進(jìn)行處理。

核心代碼實現(xiàn)

1. Redis服務(wù)類

@Service
public class RedisDelayService {
    @Autowired
    private StringRedisTemplate redisTemplate;
    private static final String DELAY_KEY = "article:delay:tasks";
    // 使用Jackson進(jìn)行序列化
    private final ObjectMapper objectMapper = new ObjectMapper();
    /**
     * 添加延遲任務(wù)
     */
    public void addArticleTask(Article article, long delaySeconds) {
        long executeTime = System.currentTimeMillis() + (delaySeconds * 1000);
        try {
            String articleJson = objectMapper.writeValueAsString(article);
            redisTemplate.opsForZSet().add(DELAY_KEY, articleJson, executeTime);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("文章序列化失敗", e);
        }
    }
    /**
     * 獲取到期的任務(wù)
     */
    public Set<String> getExpiredTasks(long maxScore) {
        return redisTemplate.opsForZSet().rangeByScore(DELAY_KEY, 0, maxScore);
    }
    /**
     * 移除已處理的任務(wù)
     */
    public void removeTask(String task) {
        redisTemplate.opsForZSet().remove(DELAY_KEY, task);
    }
}

2. 任務(wù)掃描器

@Component
public class RedisTaskScanner {
    @Autowired
    private RedisDelayService redisDelayService;
    private static final String LOCK_KEY = "article:delay:lock";
    private static final long LOCK_EXPIRE = 30; // 鎖過期時間30秒
    /**
     * 每秒掃描一次到期任務(wù)
     */
    @Scheduled(fixedRate = 1000)
    public void scanExpiredTasks() {
        // 使用Redis分布式鎖,防止集群環(huán)境下重復(fù)消費
        boolean locked = tryGetDistributedLock();
        if (!locked) {
            return;
        }
        try {
            long maxScore = System.currentTimeMillis();
            Set<String> expiredTasks = redisDelayService.getExpiredTasks(maxScore);
            if (expiredTasks != null && !expiredTasks.isEmpty()) {
                for (String taskJson : expiredTasks) {
                    processTask(taskJson);
                    // 從集合中移除已處理的任務(wù)
                    redisDelayService.removeTask(taskJson);
                }
            }
        } finally {
            // 釋放鎖
            releaseDistributedLock();
        }
    }
    private void processTask(String taskJson) {
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            Article article = objectMapper.readValue(taskJson, Article.class);
            // 執(zhí)行發(fā)布邏輯
            article.setStatus(1);
            article.setPublishTime(new Date());
            System.out.println("發(fā)布時間:" + new Date() + ",發(fā)布文章: " + article.getTitle());
            // 實際業(yè)務(wù)中更新數(shù)據(jù)庫
            // articleService.updateById(article);
        } catch (Exception e) {
            System.err.println("處理任務(wù)失敗: " + e.getMessage());
            // 可以加入重試機(jī)制或死信隊列
        }
    }
    private boolean tryGetDistributedLock() {
        // 簡化的分布式鎖實現(xiàn),生產(chǎn)環(huán)境建議使用Redisson等成熟方案
        // 這里只是示意,實際實現(xiàn)需要考慮原子性等問題
        return true;
    }
    private void releaseDistributedLock() {
        // 釋放鎖的實現(xiàn)
    }
}

3. 控制器示例

@RestController
@RequestMapping("/article")
public class ArticleController {
    @Autowired
    private RedisDelayService redisDelayService;
    @PostMapping("/schedule")
    public String schedulePublish(@RequestBody Article article, 
                                 @RequestParam long delaySeconds) {
        redisDelayService.addArticleTask(article, delaySeconds);
        return "文章已安排定時發(fā)布";
    }
}

4. 啟動類配置

@SpringBootApplication
@EnableScheduling // 開啟定時任務(wù)支持
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

優(yōu)劣分析

優(yōu)點:

  • 性能優(yōu)秀,支持高并發(fā)場景
  • 支持分布式集群部署
  • 數(shù)據(jù)可持久化,重啟不會丟失
  • 靈活性高,可以方便地調(diào)整掃描頻率

缺點:

  • 存在時間誤差,取決于輪詢間隔
  • 需要自行處理并發(fā)消費問題
  • CPU資源消耗相對較高
  • 需要維護(hù)Redis中間件

方案對比與選型建議

綜合對比

特性維度

JDK DelayQueue

RabbitMQ

Redis

準(zhǔn)時性

高 ?????

高 ?????

中高 ????

可靠性

低 ??

高 ?????

中高 ????

集群支持

不支持 ?

原生支持 ?????

原生支持 ?????

實現(xiàn)復(fù)雜度

低 ?????

中 ???

中 ???

資源開銷

內(nèi)存消耗大

中間件維護(hù)

CPU/網(wǎng)絡(luò)開銷

持久化

不支持

支持

支持

擴(kuò)展性

選型建議

1. 單體輕量級應(yīng)用
如果您的應(yīng)用是單體架構(gòu),任務(wù)量不大,且可以接受應(yīng)用重啟時任務(wù)丟失,推薦使用 JDK DelayQueue。它的實現(xiàn)最簡單,不依賴外部組件。

2. 分布式高可靠場景
如果您的應(yīng)用是微服務(wù)架構(gòu),需要高可靠性,并且已經(jīng)使用了RabbitMQ,推薦使用 RabbitMQ 延遲隊列。特別是對于電商、金融等對可靠性要求高的場景。

3. 高性能已有Redis
如果您的項目已經(jīng)使用Redis,且追求高性能和高靈活性,推薦使用 Redis 有序集合方案。特別適合任務(wù)量大、并發(fā)高的場景。

4. 簡單穩(wěn)定的備選方案
除了上述三種方案,對于文章定時發(fā)布這種允許少量延遲的場景,還可以考慮使用 Spring Schedule + 數(shù)據(jù)庫查詢的方案:

@Component
public class DatabaseScheduler {
    @Autowired
    private ArticleService articleService;
    // 每分鐘執(zhí)行一次
    @Scheduled(fixedRate = 60000)
    public void publishScheduledArticles() {
        List<Article> articles = articleService.findScheduledArticles(new Date());
        for (Article article : articles) {
            article.setStatus(1);
            articleService.update(article);
            System.out.println("發(fā)布文章: " + article.getTitle());
        }
    }
}

這種方案的優(yōu)點是實現(xiàn)簡單、穩(wěn)定可靠,缺點是實時性較差且有數(shù)據(jù)庫壓力。

生產(chǎn)環(huán)境注意事項

  1. 監(jiān)控與告警:無論選擇哪種方案,都需要建立完善的監(jiān)控體系,確保延遲任務(wù)正常執(zhí)行
  2. 任務(wù)去重:在分布式環(huán)境下要確保任務(wù)不會被重復(fù)消費
  3. 失敗重試:實現(xiàn)合理的重試機(jī)制,處理任務(wù)執(zhí)行失敗的情況
  4. 數(shù)據(jù)備份:定期備份任務(wù)數(shù)據(jù),防止數(shù)據(jù)丟失
  5. 性能測試:在生產(chǎn)環(huán)境上線前進(jìn)行充分的壓力測試

總結(jié)

文章定時發(fā)布功能雖然看似簡單,但在技術(shù)選型時需要綜合考慮業(yè)務(wù)需求、系統(tǒng)架構(gòu)和運維成本。JDK DelayQueue適合簡單場景,RabbitMQ適合高可靠性要求,Redis則在性能和靈活性上表現(xiàn)優(yōu)異。希望本文的分析和代碼示例能夠幫助你在實際項目中做出合適的技術(shù)決策。

選擇合適的技術(shù)方案,既要滿足當(dāng)前需求,也要為未來的擴(kuò)展留出空間。在實際項目中,建議根據(jù)具體的業(yè)務(wù)規(guī)模、團(tuán)隊技術(shù)棧和運維能力來做出最終決定。

到此這篇關(guān)于SpringBoot文章定時發(fā)布的技術(shù)方案詳解(三種延遲任務(wù)方案)的文章就介紹到這了,更多相關(guān)springboot 定時發(fā)布內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java正確比較浮點數(shù)的方法

    Java正確比較浮點數(shù)的方法

    這篇文章主要介紹了Java正確比較浮點數(shù)的方法,幫助大家更好的利用Java比較浮點數(shù)數(shù)據(jù),感興趣的朋友可以了解下
    2020-11-11
  • Java老手該當(dāng)心的13個錯誤

    Java老手該當(dāng)心的13個錯誤

    這篇文章主要介紹了Java老手該當(dāng)心的13個錯誤,需要的朋友可以參考下
    2015-04-04
  • SpringBoot?整合?Quartz?定時任務(wù)框架詳解

    SpringBoot?整合?Quartz?定時任務(wù)框架詳解

    這篇文章主要介紹了SpringBoot整合Quartz定時任務(wù)框架詳解,Quartz是一個完全由Java編寫的開源作業(yè)調(diào)度框架,為在Java應(yīng)用程序中進(jìn)行作業(yè)調(diào)度提供了簡單卻強(qiáng)大的機(jī)制
    2022-08-08
  • 一文讓你徹底學(xué)會Java左移、右移操作

    一文讓你徹底學(xué)會Java左移、右移操作

    這篇文章主要介紹了了Java中的位運算符,包括左移(<<)、右移(>>)和無符號右移(>>>),左移將數(shù)字的二進(jìn)制位向左移動,相當(dāng)于乘以2的n次方;右移將數(shù)字的二進(jìn)制位向右移動,保留符號位,相當(dāng)于除以2的n次方,文中介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02
  • Java中ConcurrentHashMap是如何實現(xiàn)線程安全

    Java中ConcurrentHashMap是如何實現(xiàn)線程安全

    ConcurrentHashMap是一個哈希表,支持檢索的全并發(fā)和更新的高預(yù)期并發(fā)。本文主要介紹了Java中ConcurrentHashMap是如何實現(xiàn)線程安全,感興趣的可以了解一下
    2021-11-11
  • 排查Java應(yīng)用內(nèi)存泄漏問題的步驟

    排查Java應(yīng)用內(nèi)存泄漏問題的步驟

    這篇文章主要介紹了排查Java應(yīng)用內(nèi)存泄漏問題的步驟,幫助大家更好的理解和學(xué)習(xí)Java,感興趣的朋友可以了解下
    2020-11-11
  • maven引入第三方j(luò)ar包配置詳解

    maven引入第三方j(luò)ar包配置詳解

    這篇文章主要為大家介紹了maven引入第三方j(luò)ar包配置詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-09-09
  • Java實現(xiàn)調(diào)用外部程序的示例代碼

    Java實現(xiàn)調(diào)用外部程序的示例代碼

    本文主要介紹了Java實現(xiàn)調(diào)用外部程序的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • Java 其中翻轉(zhuǎn)字符串的實現(xiàn)方法

    Java 其中翻轉(zhuǎn)字符串的實現(xiàn)方法

    這篇文章主要介紹了Java 其中翻轉(zhuǎn)字符串的實現(xiàn)方法,需要的朋友可以參考下
    2014-02-02
  • Java如何對返回參數(shù)進(jìn)行處理

    Java如何對返回參數(shù)進(jìn)行處理

    這篇文章主要介紹了Java如何對返回參數(shù)進(jìn)行處理問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-07-07

最新評論

扎鲁特旗| 玉溪市| 太仆寺旗| 丽江市| 西充县| 西充县| 资溪县| 蕲春县| 兖州市| 焦作市| 怀仁县| 三都| 射阳县| 黑龙江省| 兴义市| 徐汇区| 迁西县| 昌都县| 虎林市| 台湾省| 旌德县| 什邡市| 承德县| 孙吴县| 建瓯市| 防城港市| 罗平县| 云林县| 津南区| 崇仁县| 德清县| 临猗县| 古丈县| 平潭县| 板桥市| 广德县| 山西省| 绥芬河市| 芒康县| 怀远县| 屯留县|