日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網為廣大站長提供免費收錄網站服務,提交前請做好本站友鏈:【 網站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(50元/站),

點擊這里在線咨詢客服
新站提交
  • 網站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會員:747

幾年前的一個下午,公司里碼農們正在安靜地敲著代碼,突然很多人的手機同時“嗶嗶”地響了起來。本來以為發工資了,都挺高興!打開一看,原來是告警短信。

重大事故!IO問題引發線上20臺機器同時崩潰

圖片來自 Pexels

故障回顧

告警提示“線程數過多,超出閾值”,“CPU 空閑率太低”。打開監控系統一看,訂單服務所有 20 個服務節點都不行了,服務沒響應。

查看監控(一個全鏈路性能監控工具),每個 Spring Boot 節點線程數全都達到了最大值。但是 JVM 堆內存和 GC 沒有明顯異常。

CPU 空閑率基本都是 0%,但是 CPU 使用率并不高,反而 IO 等待卻非常高。

下面是執行 top 命令查看 CPU 狀況的截圖:

重大事故!IO問題引發線上20臺機器同時崩潰

 

從上圖,我們可以看到:

  • CPU 空閑率是 0%(上圖中紅框 id)。
  • CPU 使用率是 22%(上圖中紅框 us 13% 加上 sy 9%,us 可以理解成用戶進程占用的 CPU,sy 可以理解成系統進程占用的 CPU)。
  • CPU 在等待磁盤 IO 操作上花費的時間占比是 76.6% (上圖中紅框 wa)。

到現在可以確定,問題肯定發生在 IO 等待上。利用監控系統和 jstack 命令,最終定位問題發生在文件寫入上。

大量的磁盤讀寫導致了系統線程資源耗盡,最終導致訂單服務無法響應上游服務的請求。

IO,你不知道的那些事兒

既然 IO 對系統性能和穩定性影響這么大,我們就來深入探究一下。

所謂的 I/O(Input/Output)操作實際上就是輸入輸出的數據傳輸行為。程序員最關注的主要是磁盤 IO 和網絡 IO,因為這兩個 IO 操作和應用程序的關系最直接最緊密。

磁盤 IO:磁盤的輸入輸出,比如磁盤和內存之間的數據傳輸;網絡 IO:不同系統間跨網絡的數據傳輸,比如兩個系統間的遠程接口調用。

下面這張圖展示了應用程序中發生 IO 的具體場景:

重大事故!IO問題引發線上20臺機器同時崩潰

 

通過上圖,我們可以了解到 IO 操作發生的具體場景。一個請求過程可能會發生很多次的 IO 操作:

  • 頁面請求到服務器會發生網絡 IO。
  • 服務之間遠程調用會發生網絡 IO。
  • 應用程序訪問數據庫會發生網絡 IO。
  • 數據庫查詢或者寫入數據會發生磁盤 IO。

IO 和 CPU 的關系

不少攻城獅會這樣理解,如果 CPU 空閑率是 0%,就代表 CPU 已經在滿負荷工作,沒精力再處理其他任務了。真是這樣的嗎?

我們先看一下計算機是怎么管理磁盤 IO 操作的。計算機發展早期,磁盤和內存的數據傳輸是由 CPU 控制的,也就是說從磁盤讀取數據到內存中,是需要 CPU 存儲和轉發的,期間 CPU 一直會被占用。

我們知道磁盤的讀寫速度遠遠比不上 CPU 的運轉速度。這樣在傳輸數據時就會占用大量 CPU 資源,造成 CPU 資源嚴重浪費。

后來有人設計了一個 IO 控制器,專門控制磁盤 IO。當發生磁盤和內存間的數據傳輸前,CPU 會給 IO 控制器發送指令,讓 IO 控制器負責數據傳輸操作,數據傳輸完 IO 控制器再通知 CPU。

因此,從磁盤讀取數據到內存的過程就不再需要 CPU 參與了,CPU 可以空出來處理其他事情,大大提高了 CPU 利用率。

這個 IO 控制器就是“DMA”,即直接內存訪問,Direct Memory Access。現在的計算機基本都采用這種 DMA 模式進行數據傳輸。

重大事故!IO問題引發線上20臺機器同時崩潰

 

通過上面內容我們了解到,IO 數據傳輸時,是不占用 CPU 的。

當應用進程或線程發生 IO 等待時,CPU 會及時釋放相應的時間片資源并把時間片分配給其他進程或線程使用,從而使 CPU 資源得到充分利用。

所以,假如 CPU 大部分消耗在 IO 等待(wa)上時,即便 CPU 空閑率(id)是 0%,也并不意味著 CPU 資源完全耗盡了,如果有新的任務來了,CPU 仍然有精力執行任務。

如下圖:

重大事故!IO問題引發線上20臺機器同時崩潰

 

在 DMA 模式下執行 IO 操作是不占用 CPU 的,所以 CPU IO 等待(上圖的wa)實際上屬于 CPU 空閑率的一部分。

所以我們執行 top 命令時,除了要關注 CPU 空閑率,CPU 使用率(us,sy),還要關注 IO Wait(wa)。注意,wa 只代表磁盤 IO Wait,不包括網絡 IO Wait。

JAVA 中線程狀態和 IO 的關系

當我們用 jstack 查看 Java 線程狀態時,會看到各種線程狀態。當發生 IO 等待時(比如遠程調用時),線程是什么狀態呢,Blocked 還是 Waiting?

答案是 Runnable 狀態,是不是有些出乎意料!實際上,在操作系統層面 Java 的 Runnable 狀態除了包括 Running 狀態,還包括 Ready(就緒狀態,等待 CPU 調度)和 IO Wait 等狀態。

重大事故!IO問題引發線上20臺機器同時崩潰

 

 

如上圖,Runnable 狀態的注解明確說明了,在 JVM 層面執行的線程,在操作系統層面可能在等待其他資源。

如果等待的資源是 CPU,在操作系統層面線程就是等待被 CPU 調度的 Ready 狀態;如果等待的資源是磁盤網卡等 IO 資源,在操作系統層面線程就是等待 IO 操作完成的 IO Wait 狀態。

有人可能會問,為什么 Java 線程沒有專門的 Running 狀態呢?

目前絕大部分主流操作系統都是以時間分片的方式對任務進行輪詢調度,時間片通常很短,大概幾十毫秒。

也就是說一個線程每次在 CPU 上只能執行幾十毫秒,然后就會被 CPU 調度出來變成 Ready 狀態,等待再一次被 CPU 執行,線程在 Ready 和 Running 兩個狀態間快速切換。

通常情況,JVM 線程狀態主要為了監控使用,是給人看的。當你看到線程狀態是 Running 的一瞬間,線程狀態早已經切換 N 次了。所以,再給線程專門加一個 Running 狀態也就沒什么意義了。

深入理解網絡 IO 模型

5 種 linux 網絡 IO 模型包括:

  • 同步阻塞 IO
  • 同步非阻塞 IO
  • 多路復用 IO
  • 信號驅動 IO
  • 異步 IO

為了更好地理解網絡 IO 模型,我們先了解幾個基本概念:

①Socket(套接字):Socket 可以理解成,在兩個應用程序進行網絡通信時,分別在兩個應用程序中的通信端點。

通信時,一個應用程序將數據寫入 Socket,然后通過網卡把數據發送到另外一個應用程序的 Socket 中。

我們平常所說的 HTTP 和 TCP 協議的遠程通信,底層都是基于 Socket 實現的。5 種網絡 IO 模型也都要基于 Socket 實現網絡通信。

②阻塞與非阻塞:所謂阻塞,就是發出一個請求不能立刻返回響應,要等所有的邏輯全處理完才能返回響應。

非阻塞反之,發出一個請求立刻返回應答,不用等處理完所有邏輯。

③內核空間與用戶空間:在 Linux 中,應用程序穩定性遠遠比不上操作系統程序,為了保證操作系統的穩定性,Linux 區分了內核空間和用戶空間。

可以這樣理解,內核空間運行操作系統程序和驅動程序,用戶空間運行應用程序。

Linux 以這種方式隔離了操作系統程序和應用程序,避免了應用程序影響到操作系統自身的穩定性。

這也是 Linux 系統超級穩定的主要原因。所有的系統資源操作都在內核空間進行,比如讀寫磁盤文件,內存分配和回收,網絡接口調用等。

所以在一次網絡 IO 讀取過程中,數據并不是直接從網卡讀取到用戶空間中的應用程序緩沖區,而是先從網卡拷貝到內核空間緩沖區,然后再從內核拷貝到用戶空間中的應用程序緩沖區。

對于網絡 IO 寫入過程,過程則相反,先將數據從用戶空間中的應用程序緩沖區拷貝到內核緩沖區,再從內核緩沖區把數據通過網卡發送出去。

同步阻塞 IO

我們先看一下傳統阻塞 IO。在 Linux 中,默認情況下所有 Socket 都是阻塞模式的。

當用戶線程調用系統函數 read(),內核開始準備數據(從網絡接收數據),內核準備數據完成后,數據從內核拷貝到用戶空間的應用程序緩沖區,數據拷貝完成后,請求才返回。

從發起 Read 請求到最終完成內核到應用程序的拷貝,整個過程都是阻塞的。為了提高性能,可以為每個連接都分配一個線程。

因此,在大量連接的場景下就需要大量的線程,會造成巨大的性能損耗,這也是傳統阻塞 IO 的最大缺陷。

重大事故!IO問題引發線上20臺機器同時崩潰

 

同步非阻塞 IO

用戶線程在發起 Read 請求后立即返回,不用等待內核準備數據的過程。如果 Read 請求沒讀取到數據,用戶線程會不斷輪詢發起 Read 請求,直到數據到達(內核準備好數據)后才停止輪詢。

非阻塞 IO 模型雖然避免了由于線程阻塞問題帶來的大量線程消耗,但是頻繁的重復輪詢大大增加了請求次數,對 CPU 消耗也比較明顯。這種模型在實際應用中很少使用。

重大事故!IO問題引發線上20臺機器同時崩潰

 

多路復用 IO 模型

多路復用 IO 模型,建立在多路事件分離函數 Select,Poll,Epoll 之上。

在發起 Read 請求前,先更新 Select 的 Socket 監控列表,然后等待 Select 函數返回(此過程是阻塞的,所以說多路復用 IO 也是阻塞 IO 模型)。

當某個 Socket 有數據到達時,Select 函數返回。此時用戶線程才正式發起 Read 請求,讀取并處理數據。

這種模式用一個專門的監視線程去檢查多個 Socket,如果某個 Socket 有數據到達就交給工作線程處理。

由于等待 Socket 數據到達過程非常耗時,所以這種方式解決了阻塞 IO 模型一個 Socket 連接就需要一個線程的問題,也不存在非阻塞 IO 模型忙輪詢帶來的 CPU 性能損耗的問題。

多路復用 IO 模型的實際應用場景很多,比如大家耳熟能詳的 Java NIO,redis 以及 Dubbo 采用的通信框架 Netty 都采用了這種模型。

重大事故!IO問題引發線上20臺機器同時崩潰

 

下圖是基于 Select 函數 Socket 編程的詳細流程:

重大事故!IO問題引發線上20臺機器同時崩潰

 

信號驅動 IO 模型

信號驅動 IO 模型,應用進程使用 Sigaction 函數,內核會立即返回,也就是說內核準備數據的階段應用進程是非阻塞的。

內核準備好數據后向應用進程發送 SIGIO 信號,接到信號后數據被復制到應用程序進程。

采用這種方式,CPU 的利用率很高。不過這種模式下,在大量 IO 操作的情況下可能造成信號隊列溢出導致信號丟失,造成災難性后果。

異步 IO 模型

異步 IO 模型的基本機制是,應用進程告訴內核啟動某個操作,內核操作完成后再通知應用進程。

在多路復用 IO 模型中,Socket 狀態事件到達,得到通知后,應用進程才開始自行讀取并處理數據。

在異步 IO 模型中,應用進程得到通知時,內核已經讀取完數據并把數據放到了應用進程的緩沖區中,此時應用進程直接使用數據即可。

很明顯,異步 IO 模型性能很高。不過到目前為止,異步 IO 和信號驅動 IO 模型應用并不多見,傳統阻塞 IO 和多路復用 IO 模型還是目前應用的主流。

Linux 2.6 版本后才引入異步 IO 模型,目前很多系統對異步 IO 模型支持尚不成熟。很多應用場景采用多路復用 IO 替代異步 IO 模型。

如何避免 IO 問題帶來的系統故障

對于磁盤文件訪問的操作,可以采用線程池方式,并設置線程上線,從而避免整個 JVM 線程池污染,進而導致線程和 CPU 資源耗盡。

對于網絡間遠程調用。為了避免服務間調用的全鏈路故障,要設置合理的 TImeout 值,高并發場景下可以采用熔斷機制。

在同一 JVM 內部采用線程隔離機制,把線程分為若干組,不同的線程組分別服務于不同的類和方法,避免因為一個小功能點的故障,導致 JVM 內部所有線程受到影響。

此外,完善的運維監控(磁盤 IO,網絡 IO)和 APM(全鏈路性能監控)也非常重要,能及時預警,防患于未然,在故障發生時也能幫助我們快速定位問題。

作者:二馬讀書

簡介:曾任職于阿里巴巴,每日優鮮等互聯網公司,任技術總監,15 年電商互聯網經歷。

編輯:陶家龍

出處:架構師進階之路(ID:ermadushu)

分享到:
標簽:IO
用戶無頭像

網友整理

注冊時間:

網站:5 個   小程序:0 個  文章:12 篇

  • 51998

    網站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會員

趕快注冊賬號,推廣您的網站吧!
最新入駐小程序

數獨大挑戰2018-06-03

數獨一種數學游戲,玩家需要根據9

答題星2018-06-03

您可以通過答題星輕松地創建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學四六

運動步數有氧達人2018-06-03

記錄運動步數,積累氧氣值。還可偷

每日養生app2018-06-03

每日養生,天天健康

體育訓練成績評定2018-06-03

通用課目體育訓練成績評定