Java性能之優化RPC網絡通信

服務框架的核心

  1. 大型服務框架的核心:RPC通信
  2. 微服務的核心是遠程通信服務治理
  • 遠程通信提供了服務之間通信的橋樑,服務治理提供了服務的後勤保障
  1. 服務的拆分增加了通信的成本,因此遠程通信很容易成為系統瓶頸
  • 在滿足一定的服務治理需求的前提下,對遠程通信的性能需求是技術選型的主要影響因素
  1. 很多微服務框架中的服務通信是基於RPC通信實現的
  • 在沒有進行組件擴展的前提下,Spring Cloud是基於Feign組件實現RPC通信(基於HTTP+JSON序列化)
  • Dubbo是基於SPI擴展了很多RPC通信框架,包括RMI、Dubbo、Hessian等(默認為Dubbo+Hessian序列化)


性能測試

基於Dubbo:2.6.4,單一TCP長連接+Protobuf(響應時間和吞吐量更優),短連接的HTTP+JSON序列化

Java性能之優化RPC網絡通信

Java性能之優化RPC網絡通信

RPC通信

架構演化

無論是微服務、SOA、還是RPC架構,都是分佈式服務架構,都需要實現服務之間的互相通信,通常把這種通信統稱為RPC通信

Java性能之優化RPC網絡通信

概念

  1. RPC:Remote Process Call,遠程服務調用,通過網絡請求遠程計算機程序服務的通信技術
  2. RPC框架封裝了底層網絡通信序列化等技術
  • 只需要在項目中引入各個服務的接口包,就可以在代碼中調用RPC服務(如同調用本地方法一樣)

RMI

  1. RMI:Remote Method Invocation
  2. RMI是JDK自帶的RPC通信框架,已經成熟地應用於EJBSpring,是
    純Java網絡分佈式應用系統的核心解決方案
  3. RMI實現了一臺虛擬機應用對遠程方法的調用可以同對本地方法調用一樣,RMI封裝好了遠程通信的具體細節

實現原理

Java性能之優化RPC網絡通信

  1. RMI遠程代理對象是RMI中
    最核心的組件,除了對象本身所在的虛擬機,其他虛擬機也可以調用此對象的方法
  2. 這些虛擬機可以分佈在不同的主機上,通過遠程代理對象,遠程應用可以用網絡協議和服務進行通信

高併發下的性能瓶頸

  1. Java默認序列化
  • RMI的序列化方式採用的是Java默認序列化,性能不好,而且不支持跨語言
  1. TCP短連接
  • RMI是基於TCP短連接實現的,在高併發情況下,大量請求會帶來大量TCP連接的
    創建銷燬非常消耗性能
  1. 阻塞式網絡IO
  • Socket編程中使用傳統的IO模型,在高併發場景下基於短連接實現的網絡通信就很容易產生IO阻塞性能將大打折扣

優化路徑

TCP / UDP

  1. 網絡傳輸協議有TCPUDP,兩個協議都是基於Socket編程
  2. 基於TCP協議實現的Socket通信是
    有連接
  • 傳輸數據要通過三次握手來實現數據傳輸的可靠性,而傳輸數據是沒有邊界的,採用的是字節流模式
  1. 基於UDP協議實現的Socket通信,客戶端不需要建立連接,只需要創建一個套接字發送數據給服務端
  • 基於UDP協議實現的Socket通信具有不可靠性
  • UDP發送的數據採用的是數據報模式,每個UDP的數據報都有一個長度,該長度與數據一起發送到服務端
  1. 為了保證數據傳輸的可靠性,通常情況下會採用
    TCP協議
  • 在局域網且對數據傳輸的可靠性沒有要求的情況下,可以考慮使用UDP協議,UDP協議的效率比TCP協議高
Java性能之優化RPC網絡通信

長連接

  1. 服務之間的通信不同於客戶端與服務端之間的通信
  2. 由於客戶端數量眾多,基於短連接實現請求,可以避免長時間地佔用連接,導致系統資源浪費
  3. 服務之間的通信,連接的消費端不會像客戶端那麼多,但消費端向服務端請求的數量卻一樣多
  • 基於長連接實現,可以省去大量建立TCP連接和關閉TCP連接的操作,從而減少系統的性能消耗,節省時間


優化Socket通信

  1. 傳統的Socket通信主要存在IO阻塞,線程模型缺陷以及內存拷貝等問題,Netty4對Socket通信編程做了很多方面的優化
  2. 實現非阻塞IO:多路複用器Selector實現了非阻塞IO通信
  3. 高效的Reactor線程模型
  • Netty使用了主從Reactor多線程模型
  • 主線程:用於客戶端的連接請求操作,一旦連接建立成功,將會監聽IO事件,監聽到事件後會創建一個鏈路請求
  • 鏈路請求將會註冊到負責IO操作的IO工作線程上,由IO工作線程負責後續的IO操作
  • Reactor線程模型解決了在高併發的情況下,由於單個NIO線程無法監聽海量客戶端和滿足大量IO操作造成的問題

4.串行設計

  • 服務端在接收消息之後,存在著編碼、解碼、讀取和發送等鏈路操作
  • 如果這些操作基於並行實現,無疑會導致嚴重的鎖競爭,進而導致系統的性能下降
  • 為了提升性能,Netty採用串行無鎖化完成鏈路操作,提供了Pipeline,實現鏈路的各個操作在運行期間不會切換線程

5.零拷貝

  • 數據從內存發到網絡中,存在兩次拷貝,先是從用戶空間拷貝到
    內核空間,再從內核空間拷貝到網絡IO
  • NIO提供的ByteBuffer可以使用Direct Buffer模式
  • 直接開闢一個非堆物理內存,不需要進行字節緩衝區的二次拷貝,可以直接將數據寫入到內核空間

6.優化TCP參數配置,提高網絡吞吐量,Netty可以基於ChannelOption來設置

  • TCP_NODELAY:用於控制是否開啟Nagle算法
  • Nagle算法通過緩存的方式將小的數據包組成一個大的數據包,從而避免大量發送小的數據包
    ,導致網絡阻塞
  • 在對時延敏感的應用場景,可以選擇關閉該算法
  • SO_RCVBUF / SO_SNDBUF:Socket接收緩衝區發送緩衝區的大小
  • SO_BACKLOG:指定客戶端連接請求緩衝隊列的大小
  • 服務端處理客戶端連接請求按順序處理的,同一時間只能處理一個客戶端連接
  • 當有多個客戶端進來的時候,服務端將不能處理的客戶端連接請求放在隊列中等待處理
  • SO_KEEPALIVE
  • 連接會檢查長時間沒有發送數據的客戶端的連接狀態,檢測到客戶端斷開連接後,服務端將回收該連接
  • 將該值設置得小一些,可以提高回收連接的效率


定製報文格式

  1. 設計一套報文,用於描述具體的校驗、操作、傳輸數據等內容
  2. 為了提高傳輸效率,可以根據實際情況來設計,儘量實現報體小,滿足功能,易解析等特性

字段長度(字節)備註魔數4協議的標識,類似於字節碼的魔數,通常為固定數字版本號1序列化算法1Protobuf / Thrift指令1類似於HTTP中的增刪改查數據長度4數據N

編解碼

  1. 實現一個通信協議,需要兼容優秀的序列化框架
  2. 如果只是單純的數據對象傳輸,可以選擇性能相對較好的Protobuf序列化,有利於提高網絡通信的性能

Linux的TCP參數設置

三次握手

Java性能之優化RPC網絡通信

四次揮手

Java性能之優化RPC網絡通信

配置項

1.fs.file-max = 194448 / ulimit

2.net.ipv4.tcp_keepalive_time

3.net.ipv4.tcp_max_syn_backlog

4.net.ipv4.ip_local_port_range

5.net.ipv4.tcp_max_tw_buckets

6.net.ipv4.tcp_tw_reuse

備註

1.Linux默認單個進程可以打開的文件數量上限為1024,Socket也是文件

2.與Netty的SO_KEEPALIVE配置項的作用一致

3.SYN隊列的長度,加大隊列長度,可以容納更多等待連接的網絡連接數

4.客戶端連接服務器時,需要動態分配源端口號,該配置項表示向外連接的端口範圍

5. 當一個連接關閉時,TCP會通過四次揮手來完成一次關閉連接操作,在請求量比較大的情況下,消費端會有大量TIME_WAIT狀態的連接,該參數可以限制TIME_WAIT狀態的連接數量,如果TIME_WAIT的連接數量超過該值,TIME_WAIT將會立即被清除掉並打印警告信息

6.客戶端每次連接服務器時,都會獲得一個新的源端口以實現連接的唯一性,在TIME_WAIT狀態的連接數量過大的情況下,會增加端口號的佔用時間,由於處於TIME_WAIT狀態的連接屬於關閉連接,所以新創建的連接可以複用該端口號

最後

又到了我們最喜歡的資料分享環節了

Java性能之優化RPC網絡通信

Java性能之優化RPC網絡通信

快來關注我吧!

關注+轉發,私信我“學習”領取吧!

我是小架,我們下篇文章再見!


分享到:


相關文章: