一次webflux與webmvc性能測試實(shí)踐

最近Josh Long等Spring社區(qū)的大師來中國進(jìn)行交流與宣傳,并現(xiàn)場建立了一個(gè)spring-webflux的服務(wù)demo,首先從start.spring.io上構(gòu)建一個(gè)基于spring boot、reactive、mongo的reservation微服務(wù),然后基于微服務(wù)構(gòu)建routing支持,引入Spring Cloud Gateway,Spring Hystrix,Spring Security,F(xiàn)low Control,Client檢測,Rsocket等機(jī)制。整體Demo演示下來,讓與會(huì)的開發(fā)者充分了解Spring Framework的新功能與機(jī)制。
webflux是基于netty server,其實(shí)很早以前我就有一個(gè)疑惑,既然tomcat是阻塞式服務(wù)器,而netty是一個(gè)優(yōu)秀的nio通信框架,為什么沒有一個(gè)基于netty的server呢,性能會(huì)不會(huì)優(yōu)秀特別多呢。后來研究spring boot源碼的時(shí)候,發(fā)現(xiàn)它內(nèi)置了4個(gè)server,分別是jetty、netty、tomcat和undertow,路徑位于org.springframework.boot.web.embedded包下。因此其實(shí)netty server已經(jīng)有了,只是我之前并不知道罷了,既然有了netty和tomcat,很自然的我會(huì)希望對(duì)他們進(jìn)行一次benchmark測試下是否存在性能差異。
我先搜了下看有沒有前人的測試結(jié)果,國內(nèi)還是有的,一篇叫Spring WebFlux性能測試——響應(yīng)式Spring的道法術(shù)器的博客已經(jīng)寫了一篇比較系統(tǒng)的關(guān)于webflux與webmvc的性能測試總結(jié)。

image

這是他的測試結(jié)果,說實(shí)話,看到這個(gè)結(jié)果我還是挺震撼的,異步事件模型的網(wǎng)絡(luò)性能優(yōu)勢竟然如此巨大,那看來在對(duì)高并發(fā)有需求的場景下我們應(yīng)該選用webflux和netty server進(jìn)行編程了。
于是我激動(dòng)的開啟了自己的benchmark之旅,不過卻并沒能如同這位博主一樣比較順利。

Apache ab.exe測試

我沒有用過gatling,因此我首先還是用最簡單的工具apache.exe進(jìn)行測試
首先是對(duì)web-mvc的測試,測試一個(gè) /hello的接口

   @GetMapping("/hello")
   public String getEcho() throws InterruptedException {
        Thread.sleep (100);
        return "ok";
   }

測試命令: ab -n 20000 -c 5000 http://localhost:8082/hello
測試結(jié)果:

ab_20000n_5000c.png

可以看到95%的響應(yīng)在3s內(nèi)完成,說明在達(dá)到spring boot默認(rèn)線程數(shù)上限后,剩余的請(qǐng)求就會(huì)開始排隊(duì)了,spring boot默認(rèn)線程數(shù)是200,可以通過server.tomcat.max-threads屬性進(jìn)行修改,這里我改成了400,在測試過程中我用jconsole進(jìn)行觀測,發(fā)現(xiàn)idea的線程數(shù)峰值達(dá)到了425,和預(yù)想的情況相符。
下面是對(duì)webflux進(jìn)行測試
webflux接口代碼:

    @GetMapping("/hello")
    public Mono<String> hello() {
        return Mono.just ("hello").delayElement (Duration.ofMillis (100));
    }

測試結(jié)果如下:


ab_webflux_20000n_5000c.png

我很驚訝,因?yàn)楹皖A(yù)期的效果差距很遠(yuǎn),可以看到吞吐量和95% 請(qǐng)求響應(yīng)時(shí)間上限僅僅比 webmvc的版本優(yōu)秀一點(diǎn)點(diǎn)(1606->1548,2944->3050)。按照之前博主的結(jié)果,95%結(jié)果應(yīng)該在110ms以內(nèi),我又調(diào)整了參數(shù),分別對(duì)5000并發(fā)-20000并發(fā)進(jìn)行了測試,結(jié)果webflux在一些并發(fā)數(shù)情況下,性能要比webmvc還差。于是我猜想是不是我的測試工具有問題,畢竟ab.exe測試的結(jié)論過于簡單。于是我開始使用jmeter

Jmeter測試

我用jmeter對(duì)webflux進(jìn)行了測試,設(shè)置如下


jmeter_setting.png

其中并發(fā)數(shù)5000,在2s內(nèi)到達(dá)5000,每個(gè)并發(fā)請(qǐng)求15次,結(jié)果大吃一驚,webflux不但吞吐量隨著并發(fā)的增加而不斷下降,在后期并發(fā)打滿的情況下,httpresponse還出現(xiàn)了error。


jmeter_5000_15.png

95%響應(yīng)時(shí)間為3438ms,也不如人意。
然后我對(duì)webmvc進(jìn)行了測試

結(jié)果如下
webmvc_jmeter.png

依然有許多error,95%響應(yīng)時(shí)間也是3930ms,并不令人滿意。
后面我又增加了從3000-20000的并發(fā)數(shù)測試,結(jié)果都不令人滿意,頻繁出現(xiàn)error,而且響應(yīng)時(shí)間也不理想。
到這里為止,我開始有點(diǎn)懷疑博主的測試結(jié)果了,不過畢竟我們用的測試工具不同,前面文章的博主用的gatling,而我是jmeter,于是我開始了我第三輪測試。

gatling

我借用了博主的測試腳本,地址是https://github.com/get-set/get-reactive/tree/master/gatling。 然后下載了gatling、scala、sbt環(huán)境。不過運(yùn)行的時(shí)候始終報(bào)錯(cuò),我研究了下gatling的官方demo,我猜測可能是gatling新版本的api產(chǎn)生了變化,博主用的 over 在新版本里面已經(jīng)換成了 during。
修改了代碼后,腳本可以正常運(yùn)行,因?yàn)榧尤肓?s的每個(gè)并發(fā)每次的請(qǐng)求間隔,所以我感覺這個(gè)更符合真實(shí)的并發(fā)場景,因此對(duì)它的結(jié)果更加產(chǎn)生了期待。
然后事實(shí)依然是殘酷的,gatling的測試結(jié)果竟然僅僅比jmeter的要稍好一點(diǎn),我猜測這個(gè)結(jié)果還是因?yàn)樵谡?qǐng)求間加入了時(shí)延導(dǎo)致的。我不斷修改參數(shù),對(duì)3000-10000的并發(fā)的兩種server分別測試了20多次,依然沒有得到一個(gè)理想的結(jié)果,下面貼一個(gè)不是很好的結(jié)果做個(gè)示例。

gatling.png

可以看到高并發(fā)下,95%請(qǐng)求已經(jīng)達(dá)到了可怕的20s左右,并且有大量的error response。

不過中間有一次測試結(jié)果,我使用gatling測試5000并發(fā),結(jié)果是95%結(jié)果為104ms,我非常驚訝,感覺又有了希望。不過再也沒有重現(xiàn)過,后來我檢查了下代碼,我將請(qǐng)求repeat次數(shù)改小了,而during依然是30s,因此我猜測可能是因?yàn)閷?shí)際運(yùn)行過程中,線程并沒有完全打滿5000導(dǎo)致的。

測試環(huán)境的問題?

我后面回過頭去看前面博主的博客,發(fā)現(xiàn)評(píng)論區(qū)很多小伙伴出現(xiàn)了和我一樣的問題,就是jmerer測試webflux和webmvc的性能差距并不太大,博主也進(jìn)行了一些解答,其中提到了操作系統(tǒng),而我測試確實(shí)用的是windows系統(tǒng),而我們知道windows的nio采用的是IOCP而linux采用的是epoll,會(huì)不會(huì)是這個(gè)區(qū)別導(dǎo)致的呢?
于是我將程序部署到linux系統(tǒng)上,一開始我選擇的是我的阿里云,后來做benchmark結(jié)果簡直不忍直視,果然在高并發(fā)情況下,網(wǎng)絡(luò)情況也是會(huì)影響結(jié)果的。我只好用本機(jī)的虛擬機(jī)進(jìn)行測試,結(jié)果一波十幾折,首先是因?yàn)樘摂M機(jī)好久沒用了,網(wǎng)絡(luò)都忘了配置了,結(jié)果虛擬機(jī)和主機(jī)之間都無法通信,主機(jī)可以ping通虛擬機(jī),反向則不行。搗鼓了半天后終于ok了,我開始測試,一開始測試3000并發(fā),結(jié)果狂報(bào)錯(cuò),io.netty.channel.unix.Errors$NativeIoException: accept(..) failed: Too many open files。一看是fd打開太多,我輸入ulimit -n,果然虛擬機(jī)配置的默認(rèn)上限只有1024, vi /etc/security/limits.conf,修改連接數(shù)為65535并重啟,ulimt -n 確認(rèn)連接數(shù)后測試沒有繼續(xù)報(bào)錯(cuò)。but but but no use啊...
測試結(jié)果依然沒有好轉(zhuǎn):


virtual_jmeter.png

測試的參數(shù)是5000并發(fā),during是3s,每個(gè)并發(fā)重復(fù)10次。
可以看到無論是吞吐量還是95%響應(yīng)時(shí)間,都terrible??磥韜ebflux還有很多我需要深究的地方,要搞清楚他異步高性能的配置還需要下更大的功夫。

總結(jié)&TODO

經(jīng)過前面的測試,可以知道在我當(dāng)前的環(huán)境下,webflux并不是解決高性能高并發(fā)場景的銀彈。
但是既然前面的博主有這樣的測試結(jié)論,我相信他的數(shù)據(jù)結(jié)果也不是憑空來的,那么這接下來就是研究我們之間的環(huán)境究竟還有哪些差異。我可以想見的有:

  1. spring boot版本
  2. jdk版本
  3. 我是測試機(jī)與server都在同一臺(tái)機(jī)器,測試中也發(fā)現(xiàn)CPU完全被打滿了,而且測試腳本占用了大量CPU
  4. 操作系統(tǒng)

因此接下來需要在這些方面找找原因。
另外,我理解的webflux,確實(shí)非常適合做不間斷的響應(yīng)式數(shù)據(jù)推送的場景,例如滾動(dòng)聊天室,百度貼吧最上面的那種聊天,交易信息推送等等。不過這些都是后話,也許以后我會(huì)把之前的一些響應(yīng)式編程的總結(jié)記錄一下,不過當(dāng)前還是思考下benchmark的問題吧。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,506評(píng)論 19 139
  • Spring Web MVC Spring Web MVC 是包含在 Spring 框架中的 Web 框架,建立于...
    Hsinwong閱讀 22,931評(píng)論 1 92
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 47,253評(píng)論 6 342
  • 這一周我讀了81萬1千,是因?yàn)閹妆緯也粣劭瓷謭?bào)我不喜歡看,全是小故事,沒有意思,大故事才有意思呢。這是爸爸給...
    09小溪流家臣閱讀 144評(píng)論 1 0
  • 時(shí)隔一年后的今天,我終于可以坦然地寫出這件事情。對(duì)于這場劫后余生,我真切的感受是慶幸。 去年的今天,半夜12點(diǎn),我...
    自律女性vlog閱讀 519評(píng)論 9 7

友情鏈接更多精彩內(nèi)容