Spring Webflux:EventLoop vs Thread per Request Model

Spring Webflux 介绍

Spring Webflux在spring 5引进来的而且他支持响应式编程。他使用的是异步编程模型。使用响应式编程可以让应用程序具有牛逼的伸缩性,以支持一段时间内比较大的负载请求。

和Spring MVC相比他解决了什么问题

Spring MVC使用的是同步编程模型,每一个请求都会匹配一个线程,该线程负责将拿到的结果返回给该请求的socket连接里。当应用程序发起一次网络调用,比如从DB里获取数据、从其他应用获取结果、从文件中进行读写操作,在这种情况下,该线程不得不进行同步等待这结果。请求线程会被阻塞掉,CPU在这段时间也没有什么利用率。这就是为什么这种模型需要使用线程池来进行处理请求。这种模型在处理请求量不大的情况下可以工作得很好,但是在请求量非常大的情况下,就会导致响应很慢或者无响应。这无疑会影响线上的业务。

Spring MVC with Servlet Container.png

针对这样的应用程序,响应式编程就有用武之地了。使用这种方式,请求线程将所需的请求数据发送到网络,并不是等待响应,而是会得到一个回调函数,该函数将在这个阻塞任务完成后执行调用。这种方式可以让请求的线程马上可以去处理另外的请求。如果使用得当,线程就不会被阻塞并且线程可以充分的利用CPU.这里的适当方式指的是,对于这个模型,每个线程都需要有响应式的行为,因此数据库驱动程序、服务间通信、web服务器等也应该使用响应式线程。

内嵌Server容器

Spring Webflux默认是将Netty做为他们的内部server容器。除了支持Netty外,他还支持Tomcat,Jetty,Undertow或者其他Servlet3.1+的容器。这里需要注意的是,Netty和Undertow是非servlet,而Tomcat和Jetty是众所周知的servlet容器。

在介绍Spring Webflux之前,我们先讲讲SpringMVC,后者用的Tomcat作为内嵌服务容器也就是"每个请求一个线程模型".而Webflux,Netty用的是"Event Loop模型"。虽然Webflux也支持在Tomcat中运行,但是仅支持实现了Servlet3.1及以后的API版本。

小提示:
Servlet 3引入了异步编程的特性,Servlet 3.1另外引入了异步I/O,允许所有操作异步。

EventLoop

EventLoop是一个非阻塞I/O线程(NIO),它持续运行不会阻塞,并从一系列的socket通道接收新的请求。如果有多个EventLoop,那么每个EventLoop被分配给一组socket通道,所有的EventLoop都在一个EventLoopGroup下管理。

下面的图展示了EventLoop是怎么运行的:


Fig. 2 Event Loop Working

这里展示的是EventLoop它在服务器端的用法。但当它在客户端时,它的工作方式是一样的,它向另一个服务器发送请求,例如I/O请求。

a.所有请求都在一个统一的Socket上接收,该Socket与一个称为SocketChannel的通道相关联。

b.每个SocketChannels都会有相关联的EventLoop线程,因此对该Socket/SocketChannels的所有请求都被移交给相同的EventLoop。

c. EventLoop上的请求通过一个通道管道,这些请求会在已经配置的入站Channel处理器或WebFilters来进行对应的处理。

d.在此之后,EventLoop执行应用程序自定义的代码。

e.在它的处理完成后,EventLoop会再执行配置好的出站Channel处理器进行处理。

f.最后,EventLoop将处理完的数据放回到之前对应的SocketChannel/Socket中。

g.在循环中重复步骤a到步骤f。

这是一个简单的用例,这肯定会让资源的使用最少化,因为他使用的是单线程,但这并不能解决真正的问题。

如果应用中的EventLoop由下面的原因导致阻塞会怎样?
1.CPU高度密集型工作
2.数据库的读写操作
3.文件的读写操作
4.调用网络上的其他应用

在上面的场景中,EventLoop 将会被会阻塞在d步骤中,我们将遇到和之前一样的情况,我们的应用将很快变得响应很慢或者无响应状态。创建额外的EventLoop不是解决方案,如前所述,一系列的Socket被绑定到单个EventLoop。因此,在已经被阻塞的EventLoop中的Socket是不能够让其他的EventLoop来帮助处理的。

小提示:
只有当应用程序运行在多CPU平台上时,才应该创建多个EventLoop,因为EventLoop必须保持一直运行,一个CPU不能保持多个EventLoop同时运行。默认情况下,应用程序开始时有多少个eventloop,就有多少个CPU(CPU数量应和EventLoop保持一致)。

这就是NIO EventLoop真正强大的地方,它背后原理极其简单。应用程序应该将请求委托给另一个线程,并通过异步回调返回结果,以解除EventLoop对新的请求处理的阻塞。所以EventLoop的工作步骤修改为如下(注:前面的情况就是所一个线程搞定所有的事,新的方案就是收到请求后,将业务上的处理交给业务线程池去理,这样效率更高):

a.步骤a到步骤c 保证一致

b.EventLoop将请求委托给新的线程进行处理
i.工作线程执行这些比较费时的任务
ii.完成后,它将响应改成一个任务,并将其添加到' ScheduledTaskQueue '中

c.EventLoop轮询ScheduledTaskQueue中的任务
i.如果有,通过任务的Runnable#run方法执行步骤e到步骤f
ii.否则,继续轮询SocketChannel上的新请求


图片.png

这些工作线程可以由开发人员创建,也可以从Reactor、RxJava或其他响应库中选择“Scheduler”策略。请记住临时使用这些线程,以保持资源利用率最小。

当应用程序有多个api,其中一些api由于网络调用或CPU高密集型工作而变慢时,这种方法很有帮助。在这种情况下,应用程序仍然可以部分可用并响应用户请求。

理想情况下,要使您的应用程序完全具有响应性,不应该有任何单个线程可能阻塞的情况。到目前为止,我们能够解除阻塞我们的请求处理线程,即EventLoop。但是我们的工作线程仍然在处理阻塞任务,当更多这样的请求到来时,我们不能无限期地增加这些线程数,因为这也可能导致管理大型线程的新问题,这可能严重影响CPU利用率和内存使用。在这种情况下,我们需要高效且有策略地使用工作线程。

对于上面提到的大多数阻塞情况,最好全部用响应式方法来处理。我们必须选择为DB调用提供异步操作。另外,我们有响应式Http客户端,可以通过网络调用另一个应用,如Spring Webflux响应式web客户端,它基本使用Reactor Netty库,即EventLoop模型。因此,如果我们的应用程序使用网络和响应式的web客户端,那么EventLoop资源将被共享。

图片.png

优点:
1.请求处理线程比较轻量级
2.硬件资源得到最佳利用
3.单个EventLoop可以在http客户端和请求处理之间共享
4.单线程可以处理多个Socket上的请求,例如来自不同客户端的请求
5.在无限流响应(infinite stream response)的情况下,该模型为背压处理提供支持。

每个请求一个线程模型

自从引入同步Servlet编程以来,一个请求对应一个线程模型就在实践中得到了广泛的应用。Servlet容器采用此模型来处理传入的请求。由于请求处理是同步的,因此该模型需要许多线程来处理请求,从而提高资源利用率。没有考虑可能阻塞这些线程的网络I/O。因此,对于可扩展性来说,资源通常需要扩展,但这就增加了系统成本。

如果Spring Webflux实现了Servlet 3.1+ api,那么使用响应式Spring Webflux也可以选择使用Servlet容器。Tomcat是最常用的Servlet容器,它也支持响应式编程。

当您Tomcat上使用Spring Webflux时,应用程序从线程池(例如10个)获取固定线程数来处理请求。来自Socket的请求被分配给此线程池中的线程。这里需要注意的是,Socket与线程之间不存在永久的绑定。

如果任何请求的线程在操作I/O时被阻塞了,该请求仍然可以被线程池中的其他线程处理。但是如果越来越多这种请求会导致所有的线程都被阻塞住。到目前为止,这与我们在同步处理中使用方式相同。但是在响应式方法中,它允许用户将请求委托给EventLoop中另一个工作线程池处理。通过这种方式,请求处理线程变得可用,应用程序一直可以保持对Client端的响应。

Fig. 5. Reactive Thread Per Request Model with Webclient

下面是在该模型中处理请求时执行的步骤:
1.所有请求都在唯一的Socket上接收,该Socket与一个称为SocketChannel的通道进行关联。
2.请求会被交给一个线程池里的一个线程进行处理。
3.线程上的请求需要经过之前配置好的的处理程序(如过滤器、servlet)进行处理。
4.请求线程可以将请求委托给工作线程或响应式web客户端(Webclient),同时在Controller中执行任何阻塞代码。
5.在它的完成后,工作线程或Webclient(EventLoop)将结果返回给相关的Socket。

同样,通过选择响应式客户端(如响应式web客户端)和响应式DB驱动程序,使应用程序完全具有响应式,最小线程就可以为系统提供有效的可伸缩性。

优点:
1.支持Servlet的api
2.如果任何请求线程阻塞,它只阻塞单个客户端套接字,而不像EventLoop中那样阻塞一系列套接字
3.对硬件资源的充分利用

性能比较

为了进行比较,我们创建了一个使用Spring Webflux的示例响应式Spring应用程序,故意延迟了100ms。然后将请求委托给另一个工作线程,以解除请求处理线程的阻塞。

为了进行比较,使用了以下配置:

  • Netty单个EventLoop的配置属性reactor.net . ioworkercount =1
  • 为Tomcat配置单个请求处理线属性 server.tomcat.max-threads=1
  • AWS EC2实例,配置为t2。micro (1GB RAM, 1CPU)
  • Jmeter测试脚本有100个用户,每1秒增加个,执行了10分钟
Fig. 6. CPU Utilization Comparison

最大CPU使用率:
Tomcat :37.6%
Netty:34.6%
结果:Tomcat使用cup比Netty多8.67052%

性能结果:
Tomcat 90%平均响应时间 : 114ms


图片.png

Netty90%平均响应时间 : 109ms


图片.png

结果:Tomcat的结果比Netty响应时长增加了4.58716%

原文:https://singhkaushal.medium.com/spring-webflux-eventloop-vs-thread-per-request-model-a42d07ee8502

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容