网络通信优化
如何優雅的談論HTTP/1.0/1.1/2.0如何優雅的談論HTTP/1.0/1.1/2.0
現在測試工具非常多,包括阿里云的 PTS 測試工具也很好用,但每款測試工具其實都有自
己的優缺點。個人建議,還是在熟練掌握其中一款測試工具的前提下,再去探索其他測試工
具的使用方法會更好
網絡通信優化之I/O模型:如何解決高并發下I/O瓶頸?
問題:什么是 I/O???
I/O 是機器獲取和交換信息的主要渠道,而流是完成 I/O 操作的主要方式。
在計算機中,流是一種信息的轉換。流是有序的,因此相對于某一機器或者應用程序而言,我們通常
把機器或者應用程序接收外界的信息稱為輸入流(InputStream),
從機器或者應程序向外輸出的信息稱為輸出流(OutputStream),
合稱為輸入 / 輸出流(I/OStreams)。
機器間或程序間在進行信息交換或者數據交換時,總是先將對象或數據轉換為某種形式的流,再通過流的傳輸,到達指定機器或程序后,再將流轉換為對象數據。因此,流就可以被看作是一種數據的載體,通過它可以實現數據交換和傳輸。
回顧我的經歷,我記得在初次閱讀 Java I/O 流文檔的時候,我有過這樣一個疑問,在這里也分享給你,那就是:“不管是文件讀寫還是網絡發送接收,信息的最小存儲單元都是字節,那為什么 I/O 流操作要分為字節流操作和字符流操作呢?”
“不管是文件讀寫還是網絡發送接收,信息的最小存儲單元都是字節,那為什么 I/O 流操作要分為字節流操作和字符流操作呢?”
回答:我們知道字符到字節必須經過轉碼,這個過程非常耗時,如果我們不知道編碼類型就很容易出現亂碼問題。所以 I/O 流提供了一個直接操作字符的接口,方便我們平時對字符進行流操作。
I/O 操作分為磁盤 I/O 操作和網絡 I/O 操作。
前者是從磁盤中讀取數據源輸入到內存中,之后將讀取的信息持久化輸出在物理磁盤上;
后者是從網絡中讀取信息輸入到內存,最終將信息輸出到網絡中。
但不管是磁盤 I/O 還是網絡 I/O,在傳統 I/O 中都存在嚴重的性能問題
JVM 會發出 read() 系統調用,并通過 read 系統調用向內核發起讀請求;
內核向硬件發送讀指令,并等待讀就緒;
內核把將要讀取的數據復制到指向的內核緩存中;
操作系統內核將數據復制到用戶空間緩沖區,然后 read 系統調用返回。
1. 多次內存復制
在這個過程中,數據先從外部設備復制到內核空間,再從內核空間復制到用戶空間,這就發
生了兩次內存復制操作。這種操作會導致不必要的數據拷貝和上下文切換,從而降低 I/O
的性能。
2. 阻塞
在傳統 I/O 中,InputStream 的 read() 是一個 while 循環操作,它會一直等待數據讀取,
直到數據就緒才會返回。這就意味著如果沒有數據就緒,這個讀取操作將會一直被掛起,用
戶線程將會處于阻塞狀態。
在少量連接請求的情況下,使用這種方式沒有問題,響應速度也很高。但在發生大量連接請
求時,就需要創建大量監聽線程,這時如果線程沒有數據就緒就會被掛起,然后進入阻塞狀
態。一旦發生線程阻塞,這些線程將會不斷地搶奪 CPU 資源,從而導致大量的 CPU 上下
文切換,增加系統的性能開銷
傳統 I/O 和 NIO 的最大區別就是傳統 I/O 是面向流,NIO 是面向 Buffer。Buffer 可以將
文件一次性讀入內存再做后續處理,而傳統的方式是邊讀文件邊處理數據。雖然傳統 I/O
后面也使用了緩沖塊,例如 BufferedInputStream,但仍然不能和 NIO 相媲美。使用
NIO 替代傳統 I/O 操作,可以提升系統的整體性能,效果立竿見影。
使用 DirectBuffer 減少內存復制
提到了DirectBuffer,也就是零拷貝的實現
用DirectBuffer減少內存復制,也就是避免了用戶空間與內核空
間來回復制。
NIO 的 Buffer 除了做了緩沖塊優化之外,還提供了一個可以直接訪問物理內存的類
DirectBuffer。普通的 Buffer 分配的是 JVM 堆內存,而 DirectBuffer 是直接分配物理內
存。
我們知道數據要輸出到外部設備,必須先從用戶空間復制到內核空間,再復制到輸出設備,
而 DirectBuffer 則是直接將步驟簡化為從內核空間復制到外部設備,減少了數據拷貝。
這里拓展一點,由于 DirectBuffer 申請的是非 JVM 的物理內存,所以創建和銷毀的代價很
高。DirectBuffer 申請的內存并不是直接由 JVM 負責垃圾回收,但在 DirectBuffer 包裝
類被回收時,會通過 Java Reference 機制來釋放該內存塊。
一個線程使用一個 Selector,通過輪詢的方式,可以監聽多個 Channel 上的事件。我們可
以在注冊 Channel 時設置該通道為非阻塞,當 Channel 上沒有 I/O 操作時,該線程就不
會一直等待了,而是會不斷輪詢所有 Channel,從而避免發生阻塞。
目前操作系統的 I/O 多路復用機制都使用了 epoll,相比傳統的 select 機制,epoll 沒有最
大連接句柄 1024 的限制。所以 Selector 在理論上可以輪詢成千上萬的客戶端。
下面我用一個生活化的場景來舉例,看完你就更清楚 Channel 和 Selector 在非阻塞 I/O
中承擔什么角色,發揮什么作用了。
我們可以把監聽多個 I/O 連接請求比作一個火車站的進站口。以前檢票只能讓搭乘就近一
趟發車的旅客提前進站,而且只有一個檢票員,這時如果有其他車次的旅客要進站,就只能
在站口排隊。這就相當于最早沒有實現線程池的 I/O 操作。
后來火車站升級了,多了幾個檢票入口,允許不同車次的旅客從各自對應的檢票入口進站。這就相當于用多線程創建了多個監聽線程,同時監聽各個客戶端的 I/O 請求。
最后火車站進行了升級改造,可以容納更多旅客了,每個車次載客更多了,而且車次也安排合理,乘客不再扎堆排隊,可以從一個大的統一的檢票口進站了,這一個檢票口可以同時檢票多個車次。這個大的檢票口就相當于 Selector,車次就相當于 Channel,旅客就相當于I/O 流。
在Linux中,AIO并未真正使用操作系統所提供的異步I/O,它仍然使用poll或epoll,并將
API封裝為異步I/O的樣子,但是其本質仍然是同步非阻塞I/O,加上第三方產品的出現,
Java網絡編程明顯落后,所以沒有成為主流。
DMA和Channel的區別, DMA需要占用總線, 那么Channel是如何跳過總線向內存傳輸數據的????
回復: 一個設備接口試圖通過總線直接向外部設備(磁盤)傳送數據時,它會先向CPU發送DMA請求信號。外部設備(磁盤)通過DMA的一種專門接口電路――DMA控制器(DMAC),向CPU提出接管總線控制權的總線請求,CPU收到該信號后,在當前的總線周期結束后,會按DMA信號的優先級和提出DMA請求的先后順序響應DMA信號。CPU對某個設備接口響應DMA請求時,會讓出總線控制權。于是在DMA控制器的管理下,磁盤和存儲器直接進行數據交換,而不需CPU干預。數據傳送完畢后,設備接口會向CPU發送DMA結束信號,交還總線控制權。
而通道則是在DMA的基礎上增加了能執行有限通道指令的I/O控制器,代替CPU管理控制外設。通道有自己的指令系統,是一個協處理器,他實質是一臺能夠執行有限的輸入輸出指令,并且有專門通訊傳輸的通道總線完成控制。
網絡通信優化之序列化:避免使用Java序列化
這個編碼和解碼過程我們稱之為序列化與反序列化
Java 序列化的缺陷
目前業內優秀的序列化框架有很多,而且大部分都避免了 Java 默認序列化的一些缺陷。例
如,最近幾年比較流行的 FastJson、Kryo、Protobuf、Hessian 等。我們完全可以找一種
替換掉 Java 序列化,這里我推薦使用 Protobuf 序列化框架
網絡通信優化之通信協議:如何優化RPC網絡通信?
我們知道服務的拆分增加了通信的成本,特別是在一些搶購或者促銷的業務場景中,如果服
務之間存在方法調用,比如,搶購成功之后需要調用訂單系統、支付系統、券包系統等,這
種遠程通信就很容易成為系統的瓶頸。所以,在滿足一定的服務治理需求的前提下,對遠程
通信的性能需求就是技術選型的主要影響因素。
SpringCloud 是基于 Feign 組件實現的 RPC 通信(基于 Http+Json 序列化實現),
Dubbo 是基于 SPI 擴展了很多 RPC 通信框架,包括 RMI、Dubbo、Hessian 等 RPC 通
信框架(默認是 Dubbo+Hessian 序列化)
RPC(Remote Process Call),即遠程服務調用,是通過網絡請求遠程計算機程序服務的
通信技術。RPC 框架封裝好了底層網絡通信、序列化等技術,我們只需要在項目中引入各
個服務的接口包,就可以實現在代碼中調用 RPC 服務同調用本地方法一樣
總結
- 上一篇: delphi mysql.pas_Del
- 下一篇: BPE, WordPiece, Sent