webflux的delay原理详解

反应式编程一开始是从前端和客户端开始兴起,现在大有蔓延到后端的趋势,Spring5推出的webflux就是反应式编程的产物。

webflux对比于springMVC,性能高出很多,网上已经有很多的测评,不再在过多说明。

左图同步,右图异步

上图看出对比于同步,异步所用的线程是比较少的,不过有个前提是,程序逻辑中有阻塞(如io阻塞等),且这种阻塞是可以异步化的。

为了满足这个前提,反应式编程框架就必须将这些阻塞变成异步化,如新出的WebClient工具就是将http请求io异步化。

delay方法就是用来代替sleep方法的,下面来讲解一下delay方法是怎么将延时异步化的。

源码解读

  • 通过查看Mono<Long> delay(Duration duration)方法源码,它会构造一个MonoDelay类,并通过传入全局公用的调度器Schedulers.parallel()来调度里面的异步任务。
    public static Mono<Long> delay(Duration duration) {
        return delay(duration, Schedulers.parallel());
    }

    public static Mono<Long> delay(Duration duration, Scheduler timer) {
        return onAssembly(new MonoDelay(duration.toMillis(), TimeUnit.MILLISECONDS, timer));
    }
  • 查看MonoDelay类的订阅方法subscribe
public void subscribe(CoreSubscriber<? super Long> actual) {
    MonoDelayRunnable r = new MonoDelayRunnable(actual);

    actual.onSubscribe(r);

    try {
    //重点在于下面的 timedScheduler.schedule(r, delay, unit)
    //通过timedScheduler来调度延时任务,而不是当前线程阻塞等待
        r.setCancel(timedScheduler.schedule(r, delay, unit));
    }
    catch (RejectedExecutionException ree) {
        if(r.cancel != OperatorDisposables.DISPOSED) {
            actual.onError(Operators.onRejectedExecution(ree, r, null, null,
                    actual.currentContext()));
        }
    }
}
  • 查看ParallelScheduler的delay方法:
public Disposable schedule(Runnable task, long delay, TimeUnit unit) {
  //pick方法会获取一个ScheduledExecutorService线程执行器给到Schedulers使用
 return Schedulers.directSchedule(pick(), task, delay, unit);
}
  • 查看directSchedule方法:
static Disposable directSchedule(ScheduledExecutorService exec,
      Runnable task,
      long delay,
      TimeUnit unit) {
  //包装任务
   SchedulerTask sr = new SchedulerTask(task);
   Future<?> f;
   if (delay <= 0L) {
      f = exec.submit((Callable<?>) sr);
   }
   else {
     //延时调度
     //ScheduledExecutorService是java自带的并发调度接口,
     //通过一条线程轮询延时队列来避免所有线程阻塞
      f = exec.schedule((Callable<?>) sr, delay, unit);
   }
  //设置结果
   sr.setFuture(f);

   return sr;
}

自此就可以知道为什么delay方法没有阻塞线程,因为它的延时处理都交给了ScheduledExecutorService执行器处理,调用delay方法的主线程就直接返回了,等到延时时间过后,ScheduledExecutorService就会从线程池就获取一个线程来处理延时后的任务逻辑。整个流程就类似于上面图片中的右图。

通过反应式编程范式,将所有阻塞都修改为类似于delay之于sleep的形式,就能大幅度提升服务性能了。

查看原文 深入了解更多知识。

推荐阅读更多精彩内容

  • https://blog.csdn.net/defonds/article/details/44021605/ 译...
    huangxiongbiao阅读 1,012评论 0 11
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 10,434评论 1 32
  • layout: posttitle: 《Java并发编程的艺术》笔记categories: Javaexcerpt...
    xiaogmail阅读 5,435评论 1 19
  • 目录 一、什么是 Spring WebFlux二、WebFlux 的优势&提升性能?三、WebFlux 应用场景四...
    插件小屋阅读 6,645评论 0 28
  •   一个任务通常就是一个程序,每个运行中的程序就是一个进程。当一个程序运行时,内部可能包含了多个顺序执行流,每个顺...
    OmaiMoon阅读 1,518评论 0 12
  • 断断续续几年间尝试过各种健身运动。瑜伽会一点儿、拉丁会一点儿、芭蕾会一点儿、民族舞会一点儿、肚皮舞会一点儿、健身操...
    水晶陪你阅读 1,749评论 0 1
  • 2018年5月2日 星期三 小雨转晴 亲子日记第103篇 三年级二班徐聆越妈妈 期待了很久的语文卷拿回来,...
    大糖宫主阅读 103评论 0 0
  • 最近有点思路枯竭了,所以我想转折一下,后面会开始帝君追妻路,衔接可能不太顺,大家多担待
    墨冉_诺诺阅读 495评论 10 13
  • 东河红星影剧院门口晃着三个熟悉的身影,狗子带着东子蹲在地摊儿前翻着小人书,建国叼着颗烟坐在影剧院台阶上时不时...
    漠北孤城阅读 111评论 0 2
  • 空虚并不是一个名词,指代一种状态只是最微不足道的功能;空虚实质上是一个动词,分主动被动,分过去时,现在时,未来时。...
    简千禾阅读 286评论 0 1