微服務與RPC思想總結

微服務是一種分佈式的方式,通過微服務可以將業務拆分,使業務職責單一化,業務解耦。微服務通常都是集羣部署,服務之間的通信需要通過RPC完成。集羣需要通過服務治理去管理,服務治理主要管理:接口方法和服務之間的映射關係、負載均衡、健康檢測、服務續約、服務發現、容災容錯等。

一、RPC

其中RPC主要是通過TCP傳輸協議和高效的序列化反序列完成。高性能的TCP傳輸手動主要是通過IO的多路複用和零拷貝,最典型的框架就是netty,而IO的多路複用主要手段有select、poll、epoll,其中epoll是liunx主要的io多路複用模型的手段,也是性能最高的。插件拓展的spi機制。

由此可見RPC的一套流程是通過TCP向特定的地址傳輸序列化好的二進制數據,然後通過反序列化將數據還原。RPC的職責就是能夠幫助我們解決系統拆分後的通信問題,並且能讓我們像調用本地方法一樣去調用遠程方法。我們不需要因爲這個方法是遠程調用就需要編寫很多與業務無關的代碼,RPC 幫助我們屏蔽網絡編程細節。而屏蔽網絡編程、序列化等一系列細節操作一般是使用動態代理去做的。

動態代理

RPC 會自動給接口生成一個代理類,當我們在項目中注入接口的時候,運行過程中實際綁定的是這個接口生成的代理類。這樣在接口方法被調用的時候,它實際上是被生成代理類攔截到了,這樣我們就可以在生成的代理類裏面,加入遠程調用邏輯。常用的動態代理技術有:JDK動態代理、Javassist、Byte Buddy、cglib等

io多路複用:

dubbo使用的TCP框架是netty 是io多路複用模型,相比springcloud的http性能上更勝一籌。io多路複用模型主要有以下幾種:
select:用戶態每次都將連接的文件描述符放在一數組中,每次調用select時,都需要將整個數組拷貝到內核中,讓內核去輪詢文件描述符,之後內核會將數組中有數據的文件描述符標記,然後返回可讀文件描述符的個數,具體哪個可讀還是要用戶自己遍歷找出被標記的文件描述符。
poll:原理和select一樣,只是poll是維護着鏈表文件描述符個數沒有上限,而select最多隻能1024個
epoll:主要通過三種方式提升了IO多路複用模型的性能

    1. 內核中保存一份文件描述符集合,集合是一個紅黑樹結構,無需用戶每次都重新傳入,只需告訴內核修改的部分即可,減少了select和poll來回拷貝文件描述符方式的消耗。
    1. 內核不再通過輪詢的方式找到就緒的文件描述符,而是通過異步 IO 事件喚醒。
    1. 內核僅會將有 IO 事件的文件描述符返回給用戶,用戶也無需遍歷整個文件描述符集合。

零拷貝(圖片轉自微信公衆號:小林coding):

零拷貝最主要的兩種方式有mmap和sendfile,其中sendfile是性能最好最常用的方式。傳統的拷貝是需要經歷這樣的流程:用戶進程希望在網絡中發送數據時。
1、首先需要去磁盤讀出這些數據這就需要發送read請求,需要從用戶態切換到內核態才能完成這個命令
2、然後內核態中利用DMA技術將磁盤的數據拷貝到內核態緩存中,然後再將內核態的緩存的數據拷貝到用戶態緩存
3、緊接着又需要將這些數據從用戶態緩存拷貝到內核態緩存中,這時用戶態又切換到了內核態

4、然後又得將內核態緩存的這些數據通過DMA技術拷貝到網卡緩存中,然後發送,結束後切回到用戶態。

這其中進行了四次用戶態和內核態的切換,四次數據的拷貝。其實發送這個過程完全沒必要把數據拷貝在用戶態和內核態來回拷貝的。有兩種零拷貝的方式解決了這些問題

  • mmap:系統調用函數會直接把內核緩衝區裏的數據映射到用戶空間,這塊緩存數據變成了用戶態和內核太共享的區域,這樣操作系統內核與用戶空間就不需要再進行任何的數據拷貝操作。但這還不是最理想的零拷貝,因爲仍然需要通過 CPU 把內核緩衝區的數據拷貝到 socket 緩衝區裏,而且仍然需要 4 次上下文切換
  • sendfile:sendfile相對mmap就更加暴力和高效,首先通過 DMA 將磁盤上的數據拷貝到內核緩衝區,然後直接就將內核緩存中的數據拷貝到網卡的緩衝區裏,此過程不需要將數據從操作系統內核緩衝區拷貝到 socket 緩衝區中,這樣就減少了一次數據拷貝。這個過程只需要兩次內核態和用戶態的切換和2 次的數據拷貝

序列化:

RPC的數據傳輸是依靠TCP完成,TCP傳輸的數據是二進制數據,這時就需要將方法中傳輸的對象進行序列化和反序列化,序列化主要考慮的幾個點:安全性、兼容性、性能、序列化後體積大小。
目前比較知名的有Json、Hessian、Protobuf、Kryo。像dubbo的默認序列化方式是Hessian,springcloud的feign是Json,gRPC是Protobuf。

SPI

RPC的拓展插件功能。在 Java 裏面,JDK 有自帶的 SPI服務發現機制,它可以動態地爲某個接口尋找服務實現。使用 SPI 機制需要在 Classpath 下的 META-INF/services 目錄裏創建一個以服務接口命名的文件,這個文件裏的內容就是這個接口的具體實現類。實際很少會使用JDK的SPI功能,原因是:1、JDK 標準的 SPI 會一次性加載實例化擴展點的所有實現。 JDK 啓動的時候都會一次性全部加載META-INF/service 下文件裏面的實現類。如果有的擴展點實現初始化很耗時或者如果有些實現類並沒有用到, 那麼會很浪費資源。2、如果擴展點加載失敗,會導致調用方報錯,而且這個錯誤很難定位。

市面上比較出名的是Dubbo自己的SPI機制,Dubbo的SPI機制可以按需去拓展插件,幾乎將所有的功能組件做成基於 SPI 實現,並且默認提供了很多可以直接使用的擴展點,如序列化的方式、傳輸協議等。
當然springcloud也有自己的SPI機制,只不過是通過注入的方法去實現,主要是通過Condition按需注入實現類似的SPI機制

二、服務治理

服務治理主要有:服務發現、服務續約、健康檢測、負載均衡、容災容錯等。主要是通過將服務註冊到註冊中心實現這些功能。比較出名的註冊中心有:Eureka、nacos、zookeeper等

服務發現

服務發現主要是維護調用接口和服務提供方地址的映射,可以幫助我們定位調用服務提供方的IP地址和維護集羣的IP地址動態變化。有了服務發現我們就可以從集羣中獲取相應接口的IP集合通過負載均衡的方式選擇其中的ip地址進行調用,這也PRC的服務發現機制。服務發現通過以下兩種方式實現:
1、服務註冊:在服務提供方啓動的時候,將自己的節點消息(IP地址、端口、服務接口等)註冊到註冊中心。
2、服務訂閱:在服務調用方啓動的時候,去註冊中心查找並訂閱服務提供方的 IP,然後緩
存到本地,並用於後續的遠程調用。
3、服務續約:定時的從註冊中心拉取其他節點的信息,更新本地緩存。

目前比較流行的服務發現的框架有zookeeper、Eureka、nacos。一個分佈式系統不可能同時滿足C(一致性)、A(可用性)和P(分區容錯性),其中zookeeper是cp,Eureka是AP。
其中eureka的自我保護模式能在短時間內丟失過多的客戶端時(可能發送了網絡故障),那麼這個節點將進入自我保護模式,不再註銷任何微服務,當網絡故障回覆後,該節點會自動退出自我保護模式。在大規模集羣服務發現系統的時候,捨棄強一致性,更多地考慮系統的健壯性,最終一致性纔是分佈式系統設計中更爲常用的策略

健康檢測

在PRC進行負載均衡調用服務的時候通常需要是需要知道哪些IP地址的節點可以哪些節點不可用,這些信息需要註冊中心對註冊的服務節點進行健康檢測,最常用的方法就是定時的心跳檢測,通過建立的TCP連接上發送類似ping的請求。根據服務節點的回覆情況可以分爲以下三種狀態:
1、康狀態:建立連接成功,並且心跳探活也一直成功。
2、亞健康狀態:建立連接成功,但是心跳請求連續失敗。
3、死亡狀態:建立連接失敗。

心跳檢測的頻率不同的框架都不一樣,像Eureka就是30秒一次心跳,90秒內未收到續約,就會將服務剔除

負載均衡

微服務框架或者RPC框架都是會帶有負載均衡的功能,也是集羣必備的能力。一般都是從註冊中心上拉取服務提供方的信息緩存在本地後,當調用相應的接口服務時,會從對應接口的服務提供方節點的集合中選擇一個可以的節點進行調用,具體調用哪個節點會有不一樣的選擇策略,例如:輪詢、隨機、權重輪詢、最少活躍調用數、一致性hash等。

例如:springcloud的Ribbon,就是springcloud服務消費和負載均衡的核心組件。Ribbon是基於http和tcp的負載均衡組件,eureka利用ribbon實現了服務消費,Ribbon通過配置的服務列表進行輪詢訪問,但與eureka集成使用時,服務列表會被eureka的服務列表所重寫,拓展成從註冊中心獲取服務列表(eureka的服務列表就是從註冊中心拉取的服務節點的信息數據)。同時將自身的Iping功能替換成eureka的NIWSDiscoveryPing功能,由eureka去確認哪些服務可用,因此Ribbon利用了eureka註冊中心服務發現、服務註冊、服務續約和健康檢測的機制,完成發現可用服務的節點地址及健康信息,然後利用自身的負載策略進行服務的消費。

熔斷限流降級

熔斷限流降級是微服務分佈式系統必備功能,它主要應對,訪問流量過大、節點負載過高和業務時間過長所引起的諸多問題。通過熔斷、降級可以保護系統因業務處理時間過長導致過多接口服務超時的級聯故障問題,限流保護系統避免流量訪問過大,節點負載過高的問題。
對應熔斷限流降級業界最經典的莫過於Springcloud的Hystrix,Hystrix實現了一整套熔斷限流降級的思想。

接下來隆重介紹一下Hystrix:
Hystrix對請求有兩種隔離策略:線程隔離和信號量隔離、熔斷、降級
線程隔離:
Hystrix會開啓線程池去執行請求,這個線程池就如普通的線程池一樣有核心線程數、最大線程數、任務隊列、線程空閒時間等,請求會被扔到線程池裏面執行,當超過核心線程數時就會進行線程的擴展,超過最大線程數時就會扔到隊列中排隊。和普通的線程池的機制一摸一樣的。默認情況下都是一個類內容的所有方法的請求流量共用一個線程池。
信號量隔離:限流
和線程隔離不一樣,信號量隔離可以限制一個對應服務接口調用的最大併發請求量。根據信號量去控制最大的併發數量可以達到限流的目的
熔斷降級:
Hystrix可以開啓服務接口調用的超時功能默認是1000毫秒,可以設置超時發生時是否中斷,這個設置只有線程隔離策略有效,默認是超時時中斷執行,Hystrix還會根據固定時間間隔內調用接口的成功率去判斷是否開啓熔斷和後續探測的半熔斷,默認熔斷和半熔斷規則如下:
1、熔斷:
當在時間窗內請求後端服務失敗數量超過一定比例(默認50%可以使用circuitBreaker.errorThresholdPercentage設置比例)斷路器會切換到熔斷狀態(Open),需要注意的是時間窗內請求數量要超過一定值才進行失敗數量的統計(默認20個),假如默認值下只有19個請求就算是全部都失敗了也不會開啓熔斷(可以通過circuitBreaker.requestVolumeThreshold設置請求數量)。斷路器開啓後所有請求會直接失敗觸發相應的降級方法。
2、半熔斷:
斷路器保持在開啓狀態一段時間後(默認5秒,可以通過circuitBreaker.sleepWindowInMilliseconds設置熔斷休眠時間),自動切換到半熔斷狀態(HALF-OPEN)。 這時會放過一個請求根據返回情況去設置狀態, 如果請求成功, 斷路器會關閉熔斷狀態(CLOSED), 否則重新切換到熔斷狀態(OPEN)。Hystrix根據規則發現端服務不可用時, Hystrix會直接切斷請求鏈, 避免發送大量無效請求影響系統吞吐量, 並且斷路器有自我檢測並恢復的能力。

在熔斷過程中,所有的請求會被觸發相應的降級方法。

分佈式鏈路跟蹤:

在分佈式系統的RPC調用時,可能會涉及多個遠程服務的調用。假設分佈式的應用系統由 4 個子服務組成,4 個服務的依賴關係爲 A->B->C->D,如果調用服務出現異常,光打印日誌是往往是不能快速定位問題的。這時就需要使用分佈式鏈路跟蹤了。比較著名分的布式鏈路跟蹤組件是就是sleuth和zipking。
分佈式鏈路跟蹤有 Trace 與 Span 的概念,Trace 就是一個服務調用鏈路,每次分佈式都會產生一個 Trace,每個 Trace 都有它的唯一標識即 TraceId,在分佈式鏈路跟蹤系統中,就是通過 TraceId 來區分每個 Trace 。
Span 就是代表了一調用鏈路中的一段鏈路,也就是說 Trace 是由多個 Span 組成的。在一個Trace 下,每個 Span 也都有它的唯一標識 SpanId,而 Span 是存在父子關係的。
比如:在 A->B->C->D 的情況下,在整個調用鏈中,正常情況下會產生 3個 Span,分別是 Span1(A->B)、Span2(B->C)、Span3(C->D),這時 Span3
的父 Span 就是 Span2,而 Span2 的父 Span 就是 Span1

再例如:
在sleuth中會產生這樣的日誌:[order-service,96f95a0dd81fe3ab,852ef4cfcdecabf3,false]
1、第一個值,是項目配置的spring.application.name的值
2、第二個值,96f95a0dd81fe3ab ,sleuth生成的一個ID,就是Trace ID,用來標識一條請求鏈路,一條請求鏈路中包含一個Trace ID,多個Span ID。
3、第三個值,852ef4cfcdecabf3、就是spanId。
4、第四個值:false,是否要將該信息輸出到zipkin服務中來收集和展示。

路由網關和配置中心:

路由網關的作用類似nginx haproxy等,所有的請求在達到集羣前會先通過網關進行url過濾、權限驗證、負載均衡等,路由網關也是通過服務發現功能將註冊中心所有的節點IP信息拉取到本地,然後通過配置過濾url、進行負載均衡等。路由網關在業界比較出名的有zuul和gateway,其中gateway使用的nio網絡編程技術,在性能上比zuul更加的出色。
配置中心則是將系統的配置集中一起,例如:spring yml、網關的配置的更改、網關限流策略的變更等各種配置,然後各個節點通過TCP的長連接將配置中心相應的配置拉取下來實現熱加載和統一管理的效果。市面比較流行的是 apollo

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章