日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

网络与IO知识扫盲(四):C10K问题、BIO的弊端与NIO的引入

發(fā)布時間:2024/2/28 编程问答 60 豆豆
生活随笔 收集整理的這篇文章主要介紹了 网络与IO知识扫盲(四):C10K问题、BIO的弊端与NIO的引入 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

C10K 問題

C10K 問題: http://www.kegel.com/c10k.html
我們使用BIO的時候,來一個連接就拋出一個線程。被拋出的獨立的線程進行阻塞,等待接收已連接的client發(fā)來的數(shù)據(jù),這樣不會影響其他client繼續(xù)連接。每個線程自己忙自己的。
但是隨著連接數(shù)的變大,拋出的線程越多,由于線程之間的切換,系統(tǒng)的性能會越來越低。

舉一個例子

一個客戶端可以通過2個不同的ip,與服務(wù)端創(chuàng)建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,與服務(wù)端創(chuàng)建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();}} }

關(guān)于為什么需要在Linux上單獨配一個路由條目

在Linux上單獨配一個路由條目

因為物理機的192.168.110.100和虛擬機的192.168.150.0不是直連關(guān)系
192.168.150.2是虛擬機所在網(wǎng)絡(luò)的網(wǎng)關(guān),用于完成網(wǎng)絡(luò)地址轉(zhuǎn)換。
來自192.168.110.100的網(wǎng)絡(luò)包在返回給客戶端的時候,經(jīng)過NAT地址轉(zhuǎn)換,目標ip被改成了192.168.150.2,Windows收到之后不知道這個ip應(yīng)該發(fā)給誰了,導致三次握手的第二次回的包被windows丟棄了,沒有連接上。

關(guān)于為什么連接的速度并不快?(一秒鐘僅能夠建立4個連接左右)

為什么BIO慢?
創(chuàng)建一個連接的過程如下圖所示。

  • accept系統(tǒng)調(diào)用,是一個阻塞的循環(huán)過程,這個過程耗費時間。
  • 拋出一個線程的速度比較慢。

    這就是整個BIO的弊端。想要調(diào)優(yōu),就要解決阻塞的問題,但阻塞是由內(nèi)核提供給我們的API決accept receive定的。

NIO 的引入

NIO的N是啥意思呢?有兩個角度可以理解

  • Non-Blocking IO (操作系統(tǒng)中)
  • New IO (JDK中)

Linux中的文件描述符是輸入輸出雙向的。
Java中ServerSocketChannel也是淡化了輸入、輸出的概念,把輸入、輸出合在一起了。

一段NIO的代碼
SocketNIO.java

package com.bjmashibing.system.io;import java.net.InetSocketAddress; import java.net.StandardSocketOptions; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.LinkedList;public class SocketNIO {public static void main(String[] args) throws Exception {LinkedList<SocketChannel> clients = new LinkedList<>();ServerSocketChannel ss = ServerSocketChannel.open();ss.bind(new InetSocketAddress(9090));ss.configureBlocking(false); //設(shè)置false時,是非阻塞。OS層面的NONBLOCKING,不會阻塞地去等待連接。// ss.setOption(StandardSocketOptions.TCP_NODELAY, false); // StandardSocketOptions.TCP_NODELAY // StandardSocketOptions.SO_KEEPALIVE // StandardSocketOptions.SO_LINGER // StandardSocketOptions.SO_RCVBUF // StandardSocketOptions.SO_SNDBUF // StandardSocketOptions.SO_REUSEADDRwhile (true) {//接受客戶端的連接,不會阻塞Thread.sleep(1000);SocketChannel client = ss.accept(); //開始接收客戶端。不會阻塞等待連接,如果得不到連接,則返回 -1,NULL// ss.accept();方法調(diào)用內(nèi)核了,有下面這些情況:// 1,沒有客戶端連接進來,返回值是啥呢?在 BIO 的時候一直卡著,但是在NIO的時候不會卡著,返回的是-1,NULL// 2、有客戶端的連接,ss.accept();返回的是這個客戶端的fd 5(OS層面),Client對象(java層面)if (client == null) {System.out.println("null.....");// 可以不處理它,不要把這段當做性能消耗去思考。} else {client.configureBlocking(false); //重點// socket有兩個角度:// 1、服務(wù)端的listen socket<連接請求三次握手后,往我這里扔,我去通過accept,得到后面的連接的socket>// 2、服務(wù)端接受客戶端連接進來之后,形成的連接socket<連接后的數(shù)據(jù)讀寫使用的>int port = client.socket().getPort();System.out.println("client...port: " + port);clients.add(client);}//執(zhí)行讀取行為:遍歷已連接的客戶端去讀寫數(shù)據(jù),這個過程不會阻塞ByteBuffer buffer = ByteBuffer.allocateDirect(4096); //直接內(nèi)存分配,可以在堆里分配,也可以在堆外分配for (SocketChannel c : clients) { //串行化!!!! 多線程!!int num = c.read(buffer); // 返回值 >0 -1 0 不會阻塞if (num > 0) {buffer.flip();byte[] aaa = new byte[buffer.limit()];buffer.get(aaa);String b = new String(aaa);System.out.println(c.socket().getPort() + " : " + b);buffer.clear();}}}} }

運行起來

ss.configureBlocking(true);阻塞狀態(tài)下

ss.configureBlocking(false);非阻塞狀態(tài)下

非阻塞了,這有什么意義?
假設(shè)這時候有兩個客戶端連接進來了,其中一個的示例是下面這個樣子的:


曾經(jīng)我們是需要拋出一個線程,把這個線程扔出去,讓它自己去讀取數(shù)據(jù)。
現(xiàn)在不需要拋線程了,完全在一個線程中執(zhí)行,只不過是無數(shù)個循環(huán)。而在沒有連接的時候,循環(huán)也不會被阻塞,依然繼續(xù)循環(huán),從而讓循環(huán)后半部分的讀取數(shù)據(jù)的邏輯就有機會在當前循環(huán)當中被執(zhí)行到

再用C10K壓測一下

用C10K壓測一下NIO的性能(單客戶端10W連接):大約每秒鐘能建立50個左右的連接
現(xiàn)在使用NIO的瓶頸是:當連接進來很多客戶端時,for (SocketChannel c : clients)遍歷每一個客戶端去讀取數(shù)據(jù)的過程耗費了性能。


另外,報錯超出文件描述符的數(shù)量,這個是可以設(shè)置的:ulimit -SHn 500000(軟硬openfile,改成50萬)

注:為啥ulimit -n 1024,但是連接數(shù)超過了1024呢?
這個理論是對的,只不過要看用戶。權(quán)限對root來說等于虛設(shè),很多資源的約束在root用戶也是放開的。而且在公司里,生產(chǎn)環(huán)境肯定是非root用戶啟動程序。

超強干貨來襲 云風專訪:近40年碼齡,通宵達旦的技術(shù)人生

總結(jié)

以上是生活随笔為你收集整理的网络与IO知识扫盲(四):C10K问题、BIO的弊端与NIO的引入的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。