自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

阿里Java二面:從底層聊下IO多路復(fù)用模型?這不有嘴就會(huì)

開(kāi)發(fā) 前端
當(dāng)我們?nèi)ッ嬖嚨臅r(shí)候,問(wèn)到了 redis,nginx,netty他們的底層模型分別是什么?redis -> epoll,nginx-> epoll,netty-> epoll?

前言

當(dāng)我們?nèi)ッ嬖嚨臅r(shí)候,問(wèn)到了 redis,nginx,netty他們的底層模型分別是什么?

  • redis -> epoll
  • nginx-> epoll
  • netty-> epoll?

需要從操作系統(tǒng)的層面上來(lái)談

BIO

當(dāng)我們開(kāi)機(jī)的時(shí)候,首先被加載進(jìn)內(nèi)存的是我們的Kernel(內(nèi)核),內(nèi)核是用于管理我們的硬件的,同時(shí)內(nèi)核還會(huì)創(chuàng)建一個(gè)GDT表,然后劃分兩個(gè)空間(用戶空間和內(nèi)核空間),同時(shí)空間中的內(nèi)容是開(kāi)啟了保護(hù)模式,無(wú)法被修改的。

同時(shí)還有一個(gè)CPU的概念,CPU有自己的指令集,并且指令集是分了幾個(gè)級(jí)別的,分別是從0~3的,Kernel屬于0級(jí)別。APP只能用級(jí)別為3的指令集。

從上面我們可以知道,我們的應(yīng)用程序是無(wú)法直接訪問(wèn)我們的Kernel的,也就是程序不能直接訪問(wèn)我們的磁盤(pán),聲卡,網(wǎng)卡等設(shè)備,只有內(nèi)核才可以訪問(wèn),那我們?cè)趺崔k?

只有APP通過(guò)調(diào)用Kernel提供的 syscall(系統(tǒng)軟中斷和硬中斷)來(lái)獲取硬件中的內(nèi)容。

軟中斷

硬中斷:硬中斷指的是我們的鍵盤(pán),按下一個(gè)按鍵的時(shí)候,就會(huì)觸發(fā)我們的硬中斷,也就是內(nèi)核會(huì)有一個(gè)中斷號(hào),然后得到一個(gè)callback的回調(diào)函數(shù)

說(shuō)到這里,其實(shí)就是為了引出一個(gè) 概念,就是 IO 和 內(nèi)核之間的成本問(wèn)題

  1. /** 
  2.  * 服務(wù)器讀取文件 
  3.  * @author: 陌溪 
  4.  * @create: 2020-07-01-20:40 
  5.  */ 
  6. public class TestSocket { 
  7.     public static void main(String[] args) throws IOException { 
  8.         ServerSocket server = new ServerSocket(8090); 
  9.         System.out.println("step1: new ServerSocket(8090)"); 
  10.         while(true) { 
  11.             Socket client = server.accept(); 
  12.             System.out.println("step2: client " + client.getPort()); 
  13.             new Thread(() -> { 
  14.                 try { 
  15.                     InputStream in = client.getInputStream(); 
  16.                     BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 
  17.                     while(true) { 
  18.                         System.out.println(reader.readLine()); 
  19.                     } 
  20.                 } catch (IOException e) { 
  21.                     e.printStackTrace(); 
  22.                 } 
  23.             }, "t1").start(); 
  24.         } 
  25.     } 

抓取程序?qū)?nèi)核有沒(méi)有系統(tǒng)調(diào)用,然后輸出

  1. strace -ff -o ./ooxx java TestSocket 

然后我們執(zhí)行上面的程序,得到我們的結(jié)果

然后我們?cè)谕ㄟ^(guò)jps命令,查看當(dāng)前TestSocket的進(jìn)程號(hào)

 

  1. jps  
  2. 2912 Jps  
  3. 2878 TestSocket 

然后我們?cè)谶M(jìn)入下面的這個(gè)目錄下,啟動(dòng)2878是線程的id號(hào),這個(gè)目錄就是存放該線程的一些信息

  1. cd /proc/2878 

我們可以看到2878進(jìn)程下的,通過(guò)查看task目錄,可以看到所有線程數(shù)

還有一個(gè)目錄,就是 fd目錄,在該目錄下,就是我們的一些IO流

上面的0,1,2,分別對(duì)應(yīng)著 輸入流,輸出流和錯(cuò)誤流。在java里面我們流就是對(duì)象,而在linux系統(tǒng)中,流就是一個(gè)個(gè)的文件。后面的4,5 就對(duì)應(yīng)著我們的socket通信,分別對(duì)應(yīng)著ipv4 和 ipv6

通過(guò)netstat命令查看

然后我們使用nc連接 8090端口

  1. nc localhost 8090 

我們執(zhí)行完后,通過(guò)netstat命令查看 ,發(fā)現(xiàn)多了個(gè)連接的狀態(tài)

然后在看文件里面,也多了一個(gè)socket

我們查看系統(tǒng)調(diào)用,發(fā)現(xiàn)通過(guò)系統(tǒng)調(diào)用接收了一個(gè)58181端口號(hào)的請(qǐng)求,在前面我們還能夠看到5,這個(gè)5其實(shí)就是對(duì)應(yīng)的上圖里面的socket,走的是ipv4。

從這里其實(shí)我們就可以知道了,我們?cè)瓉?lái)調(diào)用中寫(xiě)的代碼

  1. Socket client = server.accept(); 

對(duì)應(yīng)到系統(tǒng)層面,也是調(diào)用了系統(tǒng)的方法。

同時(shí)關(guān)于系統(tǒng)調(diào)用,有以下幾種方式

  • bind
  • connect
  • listen
  • select
  • socket

首先我們需要知道,java其實(shí)是一種解釋型語(yǔ)言,通過(guò)JVM 虛擬機(jī)將我們的.java文件轉(zhuǎn)換為字節(jié)碼文件,然后調(diào)用我們os中的syscall方法,我們必須明確的是,無(wú)論怎么調(diào)用,一定最后要通過(guò)調(diào)用內(nèi)核的方法,然后調(diào)用我們的硬件。

上述的模型,就是BIO的通信,是這里面有很多阻塞,我們只能夠通過(guò)多個(gè)線程來(lái)避免主線程的阻塞。但是從上面我們可以知道,如果有大量地連接過(guò)來(lái),那服務(wù)器需要?jiǎng)?chuàng)建很多個(gè)線程與之對(duì)應(yīng),并且線程的創(chuàng)建也是需要消耗資源的,因?yàn)榫€程使用的棧是獨(dú)占的(棧大小默認(rèn)1MB),同時(shí)CPU的資源調(diào)度也是需要浪費(fèi)。

最根本的原因就是因?yàn)?BIO是阻塞的,才會(huì)造成上面的問(wèn)題。

NIO

因?yàn)锽IO存在線程阻塞的問(wèn)題,后面就提出了NIO的概念,在NIO中,有C10K的問(wèn)題,C10K = 10000個(gè)客戶端。但是在和你連接的服務(wù)器中,其實(shí)沒(méi)有多少給你發(fā)送數(shù)據(jù)了,所以我們需要做的就是,每當(dāng)有人發(fā)送消息的時(shí)候,我才和它進(jìn)行連接。

也就是每次都需要遍歷10000個(gè)客戶端,是非常耗費(fèi)時(shí)間呢,因?yàn)楹芏嗫蛻舳丝赡芫蜎](méi)有請(qǐng)求的發(fā)送。

多路復(fù)用

這個(gè)時(shí)候,我們就不需要遍歷10K個(gè)客戶端了,而是把我們的fds文件發(fā)送給內(nèi)核,然后內(nèi)核去判斷最后需要連接誒的客戶端,這樣就不用遍歷全部的了。所以這里的Select就是多路復(fù)用器,通過(guò)多路復(fù)用返回的是狀態(tài),然后我們需要程序去判斷這些狀態(tài)。

說(shuō)白了,就是通過(guò)一個(gè)多路復(fù)用器,來(lái)判斷哪些路可以走通,然后不需要輪詢?nèi)康摹?/p>

 

這個(gè)模型,是通過(guò)select,將fds文件交給內(nèi)核來(lái)做了,也就是內(nèi)核需要完成10K個(gè)文件的主動(dòng)遍歷,這個(gè)10K個(gè)調(diào)用,對(duì)比之前的10K次系統(tǒng)調(diào)用來(lái)說(shuō),是更省時(shí)間的,存在以下的問(wèn)題

  • 每次傳遞很多數(shù)據(jù)(重復(fù)勞動(dòng))
  • 然后內(nèi)核需要主動(dòng)去遍歷( 復(fù)雜度O(N) )

解決方法,通過(guò)在內(nèi)核中,開(kāi)辟一個(gè)空間,當(dāng)每次來(lái)一個(gè)客戶端,就把這個(gè)文件丟到內(nèi)核中,這樣不需要每次把10K個(gè)文件傳遞到內(nèi)核了。然后在使用一個(gè)基于事件驅(qū)動(dòng)的模型,如下圖所示就是一個(gè)異步事件驅(qū)動(dòng)的流程

同樣使用epoll,Redis是輪詢,Nginx是阻塞?

我們通過(guò)strace命令,查看nginx 和 redis的運(yùn)行流程,能夠發(fā)現(xiàn) 同樣是使用了 epoll,但是nginx是阻塞的,而redis它是輪詢(非阻塞)的。

首先那是因?yàn)镽edis只有一個(gè)線程,而這個(gè)線程要做很多事情,例如 接收客戶端,LRU,LFU(淘汰過(guò)濾)、RDB/AOF(fork線程進(jìn)行數(shù)據(jù)備份)。

也就是說(shuō)對(duì)于Redis中的C10K問(wèn)題,redis也是通過(guò)epoll的事件驅(qū)動(dòng)來(lái)進(jìn)行處理的,也就是通過(guò)epoll將每個(gè)需要讀取的客戶端的操作放在一個(gè)原子串行化的隊(duì)列中,并且一個(gè)客戶端包含以下的幾個(gè)操作:read、計(jì)算、write等

在redis 6.X版本中,還有一個(gè)IO threads的概念,首先它為了留住串行化原子性的特點(diǎn),也就是計(jì)算的時(shí)候還是串行化的處理,但是在讀取數(shù)據(jù)的時(shí)候,使用的是多線程進(jìn)行并發(fā)IO讀取。為什么要多線程讀呢?首先因?yàn)樽x操作需要發(fā)生CPU的系統(tǒng)調(diào)用,如果通過(guò)多個(gè)線程讀取,能夠充分發(fā)揮CPU的多核作用

而nginx只需要做一件事,就是等著客戶端過(guò)來(lái),不需要做其他的事情,所以也就設(shè)置成阻塞。

零拷貝

用kafka來(lái)講,首先這里面有兩個(gè)角色,一個(gè)是消息生產(chǎn)者,一個(gè)是消息消費(fèi)者

也就是說(shuō),我們通過(guò)開(kāi)辟了一個(gè)內(nèi)存空間,能夠直接抵達(dá)磁盤(pán),能夠減少kernel的系統(tǒng)調(diào)用。在讀取的時(shí)候,如果是原來(lái)的做法,就需要首先請(qǐng)求kernel,然后kernel發(fā)起一個(gè)read請(qǐng)求,讀取磁盤(pán)的文件到內(nèi)核中,然后kafka在讀取kernel中的信息。

那么什么是零拷貝呢?

零拷貝就是不發(fā)生拷貝的情況,零拷貝的前提就是數(shù)據(jù)不需要加工,在JVM中有一個(gè)RandomAccessFile,它能夠直接開(kāi)辟一個(gè)堆內(nèi)空間,或者堆外空間。

責(zé)任編輯:未麗燕 來(lái)源: 今日頭條
相關(guān)推薦

2023-01-09 10:04:47

IO多路復(fù)用模型

2020-10-13 07:51:03

五種IO模型

2021-05-31 06:50:47

SelectPoll系統(tǒng)

2020-10-14 09:11:44

IO 多路復(fù)用實(shí)現(xiàn)機(jī)

2024-08-08 14:57:32

2023-11-07 08:19:35

IO多路復(fù)用磁盤(pán)、

2023-12-13 09:45:49

模型程序

2022-08-26 00:21:44

IO模型線程

2011-12-08 10:51:25

JavaNIO

2022-09-12 06:33:15

Select多路復(fù)用

2025-04-24 10:05:51

2023-03-01 14:32:31

redisIOEpoll

2024-09-26 16:01:52

2021-03-24 08:03:38

NettyJava NIO網(wǎng)絡(luò)技術(shù)

2009-06-29 18:09:12

多路復(fù)用Oracle

2022-01-06 14:45:10

數(shù)據(jù)庫(kù)連接池IO

2021-02-10 08:09:48

Netty網(wǎng)絡(luò)多路復(fù)用

2023-05-08 00:06:45

Go語(yǔ)言機(jī)制

2023-12-06 07:16:31

Go語(yǔ)言語(yǔ)句

2020-11-19 09:35:56

Linuxscreen命令
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)