大家好,我是老三,面渣逆襲系列繼續,這期給大家帶來微服務相關的面試題。
微服務(Microservices)是一種軟體架構風格,將一個大型應用程式劃分為一組小型、自治且鬆耦合的服務。每個微服務負責執行特定的業務功能,並通過輕量級通訊機制(如HTTP)相互共同作業。每個微服務可以獨立開發、部署和擴充套件,使得應用程式更加靈活、可伸縮和可維護。
在微服務的架構演進中,一般可能會存在這樣的演進方向:單體式-->服務化-->微服務。
單體服務一般是所有專案最開始的樣子:
後來,單體服務過大,維護困難,漸漸演變到了分散式的SOA:
SOA(Service-Oriented Architecture,面向服務的架構)是一種軟體架構設計原則,強調將應用程式拆分為相互獨立的服務,通過標準化的介面進行通訊。SOA關注於服務的重用性和組合性,但並沒有具體規定服務的大小。
微服務是在SOA的基礎上進一步發展而來,是一種特定規模下的服務拆分和部署方式。微服務架構強調將應用程式拆分為小型、自治且鬆耦合的服務,每個服務都專注於特定的業務功能。這種架構使得應用程式更加靈活、可伸縮和可維護。
需要注意的是,微服務是一種特定的架構風格,而SOA是一種設計原則。微服務可以看作是對SOA思想的一種具體實踐方式,但並不等同於SOA。
微服務與單體服務的區別在於規模和部署方式。微服務將應用程式拆分為更小的、自治的服務單元,每個服務都有自己的資料庫和程式碼庫,可以獨立開發、測試、部署和擴充套件,帶來了更大的靈活性、可維護性、可延伸性和容錯性。
微服務架構不是萬金油,盡它有很多優點,但是對於是否採用微服務架構,是否將原來的單體服務進行拆分,還是要考慮到服務拆分後可能帶來的一些挑戰和問題:
簡單說,採用微服務需要權衡這些問題和挑戰,根據實際的需求來選擇對應的技術方案,很多時候單體能搞定的也可以用單體,不能為了微服務而微服務。
目前最主流的微服務開源解決方案有三種:
Dubbo:
Spring Cloud Netflix:
Spring Cloud Alibaba:
三種方案的區別:
特點 | Dubbo | Spring Cloud Netflix | Spring Cloud Alibaba |
---|---|---|---|
開發語言 | Java | Java | Java |
服務治理 | 提供完整的服務治理功能 | 提供部分服務治理功能 | 提供完整的服務治理功能 |
服務註冊與發現 | ZooKeeper/Nacos | Eureka/Consul | Nacos |
負載均衡 | 自帶負載均衡策略 | Ribbon | Ribbon\Dubbo負載均衡策略 |
服務呼叫 | RPC方式 | RestTemplate/Feign | Feign/RestTemplate/Dubbo |
熔斷器 | Sentinel | Hystrix | Sentinel/Resilience4j |
設定中心 | Apollo | Spring Cloud Config | Nacos Config |
API閘道器 | Higress/APISIX | Zuul/Gateway | Spring Cloud Gateway |
分散式事務 | Seata | 不支援分散式事務 | Seata |
限流和降級 | Sentinel | Hystrix | Sentinel |
分散式追蹤和監控 | Skywalking | Spring Cloud Sleuth + Zipkin | SkyWalking或Sentinel Dashboard |
微服務網格 | Dubbo Mesh | 不支援微服務網格 | Service Mesh(Nacos+Dubbo Mesh) |
社群活躍度 | 相對較高 | 目前較低 | 相對較高 |
孵化和成熟度 | 孵化較早,成熟度較高 | 成熟度較高 | 孵化較新,但迅速發展 |
在面試中,微服務一般主要討論的是Spring Cloud Netflix,其次是Spring Cloud Alibaba,Dubbo更多的是作為一個RPC框架來問。
微服務給系統開發帶來了一些問題和挑戰,如服務呼叫的複雜性、分散式事務的處理、服務的動態管理等。為了更好地解決這些問題和挑戰,各種微服務治理的元件應運而生,充當微服務架構的基石和支撐。
微服務的各個元件和常見實現:
註冊中心是用來管理和維護分散式系統中各個服務的地址和後設資料的元件。它主要用於實現服務發現
和服務註冊
功能。
總結一下注冊中心的作用:
SpringCloud可以與多種註冊中心進行整合,常見的註冊中心包括:
特性 | Eureka | ZooKeeper | Nacos |
---|---|---|---|
開發公司 | Netflix | Apache 基金會 | 阿里巴巴 |
CAP | AP(可用性和分割區容忍性) | CP(一致性和分割區容忍性) | 既支援AP,也支援CP |
功能 | 服務註冊與發現 | 分散式協調、設定管理、分散式鎖 | 服務註冊與發現、設定管理、服務管理 |
定位 | 適用於構建基於 HTTP 的微服務架構 | 通用的分散式協調服務架構 | 適用於微服務和雲原生應用 |
存取協定 | HTTP | TCP | HTTP/DNS |
自我保護 | 支援 | - | 支援 |
資料儲存 | 內嵌資料庫、多個範例形成叢集 | ACID 特性的分散式檔案系統 ZAB 協定 | 內嵌資料庫、MySQL 等 |
健康檢查 | Client Beat | Keep Alive | TCP/HTTP/MYSQL/Client Beat |
特點 | 簡單易用、自我保護機制 | 高效能、強一致性 | 動態設定管理、流量管理、灰度釋出等 |
可以看到Eureka和ZooKeeper的最大區別是一個支援AP
,一個支援CP
,Nacos既支援既支援AP
,也支援CP
。
Eureka的實現原理,大概可以從這幾個方面來看:
其它的註冊中心,如Nacos、Consul等等,在服務註冊和發現上,實現原理都是大同小異。
Eureka Server保證高可用,主要通過這三個方面來實現:
微服務架構中的每個服務通常都需要一些設定資訊,例如資料庫連線地址、伺服器埠、紀錄檔級別等。這些設定可能因為不同環境、不同部署範例或者動態執行時需要進行調整和管理。
微服務的範例一般非常多,如果每個範例都需要一個個地去做這些設定,那麼運維成本將會非常大,這時候就需要一個集中化的設定中心,去管理這些設定。
和註冊中心一樣,SpringCloud也支援對多種設定中心的整合。常見的設定中心選型包括:
設定中心,說白了就是一句話:設定資訊的CRUD。
具體的實現大概可以分成這麼幾個部分:
一般來說使用者端和伺服器端的互動分為兩種:推(Push)
和拉(Pull)
,Nacos在Pull
的基礎上,採用了長輪詢來進行設定的動態重新整理。
在長輪詢模式下,使用者端定時向伺服器端發起請求,檢查設定資訊是否發生變更。如果沒有變更,伺服器端會"hold"住這個請求,即暫時不返回結果,直到設定發生變化或達到一定的超時時間。
具體的實現過程如下:
通過長輪詢的方式,Nacos使用者端能夠實時感知設定的變化,並及時獲取最新的設定資訊。同時,這種方式也降低了伺服器端的壓力,避免了大量的長連線佔用記憶體資源。
嚴格來講,HTTP和不是一個層面的東西:
一些RPC框架比如gRPC,底層傳輸協定其實也是用的HTTP2,包括Dubbo3,也相容了gRPC,使用了HTTP2作為傳輸層的一層協定。
如果硬要說區別的話,如下:
HTTP | RPC | |
---|---|---|
定義 | HTTP(超文字傳輸協定)是一種用於傳輸超文字的協定。 | RPC(遠端過程呼叫)是一種用於實現分散式系統中不同節點之間通訊的協定。 |
通訊方式 | 基於請求-響應模型,使用者端傳送請求,伺服器返回響應。 | 基於方法呼叫模型,使用者端呼叫遠端方法並等待結果。 |
傳輸協定 | 基於TCP協定,可使用其他傳輸層協定如TLS/SSL進行安全加密。 | 可以使用多種傳輸協定,如TCP、UDP等。 |
資料格式 | 基於文字,常用的資料格式有JSON、XML等。 | 可以使用各種資料格式,如二進位制、JSON、Protocol Buffers等。 |
介面定義 | 使用RESTful風格的介面進行定義,常用的方法有GET、POST、PUT、DELETE等。 | 使用IDL(介面定義語言)進行介面定義,如Protocol Buffers、Thrift等。 |
跨語言性 | 支援跨語言通訊,可以使用HTTP作為通訊協定實現不同語言之間的通訊。 | 支援跨語言通訊,可以使用IDL生成不同語言的使用者端和伺服器端程式碼。 |
靈活性 | 更加靈活,適用於不同型別的應用場景,如Web開發、API呼叫等。 | 更加高效,適用於需要高效能和低延遲的分散式系統。 |
在微服務體系裡,基於HTTP風格的遠端呼叫通常使用框架如Feign來實現,基於RPC的遠端呼叫通常使用框架如Dubbo來實現。
這兩個才是適合拿來比較的東西:
Feign | Dubbo | |
---|---|---|
定義 | Feign是一個宣告式的Web服務使用者端,用於簡化HTTP API的呼叫。 | Dubbo是一個分散式服務架構,用於構建面向服務的微服務架構。 |
通訊方式 | 基於HTTP協定,使用RESTful風格的介面進行定義和呼叫。 | 基於RPC協定,支援多種序列化協定如gRPC、Hessian等。 |
服務發現 | 通常結合服務註冊中心(如Eureka、Consul)進行服務發現和負載均衡。 | 通過ZooKeeper、Nacos等進行服務註冊和發現,並提供負載均衡功能。 |
服務治理 | 不直接提供服務治理功能,需要結合其他元件或框架進行服務治理。 | 提供服務註冊與發現、負載均衡、容錯機制、服務降級等服務治理功能。 |
跨語言性 | 支援跨語言通訊,可以使用HTTP作為通訊協定實現不同語言之間的通訊。 | 支援跨語言通訊,通過Dubbo的IDL生成不同語言的使用者端和伺服器端程式碼。 |
生態系統 | 整合了Spring Cloud生態系統,與Spring Boot無縫整合。 | 擁有完整的生態系統,包括註冊中心、設定中心、監控中心等元件。 |
適用場景 | 適用於構建RESTful風格的微服務架構,特別適合基於HTTP的微服務呼叫。 | 適用於構建面向服務的微服務架構,提供更全面的服務治理和容錯機制。 |
需要注意的是,Feign和Dubbo並不是互斥的關係。實際上,Dubbo可以使用HTTP協定作為通訊方式,而Feign也可以整合RPC協定進行遠端呼叫。選擇使用哪種遠端呼叫方式取決於具體的業務需求和技術棧的選擇。
Feign是一個宣告式的Web服務使用者端,它簡化了使用基於HTTP的遠端服務的開發。
Feign是在RestTemplate 和 Ribbon的基礎上進一步封裝,使用RestTemplate實現Http呼叫,使用Ribbon實現負載均衡。
Feign的主要特點和功能包括:
@FeignClient(name = "example", url = "https://api.example.com")
public interface ExampleService {
@GetMapping("/endpoint")
String getEndpointData();
}
整合負載均衡:Feign整合了Ribbon負載均衡器,可以自動實現使用者端的負載均衡。它可以根據服務名和可用範例進行動態路由,並分發請求到不同的服務範例上,提高系統的可用性和可伸縮性。
容錯機制:Feign支援整合Hystrix容錯框架,可以在呼叫遠端服務時提供容錯和斷路器功能。當遠端服務不可用或響應時間過長時,Feign可以快速失敗並返回預設的響應結果,避免對整個系統造成級聯故障。
主要原因是由於Ribbon的懶載入機制,當第一次呼叫發生時,Feign會觸發Ribbon的載入過程,包括從服務註冊中心獲取服務列表、建立連線池等操作,這個載入過程會增加首次呼叫的耗時。
ribbon:
eager-load:
enabled: true
clients: service-1
那怎麼解決這個問題呢?
可以在應用啟動時預熱Feign使用者端,自動觸發一次無關緊要的呼叫,來提前載入Ribbon和其他相關元件。這樣,就相當於提前進行了第一次呼叫。
比較常見的一個做法是,使用攔截器傳遞認證資訊
。可以通過實現RequestInterceptor
介面來定義攔截器,在攔截器裡,把認證資訊新增到請求頭中,然後將其註冊到Feign的設定中。
@Configuration
public class FeignClientConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
// 新增認證資訊到請求頭中
template.header("Authorization", "Bearer " + getToken());
}
};
}
private String getToken() {
// 獲取認證資訊的邏輯,可以從SecurityContext或其他地方獲取
// 返回認證資訊的字串形式
return "your_token";
}
}
在Feign中,負載均衡是通過整合Ribbon來實現的。
Ribbon是Netflix開源的一個使用者端負載均衡器,可以與Feign無縫整合,為Feign提供負載均衡的能力。
Ribbon通過從服務註冊中心獲取可用服務列表,並通過負載均衡演演算法選擇合適的服務範例進行請求轉發,實現使用者端的負載均衡。
常見的負載均衡演演算法包含以下幾種:
常見的負載均衡器,比如Ribbion、Gateway等等,基本都支援這些負載均衡演演算法。
關於Dubbo,後面會單獨出一期。
在微服務中,假如一個或者多個服務出現故障,如果這時候,依賴的服務還在不斷髮起請求,或者重試,那麼這些請求的壓力會不斷在下游堆積,導致下游服務的負載急劇增加。不斷累計之下,可能會導致故障的進一步加劇,可能會導致級聯式的失敗,甚至導致整個系統崩潰,這就叫服務雪崩。
一般,為了防止服務雪崩,可以採用這些措施:
服務熔斷是微服務架構中的容錯機制,用於保護系統免受服務故障或異常的影響。當某個服務出現故障或異常時,服務熔斷可以快速隔離該服務,確保系統穩定可用。
它通過監控服務的呼叫情況,當錯誤率或響應時間超過閾值時,觸發熔斷機制,後續請求將返回預設值或錯誤資訊,避免資源浪費和系統崩潰。
服務熔斷還支援自動恢復,重新嘗試對故障服務的請求,確保服務恢復正常後繼續使用。
服務降級是也是一種微服務架構中的容錯機制,用於在系統資源緊張或服務故障時保證核心功能的可用性。
當系統出現異常情況時,服務降級會主動遮蔽一些非核心或可選的功能,而只提供最基本的功能,以確保系統的穩定執行。通過減少對資源的依賴,服務降級可以保證系統的可用性和效能。
它可以根據業務需求和系統狀況來制定策略,例如替換耗時操作、返回預設響應、返回靜態錯誤頁面等。
目前常見的服務熔斷降級實現方案有這麼幾種:
框架 | 實現方案 | 特點 |
---|---|---|
Spring Cloud | Netflix Hystrix | - 提供執行緒隔離、服務降級、請求快取、請求合併等功能 - 可與Spring Cloud其他元件無縫整合 - 官方已宣佈停止維護,推薦使用Resilience4j代替 |
Spring Cloud | Resilience4j | - 輕量級服務熔斷庫 - 提供類似於Hystrix的功能 - 具有更好的效能和更簡潔的API - 可與Spring Cloud其他元件無縫整合 |
Spring Cloud Alibaba | Sentinel | - 阿里巴巴開源的流量控制和熔斷降級元件 - 提供實時監控、流量控制、熔斷降級等功能 - 與Spring Cloud Alibaba生態系統緊密整合 |
Dubbo | Dubbo自帶熔斷降級機制 | - Dubbo框架本身提供的熔斷降級機制 - 可通過設定實現服務熔斷和降級 - 與Dubbo的RPC框架緊密整合 |
儘管已經不再更新,但是Hystrix是非常經典的服務容錯開源庫,它提供了多種機制來保護系統:
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
/**
* 服務降級範例
**/
@Service
public class MyService {
@HystrixCommand(fallbackMethod = "fallbackMethod")
public String myServiceMethod() {
// 實際的服務呼叫邏輯
// ...
}
public String fallbackMethod() {
// 降級方法的邏輯,當服務呼叫失敗時會執行此方法
// 可以返回預設值或執行其他備用邏輯
// ...
}
}
請求快取(Request Caching):Hystrix可以快取對同一請求的響應結果,當下次請求相同的資料時,直接從快取中獲取,避免重複的網路請求,提高系統的效能和響應速度。
請求合併(Request Collapsing):Hystrix可以將多個並行的請求合併為一個批次請求,減少網路開銷和資源佔用。這對於一些高並行的場景可以有效地減少請求次數,提高系統的效能。
實時監控和度量(Real-time Monitoring and Metrics):Hystrix提供了實時監控和度量功能,可以對服務的執行情況進行監控和統計,包括錯誤率、響應時間、並行量等指標。通過監控資料,可以及時發現和解決服務故障或效能問題。
執行緒池隔離(Thread Pool Isolation):Hystrix將每個依賴服務的請求都放在獨立的執行緒池中執行,避免因某個服務的故障導致整個系統的執行緒資源耗盡。通過執行緒池隔離,可以提高系統的穩定性和可用性。
Sentinel通過動態管理限流規則,根據定義的規則對請求進行限流控制。具體實現步驟如下:
// 原本的業務方法.
@SentinelResource(blockHandler = "blockHandlerForGetUser")
public User getUserById(String id) {
throw new RuntimeException("getUserById command failed");
}
// blockHandler 函數,原方法呼叫被限流/降級/系統保護的時候呼叫
public User blockHandlerForGetUser(String id, BlockException ex) {
return new User("admin");
}
private static void initFlowQpsRule() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule1 = new FlowRule();
rule1.setResource(resource);
// Set max qps to 20
rule1.setCount(20);
rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule1.setLimitApp("default");
rules.add(rule1);
FlowRuleManager.loadRules(rules);
}
Sentinel使用滑動視窗限流演演算法來實現限流。
滑動視窗限流演演算法是一種基於時間視窗的限流演演算法。它將一段時間劃分為多個時間視窗,並在每個時間視窗內統計請求的數量。通過動態地調整時間視窗的大小和滑動步長,可以更精確地控制請求的通過速率。
滑動視窗限流可以檢視前面的分散式篇。
Sentinel利用了Token Server和Token Client的機制來實現叢集限流。
開啟叢集限流後,Client向Token Server傳送請求,Token Server根據設定的規則決定是否限流。T
API閘道器(API Gateway)是一種中間層伺服器,用於集中管理、保護和路由對後端服務的存取。它充當了使用者端與後端服務之間的入口點,提供了一組統一的介面來管理和控制API的存取。
API閘道器的主要功能包括:
……
通過使用API閘道器,可以簡化前端與後端服務的互動,提供統一的介面和安全性保障,同時也方便了服務治理和監控。它是構建微服務架構和實現API管理的重要元件之一。
使用SpringCloud開發,可以採用以下的API閘道器選型:
……
在Spring Cloud Gateway裡,有三個關鍵元件:
我們再來看下Spring Cloud Gateway的具體工作流程:
又有兩個比較重要的概念:
在微服務中,有的山下游可能有十幾個服務,如果某一環出了問題,排查起來非常困難,所以,就需要進行鏈路追蹤,來幫助排查問題。
通過鏈路追蹤,可以視覺化地追蹤請求從一個微服務到另一個微服務的呼叫情況。除了排查問題,鏈路追蹤黑還可以幫助優化效能,視覺化依賴關係、服務監控和告警。
Spring Cloud提供了多種選擇的微服務鏈路追蹤方案。以下是一些常用的方案:
Jaeger:Jaeger 是 Uber 開源的分散式追蹤系統,也被納入了 CNCF(雲原生計算基金會)的維護。通過使用 Spring Cloud Sleuth 和 Jaeger 使用者端庫,可以將追蹤資訊傳送到 Jaeger 並進行視覺化展示和查詢。
SkyWalking:Apache SkyWalking 是一款開源的應用效能監控與分析系統,提供了對 Java、.NET 和 Node.js 等語言的支援。它可以與 Spring Cloud Sleuth 整合,將追蹤資料傳送到 SkyWalking 伺服器進行視覺化展示和分析。
這些方案都可以與 Spring Cloud Sleuth 進行整合,Spring Cloud Sleuth 是 Spring Cloud 中的一個元件,提供了在微服務呼叫時生成追蹤資訊的能力。
分散式事務可以檢視前面的分散式基礎篇。
Seata以下幾種模式的分散式事務:
Seata的實現原理主要包括三個核心元件:事務協調器(Transaction Coordinator)、事務管理器(Transaction Manager)和資源管理器(Resource Manager)。
Seata的實現原理基於兩階段提交(Two-Phase Commit)協定,具體的機制如下:
Seata事務的執行流程可以簡要概括為以下幾個步驟:
全域性事務ID和分支事務ID在分散式事務中通過上下文傳遞的方式進行傳遞。常見的傳遞方式包括引數傳遞、執行緒上下文傳遞和訊息中介軟體傳遞。具體的傳遞方式可以根據業務場景和技術選型進行選擇和調整。
Seata的事務回滾是通過回滾紀錄檔實現的。每個參與者在執行本地事務期間生成回滾紀錄檔,記錄了對資料的修改操作。
當需要回滾事務時,事務協調器向參與者傳送回滾請求,參與者根據回滾紀錄檔中的資訊執行復原操作,將資料恢復到事務開始前的狀態。
回滾紀錄檔的管理和儲存是Seata的核心機制,可以選擇將紀錄檔儲存在不同的媒介中。通過回滾紀錄檔的持久化和恢復,Seata確保了事務的一致性和恢復性。
我們使用Prometheus和Grafana來實現整個微服務叢集的監控和告警:
Prometheus:Prometheus 是一個開源的監控系統,具有靈活的資料模型和強大的查詢語言,能夠收集和儲存時間序列資料。它可以通過HTTP協定定期拉取微服務的指標資料,並提供可延伸的儲存和查詢功能。
Grafana:Grafana 是一個開源的視覺化儀表板工具,可以與 Prometheus 結合使用,建立實時和歷史資料的儀表板。Grafana 提供了豐富的圖表和視覺化選項,可以幫助使用者更好地理解和分析微服務的效能和狀態。
紀錄檔收集有很多種方案,我們用的是ELK
:
簡單說,這三者裡Elasticsearch提供資料儲存和檢索能力,Logstash負責將紀錄檔收集到ES,Kibana負責紀錄檔資料的視覺化分析。
使用ELK進行微服務紀錄檔收集的一般流程如下:
除了應用最廣泛的ELK,還有一些其它的方案比如Fluentd
、Graylog
、Loki
、Filebeat
,一些雲廠商也提供了付費方案,比如阿里雲的sls
。
參考:
[1]. 《重新定義SpringCloud實戰》
[2]. 《SpringCloud Alibaba微服務實戰與原理》
[4].《SpringCloud微服務和分散式系統實踐》
[5].https://cn.dubbo.apache.org/
[6].https://cloud.spring.io/spring-cloud-netflix/reference/html/
[7].https://yunlzheng.gitbook.io/prometheus-book/parti-prometheus-ji-chu
[9].https://www.bmabk.com/index.php/post/142012.html
[10].https://sentinelguard.io/zh-cn/
[11].https://www.elastic.co/elastic-stack
[12].https://seata.io/
面渣逆襲系列已經結整合冊,持續更新維護中,關注公眾號「三分惡」,回覆「666」即可無套路獲取!