關於同步/異步、阻塞/非阻塞IO的摘要

四種IO模型

Boost application performance using asynchronous I/O把同步阻塞、同步非阻塞、異步阻塞、異步非阻塞的模型講得很清楚。html

處理大量鏈接的問題

event-driven模型派(異步模型):react

有人對於event-driven模型有一些批判,認爲多線程模型(同步阻塞模型)不比事件模型差:git

  • Thousands of Threads and Blocking I/O,講了C10K提到的多線程模型的性能瓶頸在現在的內核裏已經不存在了,而多線程模型開發起來更簡單。
  • Why Events are a Bad Idea(for high concurrency servers) Rob von Behren,講了多線程模型的性能瓶頸基本上是由於內核支持的很差、多線程類庫有缺陷形成的。認爲能夠經過編譯器的優化、修復內核、修復多線程類庫來達到和事件驅動模型至關的結果。且認爲事件驅動模型的開發比較複雜。

兩種模型也不是說水火不容,SEDA提出了能夠將兩種模型結合起來,構建更具彈性的系統。10年以後該做者寫了篇回顧文章A Retrospective on SEDA程序員

SEDA提出了幾個很具備見地的意見:github

  1. 應用程序的各個stage的壓力應該是可觀測和可調節的。
  2. 應用程序應該是well-conditioned。

什麼是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

事件驅動模型到最後就變成了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的性能差異,不過看下來發現文章的壓測方式存在偏愛:

  1. 文中給Tomcat的線程少了(只給了500),只利用了40%左右的CPU,而vert.x的測試的CPU利用率爲100%。我把的Tomcat的線程設到2000,測試結果就和vert.x差很少了(驗證了多線程模型派的觀點)。
  2. vert.x的測試代碼和Tomcat的測試代碼不等價,沒有使用Thread.sleep()。不過當我嘗試在vert.x中使用sleep則發生了大量報錯,應該是個人使用問題,後面就沒有深究了。

我寫的測試能夠在這裏看到。

總結

看了前面這麼多文章其實總結下來就這麼幾點:

  1. 選擇事件驅動模型仍是多線程模型要根據具體狀況來(不過這是一句廢話,; )
  2. 推崇、反對某個模型的文章/論文都是在當時的歷史狀況下寫出來的,說白了就是存在歷史侷限性,所以必定要本身驗證,當時正確的論斷對如今來說未必正確,事情是會發生變化的。
  3. 看測試報告的時候必定要本身試驗,有些測試可能自己設計的就有問題,致使結果存在偏見。對於大多數性能測試來講,我以爲只要抓住一點就好了,就是CPU必定要用足。
  4. 咱們真正應該關注的是不變的東西。

Jeff Darcy's notes on high-performance server design提到了高性能服務器的幾個性能因素:

  • data copy,問題依然存在,須要程序員去優化。
  • context switch,這個問題已經沒有了(見多線程派的幾篇文章),現代操做系統不論有多少thread,開銷不會有顯著增長。
  • memory allocation,這個要看看,不過在Java裏彷佛和JVM GC有關。
  • lock contention,這個問題依然存在,應該儘可能使用lock-free/non-blocking的數據結構。
  • 另外補充:在C10M裏提到kernel和內核的network stack也是瓶頸。

仔細看看有些因素不就是事件驅動模型和多線程模型都面臨的問題嗎?而又有一些因素則是兩種模型提出的當時所各自存在的短板嗎?而某些短板如今不是就已經解決了嗎?

上面說的有點虛,下面講點實在的。

若是你有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閒置,這也就是爲何SEDAScalable IO in Java都提到了要額外開線程來處理這些阻塞操做。關於如何用滿CPU我以前寫了一篇文章如何估算吞吐量以及線程池大小能夠看看。

如何用滿網絡帶寬沒有什麼經驗,這裏就不說了。

相關文章
相關標籤/搜索