中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

Ruby Web 服務(wù)器的并發(fā)模型與性能

2018-07-20    來源:編程學(xué)習(xí)網(wǎng)

容器云強(qiáng)勢上線!快速搭建集群,上萬Linux鏡像隨意使用

這是整個(gè) Rack 系列文章的最后一篇了,在之前其實(shí)也嘗試寫過很多系列文章,但是到最后都因?yàn)楦鞣N原因放棄了,最近由于自己對 Ruby 的 webserver 非常感興趣,所以看了下社區(qū)中常見 webserver 的實(shí)現(xiàn)原理,包括 WEBrick、Thin、Unicorn 和 Puma,雖然在 Ruby 社區(qū)中也有一些其他的 webserver 有著比較優(yōu)異的性能,但是在這有限的文章中也沒有辦法全都介紹一遍。

在這篇文章中,作者想對 Ruby 社區(qū)中不同 webserver 的實(shí)現(xiàn)原理和并發(fā)模型進(jìn)行簡單的介紹,總結(jié)一下前面幾篇文章中的內(nèi)容。

文中所有的壓力測試都是在內(nèi)存 16GB、8 CPU、2.6 GHz Intel Core i7 的 macOS 上運(yùn)行的,如果你想要復(fù)現(xiàn)這里的測試可能不會得到完全相同的結(jié)果。

WEBrick

WEBrick 是 Ruby 社區(qū)中非常古老的 Web 服務(wù)器,從 2000 年到現(xiàn)在已經(jīng)有了將近 20 年的歷史了,雖然 WEBrick 有著非常多的問題,但是迄今為止 WEBrick 也是開發(fā)環(huán)境中最常用的 Ruby 服務(wù)器;它使用了最為簡單、直接的并發(fā)模型,運(yùn)行一個(gè) WEBrick 服務(wù)器只會在后臺啟動一個(gè)進(jìn)程,默認(rèn)監(jiān)聽來自 9292 端口的請求。

當(dāng) WEBrick 通過 .select 方法監(jiān)聽到來自客戶端的請求之后,會為每一個(gè)請求創(chuàng)建一個(gè)單獨(dú) Thread 并在新的線程中處理 HTTP 請求。

run Proc.new { |env| ['200', {'Content-Type' => 'text/plain'}, ['get rack\'d']] }

如果我們?nèi)绻麆?chuàng)建一個(gè)最簡單的 Rack 應(yīng)用,直接返回所有的 HTTP 響應(yīng),那么使用下面的命令對 WEBrick 的服務(wù)器進(jìn)行測試會得到如下的結(jié)果:

Concurrency Level:      100
Time taken for tests:   22.519 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      2160000 bytes
HTML transferred:       200000 bytes
Requests per second:    444.07 [#/sec] (mean)
Time per request:       225.189 [ms] (mean)
Time per request:       2.252 [ms] (mean, across all concurrent requests)
Transfer rate:          93.67 [Kbytes/sec] received

在處理 ApacheBench 發(fā)出的 10000 個(gè) HTTP 請求時(shí),WEBrick 對于每個(gè)請求平均消耗了 225.189ms,每秒處理了 444.07 個(gè)請求;除此之外,在處理請求的過程中 WEBrick 進(jìn)程的 CPU 占用率很快達(dá)到了 100%,通過這個(gè)測試我們就可以看出為什么不應(yīng)該在生產(chǎn)環(huán)境中使用 WEBrick 作為 Ruby 的應(yīng)用服務(wù)器,在業(yè)務(wù)邏輯和代碼更加復(fù)雜的情況下,WEBrick 的性能想必也不會達(dá)到期望。

在 2006 和 2007 兩年,Ruby 社區(qū)中發(fā)布了兩個(gè)至今都非常重要的開源項(xiàng)目,其中一個(gè)是 Mongrel,它提供了標(biāo)準(zhǔn)的 HTTP 接口,同時(shí)多語言的支持也使得 Mongrel 在當(dāng)時(shí)非常流行,另一個(gè)項(xiàng)目就是 Rack 了,它在 Web 應(yīng)用和 Web 服務(wù)器之間建立了一套統(tǒng)一的 標(biāo)準(zhǔn) ,規(guī)定了兩者的協(xié)作方式,所有的應(yīng)用只要遵循 Rack 協(xié)議就能夠隨時(shí)替換底層的應(yīng)用服務(wù)器。

隨后,在 2009 年出現(xiàn)的 Thin 就站在了巨人的肩膀上,同時(shí)遵循了 Rack 協(xié)議并使用了 Mongrel 中的解析器,而它也是 Ruby 社區(qū)中第一個(gè)使用 Reactor 模型的 Web 服務(wù)器。

Thin 使用 Reactor 模型處理客戶端的 HTTP 請求,每一個(gè)請求都會交由 EventMachine,通過內(nèi)部對事件的分發(fā),最終執(zhí)行相應(yīng)的回調(diào),這種事件驅(qū)動的 IO 模型與 node.js 非常相似,使用單進(jìn)程單線程的并發(fā)模型卻能夠快速處理 HTTP 請求;在這里,我們?nèi)匀皇褂?ApacheBench 以及同樣的負(fù)載對 Thin 的性能進(jìn)行簡單的測試。

Concurrency Level:      100
Time taken for tests:   4.221 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      880000 bytes
HTML transferred:       100000 bytes
Requests per second:    2368.90 [#/sec] (mean)
Time per request:       42.214 [ms] (mean)
Time per request:       0.422 [ms] (mean, across all concurrent requests)
Transfer rate:          203.58 [Kbytes/sec] received

對于一個(gè)相同的 HTTP 請求,Thin 的吞吐量大約是 WEBrick 的四倍,每秒能夠處理 2368.90 個(gè)請求,同時(shí)處理的速度也大幅降低到了 42.214ms;在壓力測試的過程中雖然 CPU 占用率有所上升但是在處理的過程中完全沒有超過 90%,可以說 Thin 的性能碾壓了 WEBrick,這可能也是開發(fā)者都不會在生產(chǎn)環(huán)境中使用 WEBrick 的最重要原因。

但是同樣作為單進(jìn)程運(yùn)行的 Thin,由于沒有 master 進(jìn)程的存在,哪怕當(dāng)前進(jìn)程由于各種各樣奇怪的原因被操作系統(tǒng)殺掉,我們也不會收到任何的通知,只能手動重啟應(yīng)用服務(wù)器。

Unicorn

與 Thin 同年發(fā)布的 Unicorn 雖然也是 Mongrel 項(xiàng)目的一個(gè) fork,但是使用了完全不同的并發(fā)模型,每Unicorn 內(nèi)部通過多次 fork 創(chuàng)建多個(gè) worker 進(jìn)程,所有的 worker 進(jìn)程也都由一個(gè) master 進(jìn)程管理和控制:

由于 master 進(jìn)程的存在,當(dāng) worker 進(jìn)程被意外殺掉后會被 master 進(jìn)程重啟,能夠保證持續(xù)對外界提供服務(wù),多個(gè)進(jìn)程的 worker 也能夠很好地壓榨多核 CPU 的性能,盡可能地提高請求的處理速度。

一組由 master 管理的 Unicorn worker 會監(jiān)聽綁定的兩個(gè) Socket,所有來自客戶端的請求都會通過操作系統(tǒng)內(nèi)部的負(fù)載均衡進(jìn)行調(diào)度,將請求分配到不同的 worker 進(jìn)程上進(jìn)行處理。

不過由于 Unicorn 雖然使用了多進(jìn)程的并發(fā)模型,但是每個(gè) worker 進(jìn)程在處理請求時(shí)都是用了阻塞 I/O 的方式,所以如果客戶端非常慢就會大大影響 Unicorn 的性能,不過這個(gè)問題就可以通過反向代理來 nginx 解決。

在配置 Unicorn 的 worker 數(shù)時(shí),為了最大化的利用 CPU 資源,往往會將進(jìn)程數(shù)設(shè)置為 CPU 的數(shù)量,同樣我們使用 ApacheBench 以及相同的負(fù)載測試一個(gè)使用 8 核 CPU 的 Unicorn 服務(wù)的處理效率:

Concurrency Level:      100
Time taken for tests:   2.401 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      1110000 bytes
HTML transferred:       100000 bytes
Requests per second:    4164.31 [#/sec] (mean)
Time per request:       24.014 [ms] (mean)
Time per request:       0.240 [ms] (mean, across all concurrent requests)
Transfer rate:          451.41 [Kbytes/sec] received

經(jīng)過簡單的壓力測試,當(dāng)前的一組 Unicorn 服務(wù)每秒能夠處理 4000 多個(gè)請求,每個(gè)請求也只消耗了 24ms 的時(shí)間,比起使用單進(jìn)程的 Thin 確實(shí)有著比較多的提升,但是并沒有數(shù)量級的差距。

除此之外,Unicorn 由于其多進(jìn)程的實(shí)現(xiàn)方式會占用大量的內(nèi)存,在并行的處理大量請求時(shí)你可以看到內(nèi)存的使用量有比較明顯的上升。

距離 Ruby 社區(qū)的第一個(gè) webserver WEBrick 發(fā)布的 11 年之后的 2011 年,Puma 正式發(fā)布了,它與 Thin 和 Unicorn 一樣都從 Mongrel 中繼承了 HTTP 協(xié)議的解析器,不僅如此它還基于 Rack 協(xié)議重新對底層進(jìn)行了實(shí)現(xiàn)。

與 Unicorn 不同的是,Puma 是用了多進(jìn)程加多線程模型,它可以同時(shí)在 fork 出來的多個(gè) worker 中創(chuàng)建多個(gè)線程來處理請求;不僅如此 Puma 還實(shí)現(xiàn)了用于提高并發(fā)速度的 Reactor 模塊和線程池能夠在提升吞吐量的同時(shí),降低內(nèi)存的消耗。

但是由于 MRI 的存在,往往都需要使用 JRuby 才能最大化 Puma 服務(wù)器的性能,但是即便如此,使用 MRI 的 Puma 的吞吐量也能夠輕松達(dá)到 Unicorn 的兩倍。

Concurrency Level:      100
Time taken for tests:   1.057 seconds
Complete requests:      10000
Failed requests:        0
Total transferred:      750000 bytes
HTML transferred:       100000 bytes
Requests per second:    9458.08 [#/sec] (mean)
Time per request:       10.573 [ms] (mean)
Time per request:       0.106 [ms] (mean, across all concurrent requests)
Transfer rate:          692.73 [Kbytes/sec] received

在這里我們創(chuàng)建了 8 個(gè) Puma 的 worker,每個(gè) worker 中都包含 16~32 個(gè)用于處理用戶請求的線程,每秒中處理的請求數(shù)接近 10000,處理時(shí)間也僅為 10.573ms,多進(jìn)程、多線程以及 Reactor 模式的協(xié)作確實(shí)能夠非常明顯的增加 Web 服務(wù)器的工作性能和吞吐量。

在 Puma 的 官方網(wǎng)站 中,有一張不同 Web 服務(wù)器內(nèi)存消耗的對比圖:

我們可以看到,與 Unicorn 相比 Puma 的內(nèi)存使用量幾乎可以忽略不計(jì),它明顯解決了多個(gè) worker 占用大量內(nèi)存的問題;不過使用了多線程模型的 Puma 需要開發(fā)者在應(yīng)用中保證不同的線程不會出現(xiàn)競爭條件的問題,Unicorn 的多進(jìn)程模型就不需要開發(fā)者思考這樣的事情。

上述四種不同的 Web 服務(wù)器其實(shí)有著比較明顯的性能差異,在使用同一個(gè)最簡單的 Web 應(yīng)用時(shí),不同的服務(wù)器表現(xiàn)出了差異巨大的吞吐量:

Puma 和 Unicorn 兩者之間可能還沒有明顯的數(shù)量級差距,1 倍的吞吐量差距也可能很容易被環(huán)境因素抹平了,但是 WEBrick 可以說是絕對無法與其他三者匹敵的。

上述的不同服務(wù)器其實(shí)有著截然不同的 I/O 并發(fā)模型,因?yàn)?MRI 中 GIL 的存在我們很難利用多核 CPU 的計(jì)算資源,所以大多數(shù)多線程模型在 MRI 上的性能可能只比單線程略好,達(dá)不到完全碾壓的效果,但是 JRuby 或者 Rubinius 的使用確實(shí)能夠利用多核 CPU 的計(jì)算資源,從而增加多線程模型的并發(fā)效率。

傳統(tǒng)的 I/O 模型就是在每次接收到客戶端的請求時(shí) fork 出一個(gè)新的進(jìn)程來處理當(dāng)前的請求或者在服務(wù)器啟動時(shí)就啟動多個(gè)進(jìn)程,每一個(gè)進(jìn)程在同一時(shí)間只能處理一個(gè)請求,所以這種并發(fā)模型的吞吐量有限,在今天已經(jīng)幾乎看不到使用 accept & fork 這種方式處理請求的服務(wù)器了。

目前最為流行的方式還是混合多種 I/O 模型,同時(shí)使用多進(jìn)程和多線程壓榨 CPU 計(jì)算資源,例如 Phusion Passenger 或者 Puma 都支持在單進(jìn)程和多進(jìn)程、單線程和多線程之前來回切換,配置的不同會創(chuàng)建不同的并發(fā)模型,可以說是 Web 服務(wù)器中最好的選擇了。

最后要說的 Thin 其實(shí)使用了非常不同的 I/O 模型,也就是事件驅(qū)動模型,這種模型在 Ruby 社區(qū)其實(shí)并沒有那么熱門,主要是因?yàn)?Rails 框架以及 Ruby 社區(qū)中的大部分項(xiàng)目并沒有按照 Reactor 模型的方式進(jìn)行設(shè)計(jì),默認(rèn)的文件 I/O 也都是阻塞的,而 Ruby 本身也可以利用多進(jìn)程和多線程的計(jì)算資源,沒有必要使用事件驅(qū)動的方式最大化并發(fā)量。

Node.js 就完全不同了。Javascript 作為一個(gè)所有操作都會阻塞主線程的語言,更加需要事件驅(qū)動模型讓主線程只負(fù)責(zé)接受 HTTP 請求,其余的臟活累活都交給線程池來做了,結(jié)果的返回都通過回調(diào)的形式通知主線程,這樣才能提高吞吐量。

在這個(gè)系列的文章中,我們先后介紹了 Rack 的實(shí)現(xiàn)原理以及 Rack 協(xié)議,還有四種 webserver 包括 WEBrick、Thin、Unicorn 和 Puma 的實(shí)現(xiàn),除了這四種應(yīng)用服務(wù)器之外,Ruby 社區(qū)中還有其他的應(yīng)用服務(wù)器,例如:Rainbows 和 Phusion Passenger,它們都有各自的實(shí)現(xiàn)以及優(yōu)缺點(diǎn)。

從當(dāng)前的情況來看,還是更推薦開發(fā)者使用 Puma 或者 Phusion Passenger 作為應(yīng)用的服務(wù)器,這樣能獲得最佳的效果。

Reference

  • Ruby Web 服務(wù)器:這十五年
  • Ruby 服務(wù)器對比
  • Ruby 的多線程應(yīng)用服務(wù)器介紹
  • Ruby on Rails Server options

 

來自:http://draveness.me/ruby-webserver

 

標(biāo)簽: 代碼 服務(wù)器 服務(wù)器內(nèi)存 開發(fā)者 應(yīng)用服務(wù)器

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請與原作者聯(lián)系。

上一篇:安卓開發(fā)常用工具和第三方庫匯總

下一篇:最近很火的 Safe Area 到底是什么