在微服務架構中,API Gateway 和 Service Registry 是不可或缺的兩個核心元件。API Gateway 作為單一入口點,負責接收客戶端請求、路由到相應的微服務,並彙整結果傳回給客戶端,有效簡化了客戶端與微服務之間的互動,並提升了系統的安全性與可維護性。Service Registry 則負責記錄所有微服務的位址資訊,方便 API Gateway 動態查詢和呼叫,確保服務的可用性和可擴充套件性。隨著微服務數量的不斷增加,這兩個元件的重要性也日益凸顯。它們可以有效降低系統的複雜度,提高開發效率,並確保系統的穩定性和可靠性。
微服務架構中的 API Gateway 與 Service Registry 設計
在微服務架構中,如何有效地管理客戶端請求和服務間的通訊是一個重要的課題。傳統的做法是讓客戶端直接呼叫各個微服務,但這種方式會導致客戶端程式碼的複雜度增加,並且難以進行微服務的動態調整。因此,我們需要引入 API Gateway 和 Service Registry 來簡化系統架構並提升可擴充套件性。
API Gateway 的角色與優勢
API Gateway 作為系統的進入點,負責接收客戶端的請求,並呼叫相關的微服務,最後將結果彙總後傳回給客戶端。這種設計帶來了多項好處:
- 隱藏了系統內部的複雜性,簡化了客戶端的程式碼。
- 提供了更大的彈性,可以根據需要動態地調整、組合或分割微服務。
- 減少了客戶端與伺服器之間的來回通訊次數,提升了系統效率。
此外,API Gateway 還可以承擔負載平衡、身份驗證、監控和管理等任務,甚至可以根據不同客戶端的需求提供不同的 API 介面。然而,這種設計也帶來了一個潛在的問題:API Gateway 可能會成為系統的單點故障和效能瓶頸。因此,我們需要確保 API Gateway 的組態和維護過程是高效且輕量級的。
Service Registry 的重要性
隨著微服務數量的增加,API Gateway 需要知道每個微服務的位置(例如 IP 地址)才能正確地呼叫它們。這就是 Service Registry 的作用:它維護了一個包含所有微服務及其位置的資料函式庫,供 API Gateway 查詢使用。當一個微服務啟動時,它會向 Service Registry 註冊自己的位置;當客戶端發出請求時,API Gateway 會查詢 Service Registry 以取得所需的微服務位置,然後進行呼叫並彙總結果。
為了避免因為 Service Registry 資料丟失而導致的問題,我們可以使用像 Consul 和 SkyDNS 這樣的開源工具來自動發現微服務並進行健康檢查,從而確保系統的穩定性。
系統整合與擴充套件
讓我們透過一個簡單的例子來說明如何整合這些元件。首先,我們有一個基本的微服務架構,其中客戶端直接呼叫各個微服務(如圖 3.1 所示)。接著,我們引入 API Gateway 來封裝業務邏輯並簡化客戶端的請求(如圖 3.2 所示)。然後,我們增加了 Service Registry,使得 API Gateway 可以查詢微服務的位置(如圖 3.3 所示)。最後,為了提高系統的可擴充套件性,我們在適當的位置引入了負載平衡器(如圖 3.4 所示)。
此圖示說明瞭客戶端請求如何透過 API Gateway 和 Service Registry 到達各個微服務,並最終傳回結果給客戶端的流程。
詳細解析
- 客戶端請求處理:客戶端發出請求到 API Gateway。
- API Gateway 處理:API Gateway 解析請求並查詢 Service Registry 以取得所需的微服務位置。
- Service Registry 的角色:Service Registry 維護微服務的位置資訊,並在被查詢時傳回這些資訊。
- 微服務呼叫:API Gateway 使用從 Service Registry 取得的位置資訊呼叫相關的微服務。
- 結果彙總:各個微服務處理請求後將結果傳回給 API Gateway,API Gateway 再將這些結果彙總後傳回給客戶端。
從單體式架構遷移到微服務架構
在前面的章節中,我們已經瞭解了微服務的基本概念及其運作方式。如果您仍然繼續閱讀,表示我已經達到了第一個目標:激發您對實施微服務的興趣!現在,是時候探討如何實作向微服務的轉型這一關鍵主題。
遷移的必要性
您應該還記得,單體式應用程式非常龐大(以程式碼行數衡量)且複雜(以功能相互依賴、資料等方面衡量),能夠服務於跨地域的數十萬使用者,並需要多名開發人員和IT工程師。單體式應用程式的結構可能如圖4.1所示。
圖4.1 單體式應用程式的基本結構
有時候,即使具備所有這些特性,應用程式在初期可能執行良好。您可能不會遇到應用程式擴充套件性或效能方面的挑戰。但隨著時間和使用的增加,問題將會出現,並且這些問題可能因應用程式的不同而有所不同。例如,對於雲端或網頁應用程式,您可能會因更多使用者使用您的服務而遇到擴充套件性問題,或者由於較長的建置時間和迴歸測試,使得定期發布新更新變得昂貴且困難。如圖4.2所示,單體式應用程式的使用者或開發人員可能會遇到右側列出的一個或多個問題。
圖4.2 單體式應用程式的潛在問題
這時,遷移到微服務架構可能開始聽起來不只是一個流行的想法;它聽起來像是一個救命稻草。我們已經在前面的章節中瞭解了一些關於微服務的知識,因此我們知道我們的轉型將會像圖4.3所示的應用程式一樣。
建立根據微服務的新應用程式
那麼,我們該如何進行這樣的改變呢?有兩種可能的場景:建立一個全新的應用程式,或是轉換或遷移已經存在的單體式應用程式。後一種場景更為常見,但無論目前的情況如何,瞭解這兩種場景的利弊都是值得的。
使用微服務建立新應用程式
在開始之前,我必須說,我並沒有見過很多從頭開始構建根據微服務的應用程式的真實案例。通常,應用程式已經存在,大多數我參與過的應用程式都是從單體式架構轉型為微服務架構。在這些案例中,架構師和開發人員的意圖一直是重用一些現有的實作。隨著市場上相關技能的普及和一些成功的實施案例的發布,我們將會看到更多從頭開始構建根據微服務的應用程式的例子,因此討論這種場景是非常有價值的。
假設您已經弄清楚了所有的需求,並準備好進行您要構建的應用程式的架構設計。在開始時,您需要考慮許多常見的最佳實踐,這些將在下面的章節中介紹。
組織準備度
正如我們在第2章“切換到微服務”中所討論的,您首先要問自己的問題是,您的組織是否準備好轉型到微服務。這意味著您組織的各個部門現在需要以不同的方式思考如何構建和發布軟體:
- 團隊結構:單體式應用程式團隊(如果存在的話)需要被分解成幾個小的、高效能的團隊,這些團隊需要了解或接受微服務最佳實踐的培訓。正如您在圖4.3中看到的那樣,新的系統將由一系列獨立的服務組成,每個服務負責提供特定的服務。這是微服務正規化的一個關鍵優勢——它減少了溝通成本,包括多次無休止的會議。團隊應該按照他們試圖解決的業務問題或領域來組織。然後,溝通變成了關於時間和遵循的一套標準/協定,以便這些微服務能夠作為一個平台一起工作。
- 敏捷性:每個團隊必須準備好獨立於其他團隊運作。它們應該是標準Scrum團隊的大小;否則,溝通將再次成為問題。執行是關鍵,每個團隊都應該能夠應對不斷變化的業務需求。
- 工具和培訓:關鍵需求之一是組織是否準備好投資於新的工具和人員培訓。在大多數情況下,現有的工具和流程需要被淘汰,並採用新的工具和流程。這將需要大量的資本投資,以及投資於僱傭具有新技能的人員和對現有員工進行再培訓。從長遠來看,如果決定採用微服務是正確的,組織將會看到節省並收回投資。
根據服務的方法
與單體式應用程式不同,使用微服務時,您需要採取自給自足的根據服務的方法。將您的應用程式視為一組鬆散耦合的服務,它們相互通訊以提供完整的應用程式功能。每個服務都必須被視為一個獨立、自包含的服務,具有自己的生命週期,可以由獨立的團隊開發和維護。這些團隊可能會從多種技術中選擇,包括最適合其服務需求的語言或資料函式庫。例如,對於電子商務網站,團隊可能會編寫一個完全獨立的服務,例如購物車服務。
圖4.3 從單體式架構到微服務架構的轉型
此圖示說明瞭單體式架構與微服務架構之間的差異。在單體式架構中,所有功能模組都緊密耦合在一起,而在微服務架構中,每個服務都是獨立且鬆散耦合的。
#### 內容解密:
此圖表展示了從單體式架構轉型為微服務架構的概念。在單體式架構中,所有功能模組分享同一個資料函式庫,而在微服務架構中,每個服務都有自己的資料函式庫或使用適當的資料儲存技術。這種轉型使得每個服務能夠獨立開發、佈署和擴充套件,從而提高了整體系統的靈活性和可擴充套件性。
# 示例程式碼:微服務之間的通訊
import requests
def get_user_data(user_id):
# 呼叫使用者服務取得使用者資料
response = requests.get(f'http://user-service/users/{user_id}')
if response.status_code == 200:
return response.json()
else:
return None
def process_order(user_id, order_data):
user_data = get_user_data(user_id)
if user_data:
# 處理訂單邏輯
print("訂單處理成功")
else:
print("取得使用者資料失敗")
#### 內容解密:
此程式碼示例展示了微服務之間如何透過HTTP請求進行通訊。在這個例子中,get_user_data函式呼叫使用者服務來取得指定使用者ID的使用者資料。如果成功取得資料,則傳回JSON格式的資料;否則,傳回None。process_order函式演示瞭如何使用get_user_data函式取得使用者資料並處理訂單邏輯。這種方式使得不同的微服務能夠協同工作,共同完成複雜的業務邏輯。
微服務架構下的新應用程式開發
在微服務架構下開發新應用程式時,需要考慮多個重要方面。首先,應用程式的功能可以被分解成多個獨立的微服務,每個微服務負責特定的業務功能。例如,可以有一個購物車微服務使用記憶體內資料函式庫,而另一個訂單處理微服務則使用關係型資料函式庫。真實世界的應用程式可能會使用微服務來處理基本功能,如身份驗證、帳戶管理、使用者註冊和通知,並將業務邏輯封裝在API閘道器中,根據客戶端和外部請求呼叫這些微服務。
微服務的規模與功能
微服務的規模可大可小,取決於其提供的功能。一個微服務可以是由單一開發人員實作的小型服務,也可以是需要多個開發人員的複雜服務。重要的是每個微服務都應該具有單一、明確的功能。
擴充套件性、效能和安全性
在設計微服務架構時,必須考慮擴充套件性、效能和安全性。不同的微服務可能有不同的擴充套件需求,因此需要根據每個微服務的具體需求提供適當的擴充套件性。安全性應該在所有層面進行考慮,包括靜態資料、程式間通訊和動態資料等。
程式間(服務間)通訊
程式間通訊是微服務架構中的一個關鍵方面。非同步通訊是首選方案,因為它可以保持所有請求的軌跡,並且不會長時間佔用資源。使用訊息匯流排(如RabbitMQ)可以實作高效的通訊,它簡單且可擴充套件到每秒數十萬條訊息。然而,為了防止訊息系統成為單點故障,必須設計高用性的訊息匯流排。
使用Plantuml圖表示訊息匯流排架構
此圖示展示了微服務如何透過訊息匯流排進行通訊。
技術選擇
轉向微服務架構的最大優勢之一是它提供了選擇的自由。每個團隊都可以獨立選擇最適合特定微服務的語言、技術和資料函式庫。在單體式架構中,團隊通常沒有這種靈活性,因此不要錯過這個機會。
實作
實作是微服務架構中的關鍵階段。以下是一些需要牢記的關鍵方面:
- 獨立性:每個微服務應該是高度自治的,具有自己的生命週期,並且在開發和維護時不依賴其他微服務。
- 原始碼控制:必須建立適當的版本控制系統,並且每個微服務都必須遵循標準。
- 環境:所有不同的環境(如開發、測試、預釋出和生產環境)都必須正確安全地設定和自動化。
- 容錯:必須在微服務開發過程中解決下游服務故障的問題。其他服務的故障應該對終端使用者不可見。
- 重用:在微服務中,可以透過複製和貼上來重用程式碼,但必須在一定範圍內進行,以避免程式碼耦合。
程式碼範例與解析
以下是一個簡單的微服務範例,使用Node.js和Express框架:
const express = require('express');
const app = express();
const port = 3000;
app.get('/healthcheck', (req, res) => {
res.status(200).send('OK');
});
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
內容解密:
const express = require('express');:這行程式碼引入了Express框架,Express是一個流行的Node.js網頁應用程式框架。const app = express();:建立了一個Express應用程式例項。app.get('/healthcheck', (req, res) => {...});:定義了一個HTTP GET路由,用於檢查服務的健康狀態。app.listen(port, () => {...});:使應用程式監聽指定的埠,並在伺服器啟動時執行回呼函式。
這個範例展示了一個簡單的微服務,它提供了一個健康檢查介面。實際應用中,微服務可能需要處理更複雜的業務邏輯和與其他服務的互動。
從單體式架構遷移到微服務架構
在建立新的微服務應用程式時,許多最佳實踐同樣適用於從現有的單體式應用程式遷移。然而,這裡有一些額外的指導方針,如果遵循這些方針,將使遷移過程更為簡單和高效。
為何逐步遷移是必要的
雖然將整個單體式應用程式轉換為完全根據微服務的架構聽起來很誘人,但在某些情況下,將每個功能或能力轉換為微服務可能效率不高或成本過高。你最終可能會從頭開始重寫應用程式。正確的遷移方式可能需要逐步進行,如圖 4.4 所示。
選擇遷移的起點
對於目前的單體式應用程式,下一個問題是從哪裡開始。如果應用程式非常老舊,並且很難抽取出獨立的部分(即具有很高的內聚性),那麼可能最好從頭開始重建。在其他情況下,如果程式碼的部分功能可以快速停用,並且技術架構並非完全過時,那麼最好從重建元件作為微服務開始,並替換舊程式碼。
圖 4.4:單體式架構遷移到微服務的基本步驟
此圖示展示了將單體式應用程式逐步拆分為微服務的過程。首先,將功能拆分為獨立的服務,每個服務負責特定的功能。隨著遷移的進行,更多的功能被抽取出來,形成獨立的微服務,最終實作完全的微服務架構。
逐步遷移的優勢
- 降低風險:逐步遷移可以降低一次性替換整個系統所帶來的風險。
- 提高靈活性:可以根據業務需求和技術條件,靈活地選擇優先遷移的部分。
- 減少成本:避免了從頭開始重寫整個應用程式的龐大成本。
微服務遷移的最佳實踐
在遷移過程中,應該遵循一些最佳實踐來確保過程的順暢:
- 標記(Tagging):給每個請求加上唯一的請求ID,並記錄每個請求的日誌。這樣,當出現問題時,可以透過日誌追蹤到有問題的服務。
- 集中式日誌管理:建立集中式日誌管理系統,讓所有服務將日誌記錄到這個系統中,以便於問題的追蹤和分析。
- 自動化佈署:使用 Kubernetes 和 Docker Swarm 等工具自動化佈署過程,以應對數百甚至數千個微服務的管理挑戰。
- 監控和自動擴充套件:對基礎設施到應用程式 API 的所有方面進行監控,並設定自動警示。同時,實作自動擴充套件,以根據需求動態調整服務例項的數量。
自動化維運的重要性
自動化是微服務架構成功的關鍵。沒有自動化,很難有效地管理和維護大量的微服務。自動化涵蓋了佈署、監控、擴充套件等多個方面,是實作敏捷交付和高效維運的基礎。
內容解密:
本章節主要講述了從單體式架構遷移到微服務架構的過程。首先,解釋了為何需要逐步遷移,而不是一次性替換整個系統。接著,討論了選擇遷移起點的重要性和不同情況下應該採取的策略。同時,介紹了一些在遷移過程中需要遵循的最佳實踐,如標記、集中式日誌管理、自動化佈署和監控等。最後,強調了自動化維運在微服務架構中的重要性,並總結了成功遷移的關鍵要素。
此圖示展示了單體式架構到微服務架構的逐步遷移過程。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title 微服務架構 API Gateway 與服務註冊設計
package "Kubernetes Cluster" {
package "Control Plane" {
component [API Server] as api
component [Controller Manager] as cm
component [Scheduler] as sched
database [etcd] as etcd
}
package "Worker Nodes" {
component [Kubelet] as kubelet
component [Kube-proxy] as proxy
package "Pods" {
component [Container 1] as c1
component [Container 2] as c2
}
}
}
api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2
note right of api
核心 API 入口
所有操作經由此處
end note
@enduml
此圖示說明
此圖示清晰地展示了從單體式應用程式到微服務架構的轉變過程。首先,將單體式應用程式的功能拆分出來,然後將這些功能重構為獨立的微服務,最終佈署這些微服務以實作完整的微服務架構。這種逐步遷移的方法使得轉變過程更加可控和高效。