2 回答
TA貢獻1784條經驗 獲得超8個贊
目前有 2 個基本概念來處理對具有各種優點和缺點的 Web 服務器的并行訪問:
阻塞
非阻塞
阻止網絡服務器
阻塞、多線程的第一個概念服務器在池中有有限數量的線程。每個請求都將分配給特定的線程,并且該線程將被分配直到請求被完全服務。這與超市結賬隊列的工作方式基本相同,一次一個顧客,可能有平行線。在大多數情況下,Web 服務器中的請求在處理請求的大部分時間里都是 cpu 空閑的。這是因為它必須等待 I/O:讀取套接字、寫入數據庫(基本上也是 IO)并讀取結果并寫入套接字。此外,使用/創建一堆線程很慢(上下文切換)并且需要大量內存。因此,這個概念通常不會非常有效地使用它擁有的硬件資源,并且對可以并行服務的客戶端數量有硬性限制。slow loris,一種通常單個客戶端可以毫不費力地 DOS 大型多線程 Web 服務器的攻擊。
概括
(+) 更簡單的代碼
(-) 并行客戶端的硬限制
(-) 需要更多內存
(-) 通常的網絡服務器工作硬件使用效率低下
(-) 易于 DOS
大多數“傳統”網絡服務器都是這樣工作的,例如舊的 tomcat、Apache 網絡服務器以及所有Servlet早于 3 或 3.1 的東西等等。
非阻塞網絡服務器
相比之下,非阻塞網絡服務器可以只用一個線程為多個客戶端提供服務。那是因為它使用了非阻塞內核 I/O 特性。這些只是內核調用,當可以寫入或讀取某些內容時會立即返回并回調,從而使 cpu 可以自由地做其他工作。重用我們的超市比喻,這就像,當收銀員需要他的主管解決問題時,他不會等待并阻塞整個通道,而是開始結賬下一位顧客,直到主管到達并解決第一位顧客的問題顧客。
這通常在事件循環或更高的抽象中完成,如green-threads或fibers。本質上這樣的服務器不能真正并發處理任何事情(當然你可以有多個非阻塞線程),但它們能夠并行服務數千個客戶端,因為內存消耗不會像多線程那樣急劇擴展概念(閱讀:最大并行客戶端沒有硬性限制)。也沒有線程上下文切換。缺點是,非阻塞代碼通常更難以讀寫(例如callback-hell),并且在請求執行大量 cpu 昂貴工作的情況下效果不佳。
概括
(-) 更復雜的代碼
(-) cpu 密集型任務的性能更差
(+) 作為 Web 服務器更有效地使用資源
(+) 更多沒有硬限制的并行客戶端(最大內存除外)
大多數現代“快速”網絡服務器和框架促進了非阻塞概念:Netty、Vert.x、Webflux、nginx、servlet 3.1+、Node、Go 網絡服務器。
作為旁注,查看此基準頁面,您會發現大多數最快的 Web 服務器通常都是非阻塞服務器: https: //www.techempower.com/benchmarks/
TA貢獻1884條經驗 獲得超4個贊
使用 Servlet 2.5 時,Servlet 容器會將請求分配給線程,直到該請求已被完全處理。
使用 Servlet 3.0 異步處理時,服務器可以在應用程序處理請求時在單獨的線程池中分派請求處理。然而,當涉及到 I/O 時,工作總是發生在服務器線程上,并且它總是阻塞的。這意味著“慢客戶端”可以獨占服務器線程,因為服務器在讀取/寫入網絡連接較差的客戶端時被阻塞。
使用 Servlet 3.1,允許異步 I/O,在這種情況下,“一個請求/線程”模型不再存在。在任何時候,位請求處理都可以安排在服務器管理的不同線程上。
Servlet 3.1+ 容器通過 Servlet API 提供了所有這些可能性。利用異步處理或非阻塞 I/O 取決于應用程序。在非阻塞 I/O 的情況下,范式的改變很重要,而且使用起來真的很有挑戰性。
使用 Spring WebFlux - Tomcat、Jetty 和 Netty 沒有完全相同的運行時模型,但它們都支持反應式背壓和非阻塞 I/O。
添加回答
舉報
