关于 Spring-WebFlux 的一些想法 - 干货满满张哈希 - 博客园

标签: | 发表时间:2022-01-03 21:10 | 作者:
出处:https://www.cnblogs.com

本文是本人在知乎提问 spring webflux现在看来是否成功?下的回答,其他回答也很精彩,如果感兴趣可以查看

现在基于 spring web 的同步微服务有一个非常大的缺陷就是:相对于基于 spring-webflux 的异步微服务,基于 spring-web 的同步微服务没有很好的处理客户端有请求超时配置的情况。当客户端请求超时时,客户端会直接返回超时异常,但是调用的服务端任务,在基于 spring-web 的同步微服务并没有被取消,基于 spring-webflux 的异步微服务是会被取消的。目前,还没有很好的办法在同步环境中可以取消这些已经超时的任务。

Spring-weflux 目前最大的问题,在于很多框架,包括 JDK 本身,有很多 基于 Thread 的 Context,例如 Thread local 这种。还有就是 Java Log 框架的 MDC 的实现,一般都是基于 ThreadLocal 的 Map.还有就是像 redisson 的分布式锁,它让每个线程生成唯一id并和线程绑定,解锁的时候会检查。 但是这种设计,与 Spring-Webflux 的 Context 很难兼容。可以看看 Spring cloud sleuth 在 Spring-Webflux 中加入链路信息上下文,并保持,有多麻烦,而且,还有不少的 bug 和漏掉的点,参考:

还有一点比较麻烦,就是和 现有的各种阻塞锁的设计,不兼容,因为响应式编程需要非阻塞。这需要重构成队列排序消费来解决并发竞争,工作量也很大。

然后就是 官方数据库 IO 库,不是 NIO这个问题。不论是Java自带的Future框架,还是 Spring WebFlux,还是 Vert.x,他们都是一种非阻塞的基于Ractor模型的框架(后两个框架都是利用netty实现)。在阻塞编程模式里,任何一个请求,都需要一个线程去处理,如果io阻塞了,那么这个线程也会阻塞在那。但是在非阻塞编程里面,基于响应式的编程,线程不会被阻塞,还可以处理其他请求。举一个简单例子:假设只有一个线程池,请求来的时候,线程池处理,需要读取数据库 IO,这个 IO 是 NIO 非阻塞 IO,那么就将请求数据写入数据库连接,直接返回。之后数据库返回数据,这个链接的 Selector 会有 Read 事件准备就绪,这时候,再通过这个线程池去读取数据处理(相当于回调),这时候用的线程和之前不一定是同一个线程。这样的话,线程就不用等待数据库返回,而是直接处理其他请求。这样情况下,即使某个业务 SQL 的执行时间长,也不会影响其他业务的执行。但是,这一切的基础,是 IO 必须是非阻塞 IO,也就是 NIO(或者 AIO)。官方JDBC没有 NIO,只有 BIO 实现。这样无法让线程将请求写入链接之后直接返回,必须等待响应。但是也就解决方案,就是通过其他线程池,专门处理数据库请求并等待返回进行回调,也就是业务线程池 A 将数据库 BIO 请求交给线程池B处理,读取完数据之后,再交给 A 执行剩下的业务逻辑。这样A也不用阻塞,可以处理其他请求。但是,这样还是有因为某个业务 SQL 的执行时间长,导致B所有线程被阻塞住队列也满了从而A的请求也被阻塞的情况,这是不完美的实现。真正完美的,需要 JDBC 实现 NIO。当然,也可以使用其他异步响应式的三方库,但是非官方的,兼容性以及是否更新及时,还有使用限制什么的也很麻烦。

最后,提一下 Java 本身的 Project Loom,我简单研究过他的实现原理:

简单总结即:在虚拟线程中运行的 Java 同步网络 API 会将底层原生 Socket 切换到非阻塞模式。当 Java 代码启用一个 I/O 请求并且这个请求没有立即完成(原生 socket 返回 EAGAIN - 代表"未就绪"/"会阻塞")的时候,这个底层 socket 会被注册到一个 JVM 内部事件通知机制(Poller),并且虚拟线程会被 parked。当底层 I/O 操作就绪的时候(有相关事件会到达 Poller),虚拟线程会 unparked 并且底层的 Socket 操作会被重试处理。同步 Java 网络 API 已经被重新实现,相关的 JEP 包括 JEP 353 和 JEP 373. 在虚拟线程中运行时,不能立即完成的 I/O 操作将导致虚拟线程被 parked 。当 I/O 就绪时,虚拟线程将被 unparked。这个实现相对于当前的异步非阻塞 I/O 实现代码来看,更加简单易用,隐藏了很多业务不关心的实现细节。

Project Loom 解决了主要的网络 IO 阻塞问题,并且基本不用改现有代码就能实现纤程,用阻塞的代码风格实现非阻塞的代码(而且和现在的基于 Thread 的上下文框架兼容)。是目前的 Java 架构师 Goetz 最看重的特性之一,目前 Java 17 的很多新特性其实就是为这个 Project Loom 的发布铺路,可以看看 Nicolai 和 Goetz 大神的这个视频,从 19:17 秒开始:

Brian Goetz: "I think Project Loom is going to kill Reactive Programming"(哈哈,有点过于乐观带节奏了,不过值得观望)

不过,虽然期望中是仅需要下面这种代码就可以把现有的线程和线程池替换成虚拟线程:

      Thread thread = Thread.ofVirtual().name("duke").unstarted(runnable);
ThreadFactory factory = Thread.ofVirtual().factory();

ExecutorService b = Executors.newVirtualThreadPool();

但是还有很多问题需要解决:

  1. ThreadLocal 相关的类,由于虚拟线程会无限制的生成,ThreadLocal 的生成也需要重新设计:首先是很多 JDK 中的框架基于 ThreadLocal 的 Probe 实现,例如 ThreadLocalRandom 的初始 Seed。第二是 ThreadLocal 的使用可能会导致 GC 压力增大,因为虚拟线程可以无限制的生成。
  2. 依然阻塞实际线程的地方:在同步锁的阻塞还是会阻塞实际的线程,还有文件 IO 等。
  3. 修改以上带来的 bug 以及安全问题,由于这些修改动了 JDK 的一些框架的根本,没有经过实际线上应用之前,仅凭单元测试和压测可能很难发现一些细节问题。

不过,现在的 Java 已经在为 Project loom 铺路了,例如:

相关 [spring webflux 想法] 推荐:

深入剖析 Spring WebFlux

- -
WebFlux 是 Spring Framework5.0 中引入的一种新的反应式Web框架. 通过Reactor项目实现Reactive Streams规范,完全异步和非阻塞框架. 本身不会加快程序执行速度,但在高并发情况下借助异步IO能够以少量而稳定的线程处理更高的吞吐,规避文件IO/网络IO阻塞带来的线程堆积.

关于 Spring-WebFlux 的一些想法 - 干货满满张哈希 - 博客园

- -
spring webflux现在看来是否成功. 下的回答,其他回答也很精彩,如果感兴趣可以查看. 现在基于 spring web 的同步微服务有一个非常大的缺陷就是:相对于基于 spring-webflux 的异步微服务,基于 spring-web 的同步微服务没有很好的处理客户端有请求超时配置的情况.

Spring详解

- - CSDN博客架构设计推荐文章
Spring是一个开源的控制反转(Inversion of Control ,IoC)和面向切面(AOP)的容器框架.它的主要目的是简化企业开发.. PersonDaoBean 是在应用内部创建及维护的. 所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的.

Spring定时

- - 行业应用 - ITeye博客
spring的定时任务配置分为三个步骤:. . . . . .

简单Spring+hessian

- - Web前端 - ITeye博客
简单的Spring+hessian. dist\modules里面的 spring-webmvc.jar . lib\caucho 里面的hessian-3.1.3.jar. 里面有个接口interface:. 建立一个model层:(实现Serializable接口). 在WEB-INF下面创建一个remoting-servlet.xml:.

Spring MVC 和 Struts2

- - CSDN博客架构设计推荐文章
Web层面的框架学习了三个Struts1和2,SpringMVC,那他们之间肯定存在一个优劣和适用的环境,Struts1和2的异同点我已经做过对比《 Struts1和Struts2》,这篇将对比下Struts2和SpringMVC的异同,下面数据基本来源于网络,本人是搜集整理所得,供大家参考. 一个项目使用什么样的技术,决定的因素很多,我所能想到的有:对系统的性能、开发的效率、团队学习的成本、业务场景等,下面尽量从这几个方面入手,来分析比较下他们之间存在的优劣.

Spring AOP详解

- - Java - 编程语言 - ITeye博客
        最近项目中遇到了以下几点需求,仔细思考之后,觉得采用AOP来解决. 一方面是为了以更加灵活的方式来解决问题,另一方面是借此机会深入学习Spring AOP相关的内容. 例如,以下需求不用AOP肯定也能解决,至于是否牵强附会,仁者见仁智者见智. 1.对部分函数的调用进行日志记录,用于观察特定问题在运行过程中的函数调用情况.

spring roo 入门

- - 企业架构 - ITeye博客
Spring官网下载STS(如果没有STS). 创建Spring Roo基础项目. 根 据ROO的提示输入jpa setup再按ctrl+space,很遗憾这个快捷键已经被输入法切换占用,不能借助提示输入命令,但我们可以打开ROO命令向导,这里我们输入jpa 可以查到这条命令的用法,根据提示增加provider和database选项来完成命令.

Spring Rmi配置

- - 企业架构 - ITeye博客
现在远程调用一般用RPC,webservice或者Rmi,而目前用的比较多的是webservice和Rmi. webservice和rmi的最主要的区别,rmi的客户端和服务端都必须是java,webservice没有这个限制,webservice是在http协议上传递xml文本文件. 与语言和平台无关,rmi是在tcp协议上传递可序列化的java对象,只能用在java虚拟机上,绑定语言.

Spring+memcached整合

- - 行业应用 - ITeye博客
1)  下载memcached服务端memcached-1.2.6-win32-bin.zip,地址:http:. 2)  下载java版客户端 java_memcached-release_2.6.1.zip. 3)  解压缩memcached-1.2.6-win32-bin.zip到指定目录,例如:D:\memcached-1.2.6-win32 ,.