Boost application performance using asynchronous I/O把同步阻塞、同步非阻塞、異步阻塞、異步非阻塞的模型講得很清楚。html
event-driven模型派(異步模型):react
有人對於event-driven模型有一些批判,認爲多線程模型(同步阻塞模型)不比事件模型差:git
兩種模型也不是說水火不容,SEDA提出了能夠將兩種模型結合起來,構建更具彈性的系統。10年以後該做者寫了篇回顧文章A Retrospective on SEDA。程序員
SEDA提出了幾個很具備見地的意見:github
什麼是Well-conditioned service?web
Intuitively, a service is well-conditioned if it behaves like a simple pipeline, where the depth of the pipeline is determined by the path through the network and the processing stages within the service itself. As the offered load increases, the delivered throughput increases proportionally until the pipeline is full and the throughput saturates; additional load should not degrade throughput. Similarly, the response time exhibited by the service is roughly constant at light load, because it is dominated by the depth of the pipeline. As load approaches saturation, the queueing delay dominates. In the closed-loop scenario typical of many services, where each client waits for a response before delivering the next request, response time should increase linearly with the number of clients.The key property of a well-conditioned service is graceful degradation: as offered load exceeds capacity, the service maintains high throughput with a linear response-time penalty that impacts all clients equally, or at least predictably according to some service-specific policy. Note that this is not the typical Web experience; rather, as load increases, throughput decreases and response time increases dramatically, creating the impression that the service has crashed.算法
簡單來講當負載超過一個應用的容量時,其性能表現要知足如下兩點:編程
事件驅動模型到最後就變成了Reactor Pattern,下面是幾篇文章:segmentfault
Scalable IO in Java介紹瞭如何使用NIO,其中很重要的一點是handler用來處理non-blocking的task,若是task是blocking的,那麼要交給其餘線程處理。這不就是簡化版的SEDA嗎?服務器
Reactor Pattern的老祖宗論文:Reactor Pattern,TL;DR。Understanding Reactor Pattern: Thread-Based and Event-Driven幫助你快速理解什麼是Reactor Pattern,文中提到若是要處理10K個長鏈接,Tomcat是開不了那麼多線程的。對此有一個疑問,Tomcat能夠採用NIO/NIO2的Connector,爲啥不能算做是Reactor呢?這是由於Tomcat不是事件驅動的,因此算不上。
The reactor pattern and non-blocking IO對比了Tomcat和vert.x的性能差異,不過看下來發現文章的壓測方式存在偏愛:
Thread.sleep()
。不過當我嘗試在vert.x中使用sleep則發生了大量報錯,應該是個人使用問題,後面就沒有深究了。我寫的測試能夠在這裏看到。
看了前面這麼多文章其實總結下來就這麼幾點:
Jeff Darcy's notes on high-performance server design提到了高性能服務器的幾個性能因素:
仔細看看有些因素不就是事件驅動模型和多線程模型都面臨的問題嗎?而又有一些因素則是兩種模型提出的當時所各自存在的短板嗎?而某些短板如今不是就已經解決了嗎?
上面說的有點虛,下面講點實在的。
若是你有10K個長鏈接,每一個鏈接大部分時間不使用CPU(處於Idle狀態或者blocking狀態),那麼爲每一個鏈接建立一個單獨的線程就顯得不划算。由於這樣作會佔用大量內存,而CPU的利用率卻很低,由於大多數時間線程都閒着。
事件驅動模型解決的是C10K問題,注意C是Connection,解決的是用更少的硬件資源處理更多的鏈接的問題,它不解決讓請求更快速的問題(這是程序員/算法的問題)。
要不要採用事件驅動模型取決於Task的CPU運算時間與Blocking時間的比例,若是比例很低,那麼用事件驅動模型。對於長鏈接來講,好比websocket,這個比例就很小,甚至可近似認爲是0,這個時候用事件驅動模型比較好。若是比例比較高,用多線程模型也能夠,它的編程複雜度很低。
不管是採用哪一種模型,都要用足硬件資源,這個資源能夠是CPU也能夠是網絡帶寬,若是發生資源閒置那你的吞吐量就上不去。
對於多線程模型來講開多少線程合適呢?Thousands of Threads and Blocking I/O裏講得很對,當可以使系統飽和的時候就夠了。好比CPU到100%了、網絡帶寬滿了。若是內存用滿了可是這兩個都沒用滿,那麼通常來講是出現BUG了。
對於事件驅動模型來講也有CPU用滿的問題,現實中總會存在一些阻塞操做會形成CPU閒置,這也就是爲何SEDA和Scalable IO in Java都提到了要額外開線程來處理這些阻塞操做。關於如何用滿CPU我以前寫了一篇文章如何估算吞吐量以及線程池大小能夠看看。
如何用滿網絡帶寬沒有什麼經驗,這裏就不說了。