讲师中心 微信公众号
AI工具推荐 视频效率加速

说说延时队列实现的几种姿势

胖瑶姑娘_8602

胖瑶姑娘_8602

发布时间:2025-06-26 11:12:11

|

784人浏览过

|

来源于php中文网

原创

引言

在开发中,往往会遇到一些关于延时任务的需求。例如

生成订单30分钟未支付,则自动取消生成订单60秒后,给用户发短信

对上述的任务,我们给一个专业的名字来形容,那就是延时任务 。那么这里就会产生一个问题,这个延时任务 和定时任务 的区别究竟在哪里呢?一共有如下几点区别

定时任务有明确的触发时间,延时任务没有定时任务有执行周期,而延时任务在某事件触发后一段时间内执行,没有执行周期定时任务一般执行的是批处理操作是多个任务,而延时任务一般是单个任务

下面,我们以判断订单是否超时为例,进行方案分析

方案分析(1)数据库轮询思路

该方案通常是在小型项目中使用,即通过一个线程定时的去扫描数据库,通过订单时间来判断是否有超时的订单,然后进行update或delete等操作

实现

博主当年早期是用quartz来实现的(实习那会的事),简单介绍一下 maven项目引入一个依赖如下所示

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">    <dependency>    <groupId>org.quartz-scheduler</groupId>    <artifactId>quartz</artifactId>    <version>2.2.2</version></dependency></code>

调用Demo类MyJob如下所示

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">package com.rjzheng.delay1;import org.quartz.JobBuilder;import org.quartz.JobDetail;import org.quartz.Scheduler;import org.quartz.SchedulerException;import org.quartz.SchedulerFactory;import org.quartz.SimpleScheduleBuilder;import org.quartz.Trigger;import org.quartz.TriggerBuilder;import org.quartz.impl.StdSchedulerFactory;import org.quartz.Job;import org.quartz.JobExecutionContext;import org.quartz.JobExecutionException;public class MyJob implements Job {    public void execute(JobExecutionContext context)            throws JobExecutionException {        System.out.println("要去数据库扫描啦。。。");    }    public static void main(String[] args) throws Exception {        // 创建任务        JobDetail jobDetail = JobBuilder.newJob(MyJob.class)                .withIdentity("job1", "group1").build();        // 创建触发器 每3秒钟执行一次        Trigger trigger = TriggerBuilder                .newTrigger()                .withIdentity("trigger1", "group3")                .withSchedule(                        SimpleScheduleBuilder.simpleSchedule()                                .withIntervalInSeconds(3).repeatForever())                .build();        Scheduler scheduler = new StdSchedulerFactory().getScheduler();        // 将任务及其触发器放入调度器        scheduler.scheduleJob(jobDetail, trigger);        // 调度器开始调度任务        scheduler.start();    }}</code>

运行代码,可发现每隔3秒,输出如下

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">要去数据库扫描啦。。。</code>
优缺点

优点:简单易行,支持集群操作

缺点:

DubbingX TTS 配音技能
DubbingX TTS 配音技能

使用 DubbingX 智声云配 API 为角色台词生成 TTS 配音。支持音色查询筛选、情绪自动匹配、单条/批量台词异步生成与下载。当用户想要配音、TTS、文字转语音、角色配音、台词配音,或提到"dubbingx"、"配音"、"tts"、"语音合成"、"生成语音"、"角色声音"时,务必使用此 skill。

下载
(1)对服务器内存消耗大(2)存在延迟,比如你每隔3分钟扫描一次,那最坏的延迟时间就是3分钟(3)假设你的订单有几千万条,每隔几分钟这样扫描一次,数据库损耗极大(2)JDK的延迟队列思路

该方案是利用JDK自带的DelayQueue来实现,这是一个无界阻塞队列,该队列只有在延迟期满的时候才能从中获取元素,放入DelayQueue中的对象,是必须实现Delayed接口的。

DelayedQueue实现工作流程如下图所示

说说延时队列实现的几种姿势

其中

poll():获取并移除队列的超时元素,没有则返回空take():获取并移除队列的超时元素,如果没有则wait当前线程,直到有元素满足超时条件,返回结果。实现

定义一个类OrderDelay实现Delayed,代码如下

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">package com.rjzheng.delay2;import java.util.concurrent.Delayed;import java.util.concurrent.TimeUnit;public class OrderDelay implements Delayed { private String orderId; private long timeout; OrderDelay(String orderId, long timeout) {  this.orderId = orderId;  this.timeout = timeout + System.nanoTime(); } public int compareTo(Delayed other) {  if (other == this)   return 0;  OrderDelay t = (OrderDelay) other;  long d = (getDelay(TimeUnit.NANOSECONDS) - t    .getDelay(TimeUnit.NANOSECONDS));  return (d == 0) ? 0 : ((d < 0) ? -1 : 1); } // 返回距离你自定义的超时时间还有多少 public long getDelay(TimeUnit unit) {  return unit.convert(timeout - System.nanoTime(), TimeUnit.NANOSECONDS); } void print() {  System.out.println(orderId+"编号的订单要删除啦。。。。"); }}</code>

运行的测试Demo为,我们设定延迟时间为3秒

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">package com.rjzheng.delay2;import java.util.ArrayList;import java.util.List;import java.util.concurrent.DelayQueue;import java.util.concurrent.TimeUnit;public class DelayQueueDemo {  public static void main(String[] args) {         // TODO Auto-generated method stub         List<String> list = new ArrayList<String>();         list.add("00000001");         list.add("00000002");         list.add("00000003");         list.add("00000004");         list.add("00000005");         DelayQueue<OrderDelay> queue = new DelayQueue<OrderDelay>();         long start = System.currentTimeMillis();         for(int i = 0;i<5;i++){          //延迟三秒取出             queue.put(new OrderDelay(list.get(i),                     TimeUnit.NANOSECONDS.convert(3, TimeUnit.SECONDS)));                 try {                      queue.take().print();                      System.out.println("After " +                              (System.currentTimeMillis()-start) + " MilliSeconds");             } catch (InterruptedException e) {                 // TODO Auto-generated catch block                 e.printStackTrace();             }         }     }}</code>

输出如下

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">00000001编号的订单要删除啦。。。。After 3003 MilliSeconds00000002编号的订单要删除啦。。。。After 6006 MilliSeconds00000003编号的订单要删除啦。。。。After 9006 MilliSeconds00000004编号的订单要删除啦。。。。After 12008 MilliSeconds00000005编号的订单要删除啦。。。。After 15009 MilliSeconds</code>

可以看到都是延迟3秒,订单被删除

优缺点

优点:效率高,任务触发时间延迟低。缺点:(1)服务器重启后,数据全部消失,怕宕机 (2)集群扩展相当麻烦 (3)因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常 (4)代码复杂度较高

(3)时间轮算法思路

先上一张时间轮的图(这图到处都是啦)

说说延时队列实现的几种姿势

时间轮算法可以类比于时钟,如上图箭头(指针)按某一个方向按固定频率轮动,每一次跳动称为一个 tick。这样可以看出定时轮由个3个重要的属性参数,ticksPerWheel(一轮的tick数),tickDuration(一个tick的持续时间)以及 timeUnit(时间单位),例如当ticksPerWheel=60,tickDuration=1,timeUnit=秒,这就和现实中的始终的秒针走动完全类似了。

如果当前指针指在1上面,我有一个任务需要4秒以后执行,那么这个执行的线程回调或者消息将会被放在5上。那如果需要在20秒之后执行怎么办,由于这个环形结构槽数只到8,如果要20秒,指针需要多转2圈。位置是在2圈之后的5上面(20 % 8 + 1)

实现

我们用Netty的HashedWheelTimer来实现 给Pom加上下面的依赖

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">  <dependency>   <groupId>io.netty</groupId>   <artifactId>netty-all</artifactId>   <version>4.1.24.Final</version>  </dependency></code>

测试代码HashedWheelTimerTest如下所示

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">package com.rjzheng.delay3;import io.netty.util.HashedWheelTimer;import io.netty.util.Timeout;import io.netty.util.Timer;import io.netty.util.TimerTask;import java.util.concurrent.TimeUnit;public class HashedWheelTimerTest { static class MyTimerTask implements TimerTask{  boolean flag;  public MyTimerTask(boolean flag){   this.flag = flag;  }  public void run(Timeout timeout) throws Exception {   // TODO Auto-generated method stub    System.out.println("要去数据库删除订单了。。。。");             this.flag =false;  } } public static void main(String[] argv) {  MyTimerTask timerTask = new MyTimerTask(true);        Timer timer = new HashedWheelTimer();        timer.newTimeout(timerTask, 5, TimeUnit.SECONDS);     int i = 1;        while(timerTask.flag){         try {    Thread.sleep(1000);   } catch (InterruptedException e) {    // TODO Auto-generated catch block    e.printStackTrace();   }         System.out.println(i+"秒过去了");         i++;        }    }}</code>

输出如下

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">1秒过去了2秒过去了3秒过去了4秒过去了5秒过去了要去数据库删除订单了。。。。6秒过去了</code>
优缺点

优点:效率高,任务触发时间延迟时间比delayQueue低,代码复杂度比delayQueue低。

缺点:

(1)服务器重启后,数据全部消失,怕宕机(2)集群扩展相当麻烦(3)因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常(4)redis缓存思路一

利用redis的zset,zset是一个有序集合,每一个元素(member)都关联了一个score,通过score排序来取集合中的值

zset常用命令

添加元素:ZADD key score member [[score member] [score member] ...]按顺序查询元素:ZRANGE key start stop [WITHSCORES]查询元素score:ZSCORE key member移除元素:ZREM key member [member ...]

测试如下

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">> 推荐下自己做的 Spring Cloud 的实战项目:>> <https://github.com/YunaiV/onemall># 添加单个元素redis> ZADD page_rank 10 google.com(integer) 1# 添加多个元素redis> ZADD page_rank 9 baidu.com 8 bing.com(integer) 2redis> ZRANGE page_rank 0 -1 WITHSCORES1) "bing.com"2) "8"3) "baidu.com"4) "9"5) "google.com"6) "10"# 查询元素的score值redis> ZSCORE page_rank bing.com"8"# 移除单个元素redis> ZREM page_rank google.com(integer) 1redis> ZRANGE page_rank 0 -1 WITHSCORES1) "bing.com"2) "8"3) "baidu.com"4) "9"</code>

那么如何实现呢?我们将订单超时时间戳与订单号分别设置为score和member,系统扫描第一个元素判断是否超时,具体如下图所示

说说延时队列实现的几种姿势
实现一代码语言:javascript代码运行次数:0运行复制
<code class="javascript">package com.rjzheng.delay4;import java.util.Calendar;import java.util.Set;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.Tuple;public class AppTest { private static final String ADDR = "127.0.0.1"; private static final int PORT = 6379; private static JedisPool jedisPool = new JedisPool(ADDR, PORT); public static Jedis getJedis() {       return jedisPool.getResource();    } //生产者,生成5个订单放进去 public void productionDelayMessage(){  for(int i=0;i<5;i++){   //延迟3秒   Calendar cal1 = Calendar.getInstance();         cal1.add(Calendar.SECOND, 3);         int second3later = (int) (cal1.getTimeInMillis() / 1000);         AppTest.getJedis().zadd("OrderId", second3later,"OID0000001"+i);   System.out.println(System.currentTimeMillis()+"ms:redis生成了一个订单任务:订单ID为"+"OID0000001"+i);  } } //消费者,取订单 public void consumerDelayMessage(){  Jedis jedis = AppTest.getJedis();  while(true){   Set<Tuple> items = jedis.zrangeWithScores("OrderId", 0, 1);   if(items == null || items.isEmpty()){    System.out.println("当前没有等待的任务");    try {     Thread.sleep(500);    } catch (InterruptedException e) {     // TODO Auto-generated catch block     e.printStackTrace();    }    continue;   }   int  score = (int) ((Tuple)items.toArray()[0]).getScore();   Calendar cal = Calendar.getInstance();   int nowSecond = (int) (cal.getTimeInMillis() / 1000);   if(nowSecond >= score){    String orderId = ((Tuple)items.toArray()[0]).getElement();    jedis.zrem("OrderId", orderId);    System.out.println(System.currentTimeMillis() +"ms:redis消费了一个任务:消费的订单OrderId为"+orderId);   }  } } public static void main(String[] args) {  AppTest appTest =new AppTest();  appTest.productionDelayMessage();  appTest.consumerDelayMessage(); }}</code>

此时对应输出如下

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">1525086085261ms:redis生成了一个订单任务:订单ID为OID000000101525086085263ms:redis生成了一个订单任务:订单ID为OID000000111525086085266ms:redis生成了一个订单任务:订单ID为OID000000121525086085268ms:redis生成了一个订单任务:订单ID为OID000000131525086085270ms:redis生成了一个订单任务:订单ID为OID000000141525086088000ms:redis消费了一个任务:消费的订单OrderId为OID000000101525086088001ms:redis消费了一个任务:消费的订单OrderId为OID000000111525086088002ms:redis消费了一个任务:消费的订单OrderId为OID000000121525086088003ms:redis消费了一个任务:消费的订单OrderId为OID000000131525086088004ms:redis消费了一个任务:消费的订单OrderId为OID00000014当前没有等待的任务当前没有等待的任务当前没有等待的任务</code>

可以看到,几乎都是3秒之后,消费订单。

然而,这一版存在一个致命的硬伤,在高并发条件下,多消费者会取到同一个订单号,我们上测试代码ThreadTest

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">package com.rjzheng.delay4;import java.util.concurrent.CountDownLatch;public class ThreadTest { private static final int threadNum = 10; private static CountDownLatch cdl = new CountDownLatch(threadNum); static class DelayMessage implements Runnable{  public void run() {   try {    cdl.await();   } catch (InterruptedException e) {    // TODO Auto-generated catch block    e.printStackTrace();   }   AppTest appTest =new AppTest();   appTest.consumerDelayMessage();  } } public static void main(String[] args) {  AppTest appTest =new AppTest();  appTest.productionDelayMessage();  for(int i=0;i<threadNum;i++){   new Thread(new DelayMessage()).start();   cdl.countDown();  } }}</code>

输出如下所示

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">1525087157727ms:redis生成了一个订单任务:订单ID为OID000000101525087157734ms:redis生成了一个订单任务:订单ID为OID000000111525087157738ms:redis生成了一个订单任务:订单ID为OID000000121525087157747ms:redis生成了一个订单任务:订单ID为OID000000131525087157753ms:redis生成了一个订单任务:订单ID为OID000000141525087160009ms:redis消费了一个任务:消费的订单OrderId为OID000000101525087160011ms:redis消费了一个任务:消费的订单OrderId为OID000000101525087160012ms:redis消费了一个任务:消费的订单OrderId为OID000000101525087160022ms:redis消费了一个任务:消费的订单OrderId为OID000000111525087160023ms:redis消费了一个任务:消费的订单OrderId为OID000000111525087160029ms:redis消费了一个任务:消费的订单OrderId为OID000000111525087160038ms:redis消费了一个任务:消费的订单OrderId为OID000000121525087160045ms:redis消费了一个任务:消费的订单OrderId为OID000000121525087160048ms:redis消费了一个任务:消费的订单OrderId为OID000000121525087160053ms:redis消费了一个任务:消费的订单OrderId为OID000000131525087160064ms:redis消费了一个任务:消费的订单OrderId为OID000000131525087160065ms:redis消费了一个任务:消费的订单OrderId为OID000000141525087160069ms:redis消费了一个任务:消费的订单OrderId为OID00000014当前没有等待的任务当前没有等待的任务当前没有等待的任务当前没有等待的任务</code>

显然,出现了多个线程消费同一个资源的情况。

解决方案(1)用分布式锁,但是用分布式锁,性能下降了,该方案不细说。(2)对ZREM的返回值进行判断,只有大于0的时候,才消费数据,于是将consumerDelayMessage()方法里的代码语言:javascript代码运行次数:0运行复制
<code class="javascript">if(nowSecond >= score){ String orderId = ((Tuple)items.toArray()[0]).getElement(); jedis.zrem("OrderId", orderId); System.out.println(System.currentTimeMillis()+"ms:redis消费了一个任务:消费的订单OrderId为"+orderId);}</code>

修改为

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">if(nowSecond >= score){ String orderId = ((Tuple)items.toArray()[0]).getElement(); Long num = jedis.zrem("OrderId", orderId); if( num != null && num>0){  System.out.println(System.currentTimeMillis()+"ms:redis消费了一个任务:消费的订单OrderId为"+orderId); }}</code>

在这种修改后,重新运行ThreadTest类,发现输出正常了

思路二

该方案使用redis的Keyspace Notifications,中文翻译就是键空间机制,就是利用该机制可以在key失效之后,提供一个回调,实际上是redis会给客户端发送一个消息。是需要redis版本2.8以上。

实现二

在redis.conf中,加入一条配置

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">notify-keyspace-events Ex</code>

运行代码如下

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">package com.rjzheng.delay5;import redis.clients.jedis.Jedis;import redis.clients.jedis.JedisPool;import redis.clients.jedis.JedisPubSub;public class RedisTest { private static final String ADDR = "127.0.0.1"; private static final int PORT = 6379; private static JedisPool jedis = new JedisPool(ADDR, PORT); private static RedisSub sub = new RedisSub(); public static void init() {  new Thread(new Runnable() {   public void run() {    jedis.getResource().subscribe(sub, "__keyevent@0__:expired");   }  }).start(); } public static void main(String[] args) throws InterruptedException {  init();  for(int i =0;i<10;i++){   String orderId = "OID000000"+i;   jedis.getResource().setex(orderId, 3, orderId);   System.out.println(System.currentTimeMillis()+"ms:"+orderId+"订单生成");  } } static class RedisSub extends JedisPubSub {  @Override  public void onMessage(String channel, String message) {   System.out.println(System.currentTimeMillis()+"ms:"+message+"订单取消");  } }}</code>

输出如下

代码语言:javascript代码运行次数:0运行复制
<code class="javascript">1525096202813ms:OID0000000订单生成1525096202818ms:OID0000001订单生成1525096202824ms:OID0000002订单生成1525096202826ms:OID0000003订单生成1525096202830ms:OID0000004订单生成1525096202834ms:OID0000005订单生成1525096202839ms:OID0000006订单生成1525096205819ms:OID0000000订单取消1525096205920ms:OID0000005订单取消1525096205920ms:OID0000004订单取消1525096205920ms:OID0000001订单取消1525096205920ms:OID0000003订单取消1525096205920ms:OID0000006订单取消1525096205920ms:OID0000002订单取消</code>

可以明显看到3秒过后,订单取消了

ps:redis的pub/sub 机制存在一个硬伤,官网内容如下

原 :Because Redis Pub/Sub is fire and forget currently there is no way to use this feature if your application demands reliable notification of events, that is, if your Pub/Sub client disconnects, and reconnects later, all the events delivered during the time the client was disconnected are lost.

翻 : Redis的发布/订阅目前是即发即弃(fire and forget)模式的,因此无法实现事件的可靠通知。也就是说,如果发布/订阅的客户端断链之后又重连,则在客户端断链期间的所有事件都丢失了。因此,方案二不是太推荐。当然,如果你对可靠性要求不高,可以使用。

优缺点

优点:

(1)由于使用Redis作为消息通道,消息都存储在Redis中。如果发送程序或者任务处理程序挂了,重启之后,还有重新处理数据的可能性。(2)做集群扩展相当方便(3)时间准确度高

缺点:(1)需要额外进行redis维护

(5)使用消息队列

我们可以采用rabbitMQ的延时队列。RabbitMQ具有以下两个特性,可以实现延迟队列

RabbitMQ可以针对Queue和Message设置 x-message-tt,来控制消息的生存时间,如果超时,则消息变为dead letterlRabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,用来控制队列内出现了deadletter,则按照这两个参数重新路由。结合以上两个特性,就可以模拟出延迟消息的功能,具体的,我改天再写一篇文章,这里再讲下去,篇幅太长。优缺点

优点: 高效,可以利用rabbitmq的分布式特性轻易的进行横向扩展,消息支持持久化增加了可靠性。缺点:本身的易用度要依赖于rabbitMq的运维.因为要引用rabbitMq,所以复杂度和成本变高

总结

本文总结了目前互联网中,绝大部分的延时任务的实现方案。希望大家在工作中能够有所收获。

其实大家在工作中,百分九十的人还是以业务逻辑为主,很少有机会能够进行方案设计。所以博主不推荐在分布式这块,花太多时间。不过,鉴于现在的面试造火箭,工作拧螺丝现象太过严重,所以,最后来个小漫画娱乐一下。

说说延时队列实现的几种姿势

热门AI工具

更多
切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

Laper
Laper Hot

Laper是专为编剧、导演和制片人推出的 AI 原生剧本创作工具。

AionClaw
AionClaw Hot

AionClaw是一款面向办公、创作和编程任务的AI桌面智能体。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

LibLibAI
LibLibAI Hot

一款AI视频创作工具,主要用于国内领先的AI创意平台,以海量模型、低门槛操作与“创作-分享-商业化”生态,让小白与专业创作者都能高效实现图文乃至视频创意表达,适合需要提升相关任务效率的用户。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

相关专题

更多
rabbitmq和kafka有什么区别
rabbitmq和kafka有什么区别

rabbitmq和kafka的区别:1、语言与平台;2、消息传递模型;3、可靠性;4、性能与吞吐量;5、集群与负载均衡;6、消费模型;7、用途与场景;8、社区与生态系统;9、监控与管理;10、其他特性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

504

2024.02.23

Java 消息队列与异步架构实战
Java 消息队列与异步架构实战

本专题系统讲解 Java 在消息队列与异步系统架构中的核心应用,涵盖消息队列基本原理、Kafka 与 RabbitMQ 的使用场景对比、生产者与消费者模型、消息可靠性与顺序性保障、重复消费与幂等处理,以及在高并发系统中的异步解耦设计。通过实战案例,帮助学习者掌握 使用 Java 构建高吞吐、高可靠异步消息系统的完整思路。

648

2026.01.28

RabbitMQ使用教程合集
RabbitMQ使用教程合集

RabbitMQ使用教程合集整理 RabbitMQ 基础教程、消息队列开发案例、生产者消费者、交换机与队列实战内容。

144

2026.05.20

RabbitMQ集群部署指南
RabbitMQ集群部署指南

RabbitMQ集群部署指南聚合 RabbitMQ 集群部署、高可用架构、镜像队列、故障恢复、监控与运维优化内容。

97

2026.05.20

RabbitMQ Docker实战指南
RabbitMQ Docker实战指南

RabbitMQ Docker实战指南提供 RabbitMQ Docker 镜像部署、Docker Compose、Kubernetes Operator 与云原生实践教程。

122

2026.05.20

什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

1873

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

2454

2023.10.07

Java Maven专题
Java Maven专题

本专题聚焦 Java 主流构建工具 Maven 的学习与应用,系统讲解项目结构、依赖管理、插件使用、生命周期与多模块项目配置。通过企业管理系统、Web 应用与微服务项目实战,帮助学员全面掌握 Maven 在 Java 项目构建与团队协作中的核心技能。

2780

2025.09.15

Vibeknow在线使用入口合集
Vibeknow在线使用入口合集

本专题汇总了Vibeknow在线创作视频的官方入口及网页版使用教程,涵盖PPT、PDF、Word等文档一键转讲解视频的核心操作,并整理了免费版水印规则与手机端浏览器访问指南,助你快速将知识内容视频化。

0

2026.09.21

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Valgrind支持平台说明
Valgrind支持平台说明

共0课时 | 0人学习

CLion 远程开发实战
CLion 远程开发实战

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn