网络与IO知识扫盲(四):C10K问题、BIO的弊端与NIO的引入
C10K 問題
C10K 問題: http://www.kegel.com/c10k.html
我們使用BIO的時候,來一個連接就拋出一個線程。被拋出的獨立的線程進行阻塞,等待接收已連接的client發來的數據,這樣不會影響其他client繼續連接。每個線程自己忙自己的。
但是隨著連接數的變大,拋出的線程越多,由于線程之間的切換,系統的性能會越來越低。
舉一個例子
一個客戶端可以通過2個不同的ip,與服務端創建2*65000個連接。
C10Kclient.java
package com.bjmashibing.system.io;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.SocketChannel; import java.util.LinkedList;public class C10Kclient {public static void main(String[] args) {LinkedList<SocketChannel> clients = new LinkedList<>();InetSocketAddress serverAddr = new InetSocketAddress("192.168.150.11", 9090);for (int i = 10000; i < 65000; i++) { // 一個客戶端可以通過2個不同的ip,與服務端創建2*65000個連接try {SocketChannel client1 = SocketChannel.open();SocketChannel client2 = SocketChannel.open();/*linux中你看到的連接就是:client...port: 10508client...port: 10508*/client1.bind(new InetSocketAddress("192.168.150.1", i)); // 這臺機器上的第一個ip// 192.168.150.1:10000 192.168.150.11:9090client1.connect(serverAddr);boolean c1 = client1.isOpen();clients.add(client1);client2.bind(new InetSocketAddress("192.168.110.100", i)); // 這臺機器上的第二個ip// 192.168.110.100:10000 192.168.150.11:9090client2.connect(serverAddr);boolean c2 = client2.isOpen();clients.add(client2);} catch (IOException e) {e.printStackTrace();}}System.out.println("clients " + clients.size());try {System.in.read();} catch (IOException e) {e.printStackTrace();}} }關于為什么需要在Linux上單獨配一個路由條目
在Linux上單獨配一個路由條目
因為物理機的192.168.110.100和虛擬機的192.168.150.0不是直連關系
192.168.150.2是虛擬機所在網絡的網關,用于完成網絡地址轉換。
來自192.168.110.100的網絡包在返回給客戶端的時候,經過NAT地址轉換,目標ip被改成了192.168.150.2,Windows收到之后不知道這個ip應該發給誰了,導致三次握手的第二次回的包被windows丟棄了,沒有連接上。
關于為什么連接的速度并不快?(一秒鐘僅能夠建立4個連接左右)
為什么BIO慢?
創建一個連接的過程如下圖所示。
- accept系統調用,是一個阻塞的循環過程,這個過程耗費時間。
- 拋出一個線程的速度比較慢。
這就是整個BIO的弊端。想要調優,就要解決阻塞的問題,但阻塞是由內核提供給我們的API決accept receive定的。
NIO 的引入
NIO的N是啥意思呢?有兩個角度可以理解
- Non-Blocking IO (操作系統中)
- New IO (JDK中)
Linux中的文件描述符是輸入輸出雙向的。
Java中ServerSocketChannel也是淡化了輸入、輸出的概念,把輸入、輸出合在一起了。
一段NIO的代碼
SocketNIO.java
運行起來
ss.configureBlocking(true);阻塞狀態下
ss.configureBlocking(false);非阻塞狀態下
非阻塞了,這有什么意義?
假設這時候有兩個客戶端連接進來了,其中一個的示例是下面這個樣子的:
曾經我們是需要拋出一個線程,把這個線程扔出去,讓它自己去讀取數據。
現在不需要拋線程了,完全在一個線程中執行,只不過是無數個循環。而在沒有連接的時候,循環也不會被阻塞,依然繼續循環,從而讓循環后半部分的讀取數據的邏輯就有機會在當前循環當中被執行到。
再用C10K壓測一下
用C10K壓測一下NIO的性能(單客戶端10W連接):大約每秒鐘能建立50個左右的連接
現在使用NIO的瓶頸是:當連接進來很多客戶端時,for (SocketChannel c : clients)遍歷每一個客戶端去讀取數據的過程耗費了性能。
另外,報錯超出文件描述符的數量,這個是可以設置的:ulimit -SHn 500000(軟硬openfile,改成50萬)
注:為啥ulimit -n 1024,但是連接數超過了1024呢?
這個理論是對的,只不過要看用戶。權限對root來說等于虛設,很多資源的約束在root用戶也是放開的。而且在公司里,生產環境肯定是非root用戶啟動程序。
總結
以上是生活随笔為你收集整理的网络与IO知识扫盲(四):C10K问题、BIO的弊端与NIO的引入的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 网络与IO知识扫盲(三):从系统调用的角
- 下一篇: 网络与IO知识扫盲(五):从 NIO 到