微服務架構的優勢在於其靈活性、可擴充套件性和可維護性,尤其在面對快速變化的業務需求時更能展現其價值。本文以幫助台應用程式遷移為例,逐步說明從單體應用轉向微服務架構的過程。首先,我們需要根據業務功能拆分單體應用,並選擇合適的佈署策略。常見的策略包括多個微服務分享主機、每個微服務佈署在單獨的虛擬機器上,以及每個微服務佈署在單獨的容器中。考量隔離性、資源利用率和技術成熟度等因素,容器化佈署方案更具優勢。在程式碼調整方面,我們需要更新組態檔案,將原先指向單體應用的端點改為指向新的微服務,例如搜尋、工單管理和目錄管理等服務。同時,前端程式碼也需要相應調整,例如修改搜尋檢視以呼叫新的Solr搜尋服務。此外,我們還需要更新POJO和DAO層邏輯,以適應新的資料結構和業務流程。
微服務遷移案例研究:從單一應用到微服務架構
微服務佈署的多種策略
在從單一應用遷移到微服務架構的過程中,選擇合適的佈署策略至關重要。以下是三種常見的佈署策略及其優缺點:
多個微服務分享同一主機
在這種策略中,多個微服務或例項分享同一主機的資源(如CPU、記憶體和I/O)。這種方法相對高效,因為資源得到了充分利用。然而,缺點是各微服務之間缺乏隔離,除非每個微服務都是獨立的程式。此外,如果有一個微服務出現異常行為,可能會消耗掉主機的所有記憶體或CPU資源。
每個微服務佈署在單獨的虛擬機器上
這種策略的主要優點是每個微服務在虛擬機器內執行,具有完全的隔離性。每個微服務都有完整的記憶體、CPU和I/O資源分配。然而,這種方法的缺點是資源利用率較低,因為虛擬機器可能會被低效利用。這一問題可以透過合理分配資源和使用自動擴充套件技術來緩解。
每個微服務佈署在單獨的容器中
將微服務佈署在容器中,簡單來說就是將服務封裝成能在容器內執行的形式。這樣可以根據需求隨時啟動容器。容器的優點是每個容器都具有隔離性,且資源消耗可以監控、控制和管理。與虛擬機器相比,容器更輕量且易於構建、封裝和啟動。他們啟動速度極快,因為不需要像虛擬機器那樣啟動作業系統。
然而,容器技術仍在不斷發展中,面臨著安全性、大規模管理等挑戰。自Docker於2013年推出以來,容器技術已經變得更加普及和可存取。
幫助台應用程式的微服務佈署步驟
更新組態檔案
首先,我們需要將現有單一應用中的端點指向新建立的微服務。修改Application.properties檔案,更新Web服務的端點:
endPoints.searchEndPoint=http://host:port/search-svc/rest/SearchService/search
endPoints.getCatalog=http://host:port/ticketing-svc/rest/CatalogService/getCatalog
endPoints.createTicket=http://host:port/catalog-svc/rest/TicketService/createTicket
修改搜尋檢視
接著,修改單一應用中的search.jsp檔案,新增高階搜尋按鈕並呼叫Solr搜尋Web伺服器端點:
function solrsearch() {
var solrSearchEndPoint = '<%= props.getProperty("endPoints.solrSearchEndPoint") %>';
var searchText = document.getElementById("searchText").value;
if (searchText == '') {
alert('請輸入搜尋文字');
return;
}
var dataToSend = {"query": searchText};
$.ajax({
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
url: solrSearchEndPoint,
type: 'POST',
dataType: 'json',
data: JSON.stringify(dataToSend),
success: function(data, textStatus, jqXHR) {
$("#solrresults").empty();
var docs = data.results;
$.each(docs, function(i, item) {
$('#solrresults').prepend($('<div>' + objToString(item) + '</div>'));
});
var total = '找到 ' + docs.length + ' 個結果';
$('#solrresults').prepend('<div>' + total + '</div>');
}
}).fail(function (jqXHR, textStatus, error) {
alert(jqXHR.responseText);
});
}
架構圖示
此圖示說明瞭將幫助台應用程式遷移到微服務架構後的佈署結構:
架構解說:
- Tomcat Server:作為我們的應用伺服器。
- helpdesk.war:原始的單一應用程式。
- search-svc.war:搜尋相關的微服務。
- ticketing-svc.war:工單管理相關的微服務。
- catalog-svc.war:目錄管理相關的微服務。
建置與佈署步驟
- 建置個別微服務WAR檔案:使用Apache Maven來建置每個微服務,確保每個WAR檔案都是獨立封裝好的。
- 執行Maven建置:確保每個WAR檔案都成功建置並安裝到本地儲存函式庫中。
- 複製WAR檔案:將所有WAR檔案複製到Tomcat的
webapps目錄中。
完成以上步驟後,你會在Linux系統中的Tomcat目錄結構中看到以下內容:
search-svc catalog-svc docs helpdesk host-manager
ROOT ticketing-svc.war search-svc.war catalog-svc.war
examples
helpdesk.war manager ticketing-svc
新需求與錯誤修復
我們已成功將單一應用程式架構遷移到根據新商業需求的微服務架構。然而,商業需求會持續演變。以下是如何管理可能的變更請求的一些方法:
假設我們需要在「檢視工單」服務中新增一個額外引數,且這個引數對其他元件沒有依賴性。我們可以使用以下程式碼片段來更改工單請求:
public TicketResponse createHdTicket(
@Context HttpHeaders headers,
TicketRequest ticketRequest)
throws ServiceInvocationException {
// 處理工單建立邏輯
}
此圖示說明瞭新增引數後工單處理流程:
內容解密:
- HTTP Headers: 用於傳遞額外資訊如身份驗證或內容型別。
- TicketRequest: 包含工單請求所需資訊。
- ServiceInvocationException: 自定義異常型別用於處理服務呼叫錯誤。
總結來說,對於未來可能遇到任何更新需求或Bug修復,我們只需針對相關模組進行更新和重新佈署即可。此過程不會影響其他模組的正常運作,顯著提升了系統靈活性和穩定性。
透過以上步驟與架構設計,玄貓成功地從傳統單體架構過渡到了現代化、靈活且易於維護的微服務架構。這不僅提升了系統效能和可靠性,還使得未來面對業務需求變化時,能夠更快速地回應與調整。
從單體應用轉向微服務:一個實際案例
在現代軟體開發中,微服務架構已成為主流,因為它提供了更高的靈活性、可擴充套件性和維護性。讓我們探討如何從單體應用轉向微服務,並透過具體案例來說明其優勢。
新增屬性到POJO(Plain Old Java Object)
首先,我們來看看如何在Java的POJO中新增一個屬性。以下是範例程式碼:
@Component
private String emailAddress;
@XmlElement
public String getEmailAddress() {
return emailAddress;
}
public void setEmailAddress(String emailAddress) {
this.emailAddress = emailAddress;
}
在這裡,我們使用Spring的@Component註解來標註這個屬性,並使用JAXB的@XmlElement註解來標註XML元素。這樣,當我們將這個物件序列化為XML時,emailAddress屬性也會被包含在內。
內容解密:
@Component:這是Spring框架中的一個註解,用來標註一個類別為Spring容器中的一個元件。這樣,Spring容器就可以自動掃描並管理這個元件。@XmlElement:這是JAXB(Java Architecture for XML Binding)框架中的一個註解,用來標註一個屬性為XML元素。當我們將這個物件序列化為XML時,這個屬性會被包含在XML中。getEmailAddress和setEmailAddress:這兩個方法是JavaBean的標準方法,用來取得和設定emailAddress屬性的值。
在DAO層新增邏輯
接下來,我們需要在DAO層新增邏輯來從資料函式庫取得這個屬性。以下是範例程式碼:
private String saveToDatabase(TicketRequest ticketRequest) {
// 舊有邏輯
ticket.setEmailAddress(ticketRequest.getEmailAddress());
}
在這裡,我們將從TicketRequest物件中取得emailAddress屬性的值,並設定到ticket物件中。
內容解密:
saveToDatabase:這是一個方法,用來將資料儲存到資料函式庫中。在這裡,我們從TicketRequest物件中取得了emailAddress屬性的值,並設定到ticket物件中。ticketRequest.getEmailAddress():這行程式碼是從TicketRequest物件中取得emailAddress屬性的值。ticket.setEmailAddress(...):這行程式碼是將取得到的值設定到ticket物件中的。
微服務與單體應用的區別
從上述例子可以看出,在微服務架構中,我們只需要改變某個特定的服務即可。這樣的修改不會影響到其他服務。而如果是在單體應用中,我們需要重新編譯整個應用,並進行全面的迴歸測試,這樣會花費大量的時間和資源。
以下是微服務架構帶來的一些優勢:
1. 修復bug
只有包含bug的那部分應用需要修復。如果bug出現在某個微服務的程式碼中,我們只需要修改那個特定的微服務即可。由於每個微服務通常都會進行負載平衡佈署,所以我們可以逐步佈署修復版本以避免影回應用可用性。
2. 替換應用元件
如果我們希望使用雲端服務進行票務管理,只需進行組態變更即可。例如更新組態檔案以指向雲端伺服器端點。
3. 新增或替換技術堆疊
如果需要使用不同技術堆疊(如PHP/NOSQL)開發現有或新服務,開發人員有完全自由進行變更且依賴最小化。
4. 有選擇地擴充套件
微服務的一大優勢就是可以有選擇地擴充套件。例如,如果票務層有更多流量需求,我們可以輕鬆地增加虛擬機器或容器來處理票務服務而不需要影響其他服務或單體應用部分。
執行錯誤教訓
玄貓在實作過程中發現了一些錯誤教訓:
- 邏輯錯誤:初期設計時未充分考慮不同業務模組之間的耦合關係。
- 測試覆寫率不足:未能全面覆寫各種可能情況下的測試場景。
- 佈署複雜度增加:雖然微服務帶來了靈活性但也增加了佈署管理的複雜度。
隔離故障處理
最後來談談如何處理故障。假設某個微服務出現了問題或當機,只要設計得當,它不會影響整個系統。舉例來說在電子商務網站中有一些評價系統出現問題時並不會影響購物車和結帳部分。 而在單體應用則不同根據程式碼結構可能因評價系統當機導致整個網站無法使用。
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle
title 微服務遷移實戰:從單體到微服務架構案例
package "Docker 架構" {
actor "開發者" as dev
package "Docker Engine" {
component [Docker Daemon] as daemon
component [Docker CLI] as cli
component [REST API] as api
}
package "容器運行時" {
component [containerd] as containerd
component [runc] as runc
}
package "儲存" {
database [Images] as images
database [Volumes] as volumes
database [Networks] as networks
}
cloud "Registry" as registry
}
dev --> cli : 命令操作
cli --> api : API 呼叫
api --> daemon : 處理請求
daemon --> containerd : 容器管理
containerd --> runc : 執行容器
daemon --> images : 映像檔管理
daemon --> registry : 拉取/推送
daemon --> volumes : 資料持久化
daemon --> networks : 網路配置
@enduml
此圖示解說:
此圖示展示了電子商務網站中的各種功能模組及其相互關係。
- 電子商務網站包含購物車、結帳和評價系統等功能模組。
- 評價系統可能會當機但它對購物車和結帳功能模組沒有影響。 這樣設計可以確保系統其他部分仍然能夠正常運作即使其中一部分功能失效。
裝載容器化微服務
隨著業界越來越多採用容器技術如Docker來管理和執行微服務進而實作自動化佈署和擴充套件變得簡單易行了。 接下來章節玄貓將詳細介紹如何使用Docker將幫助台支援應用程式中的多項微服務進行容器化進而實作自動化擴充套件與管理。