分佈式網絡基礎

在分佈式服務化架構設計中,服務與服務之間通信均是基於網絡底層協議來實現的,於是我們需要對網絡相關基礎知識有一個基本的認知,這樣在我們服務與服務之間進行通信(跨進程通信)過程能夠在我們的腦圖形成一個基本的數據傳輸流程以及其中的細節問題,這樣對於我們在進行網絡問題的排查能夠帶來一定的幫助.現在開始展開網絡基礎相關知識的闡述.

網絡基礎知識

通信協議

什麼是協議

協議是計算機與計算機之間通過網絡通信時事先達成的一種“約定”,這種“約定”使那些由不同的廠商設備,不同的CPU以及不同的操作系統組成的計算機之間,只要遵循相同的協議就能夠在網絡傳輸中實現數據交互的通信.

分組交換協議

分組交換

在網絡傳輸過程中,如果傳輸的數據塊很大,則需要將其切成多個以包爲單位的數據塊進行傳輸,而這種將數據分裝爲一個個以包爲單元的數據塊稱爲分組.

網絡數據包

數據包(報文)由控制信息(稱爲報文首部抑或是header)以及用戶數據(也稱爲有效負載payload),控制信息包含有效負載的傳輸信息,即數據包的源IP地址/目標IP地址/分組之後的序號以便於在接收端的目標IP機器能夠根據序號進行拼接組成一個與源數據包一致的數據塊.

一個完整的數據包組成的結構如下:

  • 網絡數據包的路由地址:包含發送數據包的源地址以及接收數據包的目標地址
  • 錯誤檢測與糾正: 在網絡協議中執行錯誤檢測與糾正,爲避免在傳輸過程中發生錯誤,需要在網絡數據包進行數據校驗,奇偶檢驗位或者循環冗餘校驗.
  • 跳數限制:主要是作用是告訴網絡路由器數據包在網絡中的時間是否超過了TTL設定的一個時間段,如果超過將會被丟棄並向發送端發起一個數據丟棄的一個報文由發送端決定是否進行重發.由於每個路由器都至少要把TTL域減一,TTL通常表示包在被丟棄前最多能經過的路由器個數.當記數到0時,路由器決定丟棄該包,併發送一個ICMP報文給最初的發送者.
  • 數據包的長度
  • 處理數據包的優先級
  • 有效的負載,即數據包實際的數據,除了當前的結構,上述的字段信息可以在header中聲明和定義.

分組交換協議

爲了保證發送端與接收端能夠正確進行通信,分組的兩端必須保持報文首部和內容保持一致的約定,即房租交換協議.

協議棧與網絡分層模型

協議棧

協議棧(網絡堆棧)

一組可以協同工作的網絡協議層,定義七個協議層的OSI參考模型通常稱爲棧,定義Internet上的TCP/IP協議集也稱爲棧.術語的堆棧式指處理協議的實際軟件.

  • 加載堆棧: 加載使用特定協議集所需的軟件
  • 綁定堆棧: 將一組網絡協議鏈接到網絡接口INC,每個INC至少綁定一個堆棧才能實現不同設備之間的數據傳輸.

IP堆棧的含義

一般最普遍的網絡堆棧的實現爲一個互聯網的協議棧,也稱爲IP棧,在操作系統中,IP棧提供了一套應用程序庫,用於與遠程設備建立和關閉鏈接以及在遠程設備之間發送和接收數據,而這其中的一套應用程序庫就是我們熟知的Socket API庫,並且幾乎所有提供IP堆棧的平臺上的api都是一致的.TCP/IP棧提供了socket套接字用於接收和發送來自應用程序的請求,而應用程序具備選擇TCP/UDP(UDP也有所屬於自己的一套應用程序庫來接收和發送數據)不同的協議來進行數據傳輸.於是在IP堆棧中提供UDP以及TCP兩種協議併爲兩種協議提供相應的應用程序庫,當我們應用程序選擇TCP協議來進行網絡通信的時候,此時就會將一套支持TCP協議的socket庫加載到IP棧中,並將TCP協議綁定到我們的INC接口來實現與遠程設備的數據傳輸.
在這裏插入圖片描述

網絡分層模型

協議棧與OSI七層參考模型

在這裏插入圖片描述

  • 在網絡分層模型中,應用層,表示層以及會話層傳輸數據的單位爲有效負載payload(實際數據),在TCP/UDP的傳輸層中傳輸數據的單位爲片段Segment,在IP(網絡層)傳輸數據的單位爲數據包package,在數據鏈路層傳輸數據的單位爲幀Frames,物理層之間的數據傳輸以二進制0和1的bit數據流進行傳輸.
  • OSI參考模型每一層之間的通信都遵循特定的約定,每一層的約定對應着一套協議來負責實現該層的數據分組與組裝.
  • OSI參考模型每個分層都接收由它下一層所提供的服務並負責爲自己的上一層提供特定服務,上下層之間進行數據通信所遵循的約定稱爲接口,上下層之間通過接口來實現通信.
  • 根據上述可以看出,每一個分層的協議傳輸的數據單位不同,同時對應的處理協議也不同,我們知道不同的協議對應着一套不同的處理程序庫API,也就是OSI分層模型中的每一層對應的協議棧都會有所不同,協議棧之間通過接口來實現數據通信.

TCP/IP四層模型

  • 摘錄《圖解TCP/IP協議》對OSI以及TCP/IP分層模型的對比如下:

在這裏插入圖片描述

通過上圖,我們可以很清晰地看出OSI與TCP/IP分層模型之間的聯繫與區分,同時每一層都對應到我們的計算機中的應用程序,操作系統和網卡硬件設備接口.

  • 關於數據傳輸單位的術語,這裏直接摘錄《圖解TCP/IP協議》一書:

  • TCP/IP傳輸的完整數據包描述如下:

在這裏插入圖片描述

TCP/IP協議

TCP/IP協議傳輸流程

在這裏插入圖片描述

  • 發送端將數據payload發送到應用程序中,經應用程序通過接口轉發給傳輸的TCP/UDP協議,負責與遠程設備建立連接,並在有效數據的數據包添加報文header(源端口以及目的端口號)
  • 其次,傳輸層將上述帶有端口號信息的數據報header以及payload傳遞給網絡IP層,IP層接收到數據並在現有的數據添加IP的header,即源IP地址與目的IP地址.
  • 接着,網絡IP層將數據再次傳遞到數據鏈路層,此時在原有的數據報文上添加鏈路層的源mac地址以及目標mac地址,最後轉發到物理層並將數據轉換爲二進制單位bit的數據流傳輸到接收端設備.

握手協議

三次握手

三次握手

在這裏插入圖片描述

  • 首先,客戶端發起請求建立連接的SYN,同時發送初始序號(ISN)seq爲x到服務端,此時客戶端處於請求連接等待確認狀態,SYN=1的報文不能攜帶數據,但是要消耗一個序號,此時客戶端處於SYN_SEND狀態.
  • 其次,服務端接收到客戶端的請求連接並給予SYN=1的響應ACK=1,並指定服務端初始的序號seq=y,並將客戶端的seq+1作爲應答,即ack=x+1發送到客戶端.此時服務端處於SYN_RECV狀態.
  • 最後,客戶端接收到SYN的報文並給予響應ACK,同時也對服務端的seq進行+1並給予響應,即ack=y+1,並將服務端發送的序號seq返回.此時客戶端與服務端都彼此建立連接.

進行三次握手的原因

  • 第一次握手確認客戶端能夠正常實現發送報文,而服務端能夠正常接收報文;
  • 第二次握手確認服務端能夠發送報文,此時對於服務端而言,客戶端能正常發送報文但是接收報文還不確定,而對於客戶端而言,服務端可以正常接收和發送報文.
  • 第三次客戶端向服務端發送接收到報文的請求響應,此時對於服務端而言客戶端已經具備接收和發送報文處理,於是客戶端與服務端建立連接並開始進行通信.

半連接與全連接隊列

  • 半連接隊列: 當服務端第一次接收到客戶端發起的SYN報文請求時,服務端處於SYN_RECV的狀態,此時服務端與客戶端還沒有建立完全連接,服務端將當前的狀態的請求保存在一個隊列中,即半連接隊列
  • 全連接隊列: 服務端與客戶端完成三次握手建立連接,此時會將已建立連接的請求報文添加到隊列中,如果隊列滿則會出現丟包
  • 丟包場景: 客戶端發送SYN報文到服務端過程中超時會丟包,同理服務端向客戶端發送ACK以及seq的響應超時也會丟包,同時根據上述可知,如果半連接隊列或者是全連接隊列滿的話,也會出現丟包現象.

SYN攻擊(DDos攻擊)

SYN攻擊就是客戶端在短時間內僞造大量不存在的IP地址,並向服務端不斷地發送SYN報文請求,由於源IP地址不存在,於是服務爲了回覆確認請求的報文SYN並等待客戶端的響應ACK,導致服務端需要不斷重發直至超時,而這些僞造的SYN報文將長時間佔用在未連接隊列中,這樣容易使得正常的SYN報文請求因爲隊列滿而被丟棄,從而引起網絡擁塞甚至系統癱瘓.SYN 攻擊是一種典型的 DoS/DDoS 攻擊.

一般通過以下命令進行排查:

netstat -n -p TCP | grep SYN_RECV

預防SYN攻擊手段:

  • 縮短超時(SYN Timeout)時間
  • 增加最大半連接數
  • 過濾網關防護
  • SYN cookies技術
四次揮手

四次揮手過程

  • 客戶端向服務端發起連接釋放的FIN請求報文並消耗一個初始的序號(ISN)seq值爲u,此時客戶端不再向服務端發送數據,處於等待服務端FIN的響應,處於等待終止連接狀態.
  • 服務端接收到客戶端的連接釋放FIN報文請求並開始斷開與服務端的連接,釋放客戶端持有的連接信息,在這個過程中連接釋放需要等待其正常執行釋放流程,於是服務端先向客戶端給予響應ACK,並對於客戶端發送的序號進行+1響應,即u+1,此時服務端消耗序號v併發送到客戶端中.此時TCP連接釋放處於半關閉狀態,客戶端需要等待服務端發送確認連接釋放的報文FIN,此時客戶端進入終止連接狀態2.
  • 這個時候服務端已經完成連接釋放工作,向客戶端發送連接釋放確認請求FIN以及對先前客戶端發起的FIN報文再次給予響應ACK,同時將客戶端先前發起的連接關閉請求報文的序號seq+1返回到客戶端中,即ack=seq+1,並重新初始化seq序號返回給客戶端.
  • 客戶端接收到服務連接釋放確認的報文,並響應給服務端ACK,同時將服務端攜帶的序號+1返回,同時也將攜帶服務端返回的序號響應ack一同返回給服務端,此時客戶端TCP連接仍然未完全關閉,需要等待一段時間用於處理連接釋放等收尾工作纔會自動關閉,最後客戶端與服務端都處於CLOSED狀態.

進行四次揮手的原因

主要是服務端在接收到客戶端發送連接請求的報文FIN的時候,此時服務端並不能立即關閉,因爲此時可能存在未處理完的網絡通信,需要等待網絡通信處理完成才能向客戶端發送連接關閉確認報文,但是爲了避免客戶端不知道情況,於是就發送一個請求響應的報文段(ACK=1,seq=v,ack=u+1)告知客戶端,“你的關閉連接請求報文我收到了,但是我這邊連接關閉還需要等待一段時間才能關閉,晚點我再給你發一個報文確認請求”.

滑動窗口與擁塞控制

滑動窗口

爲什麼TCP網絡傳輸需要引入滑動窗口

在這裏插入圖片描述
通過上述圖示對比可知:

  • 在沒有引入滑動窗口的時候,我們的TCP網絡發送數據包的時候都是一個一個地往服務端發送數據,此時如果發送的數據包需要等待的響應ACK較長的話,那麼也可以清楚地知道整個網絡耗時以及吞吐量都會大大地降低,嚴重影響到網絡的性能,於是引入滑動窗口來提升我們的網絡性能,即在較短的時間且允許的數據包大小範圍內發送數據包無需等待響應,此時一個是可以降低網絡耗時,一個是提升網絡的吞吐量,相比左邊逐個發送數據包性能上有所提升.
  • 關於超時重發,對於沒有使用滑動窗口技術來發送數據包,如果在指定的超時時間內沒有收到服務端返回的響應將會進行重複;而對於使用滑動窗口技術,由於窗口數據包都有存在順序序號,於是客戶端批量發送數據包的時候,如果接收到服務端三次響應的重複確認應答,那麼客戶端將會進行重發,也稱爲高速重發機制.

滑動窗口與流量控制

爲了防止服務端(或者稱爲接收端)接收到一個毫無關係的數據包並在當前的數據包進行耗時的處理而導致正常的數據包因處於高負載的情況被丟棄而重發消耗網絡流量,於是就有了流量控制.

  • 流量控制: TCP提供一種機制讓發送端根據接收端的實際接收能力控制發送的數據量來避免無效的數據包處理導致耗時影響網絡性能.
  • 流量控制的窗口大小:接收端會向發送端通知自己可以接收的數據大小,於是發送端會發送不會超過這個限度的數據.該限度的大小稱爲窗口大小.
  • 流量控制與超時: 客戶端利用滑動窗口批量發送數據包,等待下一次服務端的窗口更新通知告知可以發送的數據包大小,而此時服務端接收到數據之後發現緩衝區滿了,需要暫停接收數據,這個時候將會向客戶端發送服務端窗口更新通知,如果此時因爲超時或者更新通知數據丟包而導致無法繼續通信,客戶端發現過了超時時間還沒有窗口更新通知,此時就會發送一個窗口探測的數據包以獲取最新的窗口更新通知大小.

在這裏插入圖片描述

擁塞控制

爲了避免TCP一開始發送較大量的數據包而導致網絡擁堵癱瘓的問題,在通信一開始時就會通過一個叫做慢啓動的算法得出數值,對發送數據量的控制.

  • 擁塞窗口: 爲了在發送端調節所要發送的數據的量大小,在慢啓動的時候會先發送一個擁塞窗口大小爲1的數據段到發送端,此後每收到一次確認應答ACK,就會窗口的值就會加1,並且在發送端進行發送數據包時,會將擁塞窗口與滑動窗口通知的大小進行比較取最小值進行發送.
  • 對於重發採用超時機制,那麼擁塞窗口可以設置爲1之後進行慢啓動修正.

粘包與拆包

粘包與拆包問題分析

一般情況下出現粘包以及拆包現象主要是在網絡傳輸的過程自定義協議,但是對於自定義的協議進行解析的時候存在一些換行.空格等一些特殊字符,這些特殊字符需要我們在應用層上進行手動處理來保證是一個完整的數據消息片段.

  • 正常傳輸

  • 粘包

// 代碼表現
// 客戶端發送數據
String m1 = "this is the first msg";
String m2 = "this is the second msg";

client.send(m1);
client.send(m2);

// 服務端接收數據
while(true){
  String msg = server.read();
	log.info("msg=" + msg);
}
// msg.log
// msg = this is the first msgthis is the second msg
  • 拆包

// 代碼表現
// 客戶端發送數據
String m1 = "this is the first msg";
String m2 = "this is the second msg";

client.send(m1);
client.send(m2);

// 服務端接收數據
while(true){
  String msg = server.read();
	log.info("msg=" + msg);
}
// msg.log
// msg = this is the first msgthis is
// msg = the second msg
解決方案

一般需要我們進行自定義的協議,大部分都是編寫框架或者是公司內部的一些基礎的通用技術支持,在Java網絡編程中一般底層網絡利用Netty框架來幫助我們快速實現一個高性能的web服務,而Netty框架本身提供了編解碼器來幫助我們解決上述的粘包與拆包問題.

  • LineBasedFrameDecoder 可以基於換行符解決。
  • DelimiterBasedFrameDecoder可基於分隔符解決。
  • FixedLengthFrameDecoder可指定長度解決

後續關於Netty的編解碼器再重新寫一篇文章來闡述,在這裏僅需知道什麼是粘包與拆包即可.

HTTP/HTTPS協議

HTTP協議

http簡要概述

  • 用於客戶端與服務端之間的通信
  • 通過發起請求以及請求響應完成客戶端與服務端之間的通信
  • http協議是不保存狀態的協議,即無狀態協議,保存狀態是通過cookie與session協同完成
  • 通過請求的uri定位到遠程資源
  • 具備告知服務器端具體的意圖行爲,比如doGET/doPOST/doPUT/doDELETE/doPATCH等操作
  • http協議是屬於短連接的方式,但是可以通過“keep-alive”來持久連接,主要是爲了減少每次建立連接都要進行三次握手以及退出連接進行四次揮手的過程.
  • 使用管線化技術來避免每次連接都需要等待連接的響應,類似於滑動窗口原理,可以同一個時間點發起http的請求.

最後,關於上述的描述,摘錄《圖解HTTP》截圖說明:

http方法明細如下:

http的持久連接明細如下:

在這裏插入圖片描述

http管線化(pipeline)如下:

在這裏插入圖片描述

http報文說明

在這裏插入圖片描述

  • HTTP請求體結構
curl -v https://www.baidu.com

GET / HTTP/1.1                            ### 請求行(請求方法 + http協議版本)
Host: www.baidu.com
User-Agent: curl/7.54.0
Accept: */*
  • HTTP響應體結構
curl -i https://www.baidu.com

HTTP/1.1 200 OK														### 狀態行(http協議版本 + 狀態碼)
Accept-Ranges: bytes
Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
Connection: keep-alive
Content-Length: 2443
Content-Type: text/html
Date: Thu, 21 May 2020 06:57:08 GMT
Etag: "58860402-98b"
Last-Modified: Mon, 23 Jan 2017 13:24:18 GMT
Pragma: no-cache
Server: bfe/1.0.8.18
Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/

<!DOCTYPE html>
<!--STATUS OK--><html> .... </html>

http常用頭字段說明

  • 通用的部分首部字段
## Cache-Control: 控制緩存行爲
## Pragma: 報文指令
## Transfer-Encoding: 指定報文主體的傳輸編碼方式
## Via: 代理服務器信息
## Warning: 錯誤通知
  • 部分請求首部的字段
## Accept: 通知服務器用戶代理可處理的媒體類型以及媒體類型的相對優先級
## Accept-Charset: 用戶代理可處理的字符集,用q表示優先級
## Accept-Encoding: 用戶代理可以處理的內容編碼,如gzip等
## Authorization: 用戶代理需要進行認證授權信息
## Referer: 告知服務器請求資源的原始uri
  • 部分響應首部的字段
## Location: 響應狀態碼返回302的時候會攜帶Location的uri
## WWW-Authorization: 但客戶端發起請求的授權認證不對的時候服務端會返回401並攜帶此響應信息指定認證方式
  • 實體使用的首部字段
## Allow: 允許使用的http方法
## Content-Length: 實體主體部分的大小
## Content-Range: 數據太太,分組發送,告知當前數據發送的字節大小資源情況
  • 緩存使用的字段
## 緩存控制,Cache-Control 與 Pragma
## Cache-Control: no-store  沒有緩存
## Cache-Control: no-cache  緩存但重新驗證,有緩存但未過期,響應返回304
## Cache-Control: private | public    緩存默認私有/公有(中間代理、CDN等代理中間件緩存)
## Cache-Control: max-age=31536000  | must-revalidate   緩存過期與驗證

## Pragma,與Cache-Control: no-cache定義一致,但是定義Pragma以向後兼容基於HTTP/1.0的客戶端,不能拿來完全替代HTTP/1.1中定義的Cache-control頭

## 緩存失效時間計算公式: expirationTime = responseTime + freshnessLifetime - currentAge
## 可以查看頭部信息的Cache-Control: max-age=N | expires | Last-Modified

## 緩存驗證
## ETags:作爲緩存的一種強校驗器,ETag響應頭是一個對用戶代理(User Agent,即UA)不透明,如果資源請求的響應頭裏含有ETag,客戶端可以在後續的請求的頭中帶上If-None-Match頭來驗證緩存
## Last-Modified 響應頭可以作爲一種弱校驗器。說它弱是因爲它只能精確到一秒。如果響應頭裏含有這個信息,客戶端可以在後續的請求中帶上 If-Modified-Since 來驗證緩存
## 當向服務端發起緩存校驗的請求時,服務端會返回 200 ok表示返回正常的結果或者 304 Not Modified(不返回body)表示瀏覽器可以使用本地緩存文件。304的響應頭也可以同時更新緩存文檔的過期時間

## Vary響應
## Vary: User-Agent,緩存服務器需要通過UA判斷是否使用緩存的頁面.如果需要區分移動端和桌面端的展示內容,利用這種方式就能避免在不同的終端展示錯誤的佈局
  • 其他首部字段
## X-FRAME-OPTIONS: 在html使用iframe標籤的時候,是否允許跨域訪問,有DENY/SAMEORIGN/ALL
## X-XSS-Protection: 針對跨站腳本攻擊的防護機制開關,0 - XSS過濾設置無效, 1 - 設置爲有效

http常用的狀態碼說明

  • 狀態碼的類別如下:

在這裏插入圖片描述

  • 2XX:返回響應成功,204表示沒有資源返回但響應正常,206表示請求部分資源
  • 301: 資源的uri已經被更新,告知客戶端請求資源uri需要進行更新,以後使用新的資源uri進行訪問
  • 302: 資源的uri被臨時更新,告知客戶端當前請求資源的uri需要臨時更新,但是之後仍然會使用舊的資源uri.
  • 304: 表示緩存,告知客戶端直接使用緩存響應即可.
  • 400: 客戶端請求報文存在問題
  • 401: 表示需要客戶端請求資源的時候發起認證
  • 403: 沒有權限訪問,就是不讓客戶端訪問資源,不需要給出理由
  • 404: 服務端沒有資源
  • 500: 服務端本身處理程序出現問題
  • 503: 表示服務端的負載過大或者是停機維護,請稍後再試.

HTTPS協議

  • http與https區分

https協議是在http的基礎上增加一層SSL/TLS協議,也就是HTTP部分接口使用SSL/TLS協議來代替進行密文加密傳輸數據.

  • https執行流程原理

https採用非對稱的公開密鑰加密和對稱加密的共享加密並用的混合加密機制,同時爲了保證公開的密鑰是可信任的,於是需要第三方的認證機構頒發證書來完成公開密鑰的認證.具體的https加密執行流程如下:
在這裏插入圖片描述

  • https發起一個請求的時候,底層仍然是以TCP與遠程建立連接,需要經過三次握手,此時客戶端發起請求的時候,會攜帶以下信息: TLS/SSL協議版本 + client.random加密的對話密鑰 + 加密算法 + sessionId(用於保持會話連接)
  • 其次,服務端接收到信息的時候,確認SSL/TLS的版本號,會接收到客戶端的加密算法,並同樣生成一個對話密鑰(server.random)並攜帶證書信息返回給客戶端.
  • 客戶端接收到服務端的證書信息的時候,使用第三方機構進行證書的驗證,一般是根據證書管理路徑/是否有效等進行驗證,然後驗證成功之後,將隨機生成一個pre master secret,然後利用上述建立會話的基礎,以client.random + server.random + pre master secret進行對稱加密生成摘要,再利用證書的公鑰進行加密,接着再將握手消息進行hash,利用協商好的對話密鑰pre master secret進行加密(握手信息 + 握手消息的hash)發送到服務端.
  • 服務端接收sessionToken的時候需要用私鑰進行解密然後再利用對稱加密算法進行摘要解密得到隨機對話密鑰,然後再將握手消息進行解密,得到握手信息,對握手消息進行hash與傳輸過來的hash進行驗證,驗證通過之後,將使用pre master secret進行加密部分握手消息傳遞的客戶端
  • 客戶端使用對話密鑰進行解密,驗證與服務端傳遞的hash是否一致,如果一致握手協議至此完成,接下來所有的通信數據將由之前交互過程中生成的 pre master secret / client.random/server.random 通過隨機密鑰的算法得出 session Key,作爲後續交互過程中的對稱密鑰.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章