隨著使用者數量增加和業務需求變化,傳統的單體式應用程式面臨諸多挑戰,例如擴充套件性瓶頸、佈署效率低下以及維護成本高等。為瞭解決這些問題,許多企業開始將單體式應用程式遷移至微服務架構。本文以 Helpdesk 應用程式為例,探討了微服務遷移的過程和最佳實踐。首先,我們分析了 Helpdesk 應用程式的業務需求和技術挑戰,並根據微服務準則制定了遷移規劃。接著,我們將產品目錄、票務管理和搜尋服務等核心功能拆分為獨立的微服務,並使用 Apache Maven 進行建置和管理。在佈署方面,我們探討了多種佈署策略,包括單機佈署、虛擬機器佈署和容器化佈署,並選擇了將微服務佈署在與原單體式應用相同的 Tomcat 伺服器上,以便於測試和驗證。最後,我們還討論瞭如何在新需求和錯誤修復場景下,利用微服務架構的優勢提升開發效率。
第 12 章:遷移到微服務的案例研究
在第 11 章「單體式 Helpdesk 應用程式案例研究」中,我們按照行業標準做法構建了一個傳統的根據網頁的幫助台應用程式。目的是提供一個接近真實世界的範例,並強調組織今天面臨的單體式應用程式挑戰。在本章中,我們使用微服務知識修改相同的幫助台應用程式,並學習如何解決我們強調的一些挑戰。
遷移規劃
假設我們的幫助台應用程式的高階業務需求在 2005 年首次編寫時如下:
- 支援大約 50 萬名客戶,使他們能夠在網上開啟工作票據以解決問題。
- 所有功能同等重要,並且應該始終可用。
- 應用程式具有水平擴充套件性。
- 透過允許使用者搜尋現有解決方案來減少提交的票據數量。
從單體架構遷移至微服務:案例研究
規劃遷移
套用微服務準則
回顧第4章中概述的微服務準則,這些準則定義了選擇和優先考慮單體應用程式中應遷移到微服務的能力的方法之一。根據新的需求和使用者行為,我們考慮以下七項最佳實踐:
- 擴充套件:由於使用者數量增加,應用程式需要擴充套件。工單和搜尋是兩個最重要的功能,因此將它們轉換為微服務是合理的。
- 改善的技術替代方案或多語言程式設計:新的需求需要一個智慧搜尋系統,Apache Solr是一個開源工具,可以提供相關的、上下文敏感的結果。
- 儲存替代方案或多語言持久化:目前的單體應用程式使用MySQL資料函式庫儲存所有資料。雖然工單資料儲存在關聯式資料函式庫中是合理的,但將產品目錄資料儲存在記憶體快取中,並使用平面檔案作為後備儲存,可以提高效能。
- 變更:由於大多數增強功能都與工單邏輯相關,因此將工單轉換為微服務是合理的。
- 佈署:在我們的應用程式中,沒有任何元件的佈署複雜度,因此這項準則不適用。
- 輔助服務:由於產品目錄服務的問題影響了工單流程,因此需要將產品目錄服務轉換為微服務,以確保工單流程不受影響。
Solr搜尋引擎簡介
Solr是一個根據Apache Lucene的搜尋引擎平台,用Java編寫,使用Lucene函式庫實作索引。它可以透過多種REST API存取,包括XML和JSON。Solr的基本功能包括:
內容解密:
Solr提供了許多先進的功能,例如:
先進的全文字搜尋
為高容量網路流量進行最佳化
全面的HTML管理介面
伺服器統計資料透過Java Management Extensions(JMX)公開,以便監控
線性可擴充套件,自動索引複製,自動容錯移轉和還原
近乎實時的索引
使用XML組態,具有彈性和適應性
可擴充套件的外掛架構
// Solr查詢範例
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocumentList;
// 建立Solr客戶端
SolrClient solrClient = new HttpSolrClient.Builder("http://localhost:8983/solr/mycore").build();
// 建立查詢
SolrQuery query = new SolrQuery("q=*:*");
// 執行查詢
QueryResponse response = solrClient.query(query);
// 取得結果
SolrDocumentList results = response.getResults();
內容解密:
上述程式碼展示瞭如何使用SolrJ客戶端進行查詢。首先,建立一個SolrClient例項,指向Solr伺服器的URL。然後,建立一個SolrQuery物件,指定查詢字串。接著,執行查詢並取得結果。最後,可以從QueryResponse物件中取得SolrDocumentList,其中包含了查詢結果。
從單體架構轉換到微服務架構的技術解析
在現代軟體開發中,微服務架構已成為提升系統可擴充套件性、靈活性和維護性的重要技術方案。本文將探討如何將單體架構的應用程式轉換為微服務架構,並以實際案例進行詳細說明。
架構轉換後的影響
當產品目錄、票務管理和搜尋服務被轉換為獨立的微服務後,系統架構將如圖 12.1 所示。原本緊密耦合的單體應用被拆分為多個獨立的微服務,每個微服務都佈署在負載平衡器(如 HAProxy)後方,以確保高用性和可擴充套件性。
產品目錄微服務的轉換步驟
- 建立新專案:在 Eclipse 中建立名為
catalog-svc的新專案。 - 安裝 Apache Maven:下載並安裝 Apache Maven,以利用其強大的依賴管理功能。
- 定義 Maven pom.xml 檔案:在專案根目錄中建立
pom.xml檔案,並定義所需的依賴項。 - 實作服務介面和實作類別:建立服務介面(如
CatalogService)、服務實作類別(如CatalogServiceImpl)、服務輔助類別(如CatalogServiceHelper)和資料存取物件(DAO)類別(如CatalogDao)。 - 修改 applicationContext.xml 檔案:僅針對該微服務的 Bean 修改
applicationContext.xml檔案。 - 執行 Maven 安裝:執行
mvn install命令,以生成catalog-svc的 WAR 檔案。 - 佈署 WAR 檔案:將生成的 WAR 檔案佈署到相同的 Tomcat 伺服器上。
程式碼範例
以下是產品目錄微服務的部分關鍵程式碼範例:
// Service Interface
public interface CatalogService extends BeanFactoryAware, ApplicationContextAware {
public abstract ProductDetailsResponse getCatalog(@Context HttpHeaders headers, String userId) throws ServiceInvocationException;
public abstract CatalogResponse addCatalog(@Context HttpHeaders headers, CatalogRequest req) throws ServiceInvocationException;
// 其他方法...
}
// Service Implementation
@Component
@Path("/CatalogService")
public class CatalogServiceImpl implements CatalogService {
@Override
@GET
@Consumes({"application/xml", "application/json"})
@Produces({"application/json"})
@Path("/getCatalog/{customerId}")
public ProductDetailsResponse getCatalog(@Context HttpHeaders headers, @PathParam("customerId") String customerId) throws ServiceInvocationException {
// 待實作的任務
}
}
// Service Helper
public class CatalogServiceHelper {
CatalogDao dao = null;
// 待實作的任務
}
// DAO Class
public class CatalogDao extends DataSource {
// 待實作的任務
}
內容解密:
CatalogService介面定義:定義了目錄服務的介面,包括取得目錄、新增目錄、更新目錄和刪除目錄等方法。CatalogServiceImpl實作類別:實作了CatalogService介面,並使用 JAX-RS 註解定義 RESTful API 端點。CatalogServiceHelper輔助類別:提供輔助方法,以簡化服務實作類別的邏輯。CatalogDao資料存取物件:負責與資料函式庫互動,以進行資料存取操作。
圖表說明
此圖示展示了轉換後的微服務架構:
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 圖表說明
rectangle "請求" as node1
rectangle "分發" as node2
rectangle "存取" as node3
node1 --> node2
node2 --> node3
@enduml
圖表內容解密:
- 客戶端請求:客戶端傳送請求到負載平衡器。
- 負載平衡器分發請求:負載平衡器將請求分發到不同的微服務例項。
- 微服務存取資料函式庫:各個微服務例項存取共用的資料函式庫。
從單體架構遷移到微服務:實踐與佈署
在現代軟體開發中,微服務架構已經成為企業應對複雜系統的首選方案之一。本文將探討如何將傳統的單體架構應用程式轉換為微服務架構,並詳細介紹相關的實踐步驟和佈署策略。
資料函式庫連線池組態最佳化
在進行微服務轉換的過程中,最佳化資料函式庫連線池組態是提升系統效能的關鍵步驟之一。以下是一個典型的資料函式庫連線池組態範例:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="username" value="<Username>"/>
<property name="password" value="<Password>"/>
<property name="initialSize" value="5"/>
<property name="maxActive" value="50"/>
<property name="validationQuery" value="select 1 from dual"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="true"/>
<property name="minIdle" value="0"/>
<property name="maxWait" value="120000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="removeAbandoned" value="true"/>
<property name="removeAbandonedTimeout" value="30000"/>
<property name="logAbandoned" value="true"/>
</bean>
組態解密:
- 初始連線數(initialSize):設定初始建立的連線數為5,以滿足初始請求需求。
- 最大活躍連線數(maxActive):允許的最大平行連線數為50,防止資料函式庫過載。
- 驗證查詢(validationQuery):使用
select 1 from dual來驗證連線的有效性。 - 空閒測試(testWhileIdle/testOnBorrow):啟用空閒連線測試和借用連線時的測試,確保連線可用性。
- 最小空閒連線數(minIdle):設定最小空閒連線數為0,允許所有連線在必要時被回收。
- 最大等待時間(maxWait):當連線池耗盡時,客戶端最大等待時間為120秒,防止請求無限期等待。
- 連接回收機制(removeAbandoned/removeAbandonedTimeout/logAbandoned):啟用放棄連線的回收機制,超時時間設為30秒,並記錄日誌以便問題追蹤。
微服務轉換實踐
本案例研究涉及將單體架構的Helpdesk應用程式分解為多個獨立的微服務,包括產品目錄、票務管理和搜尋服務。這些服務均採用Apache Maven作為建置工具,以實作更好的依賴管理。
產品目錄微服務
- 抽取特定程式碼和組態:將產品目錄相關的介面、服務實作、輔助類別和組態檔案從單體應用程式中抽取出來,形成獨立的建置實體。
- 採用Apache Maven:轉用Maven作為建置工具,以更好地管理依賴關係。
票務管理微服務
票務管理服務的轉換步驟與產品目錄服務類別似,同樣需要抽取相關程式碼和組態,並採用Maven進行建置。
搜尋服務微服務
搜尋服務的轉換包括兩個部分:根據資料函式庫的搜尋和根據Solr的進階搜尋。
根據資料函式庫的搜尋:將相關程式碼和組態從單體應用程式中遷移出來,形成獨立的微服務。
根據Solr的進階搜尋:安裝和組態Solr引擎,建立獨立的進階搜尋微服務。以下是一個使用Solr進行搜尋的程式碼範例:
@POST
@Consumes({"application/xml", "application/json"})
@Produces({"application/json"})
@Path("/solrSearch")
public QueryResponse search(@Context HttpHeaders headers, SearchRequest request) {
HttpSolrServer solr = new HttpSolrServer("http://<Solr主機IP>:8983/solr/helpdesk");
SolrQuery query = new SolrQuery();
query.setQuery(request.getQuery());
query.setStart(0);
QueryResponse response = solr.query(query);
return response;
}
程式碼解密:
- 建立Solr伺服器連線:使用
HttpSolrServer與指定的Solr引擎建立連線。 - 設定查詢引數:根據請求設定查詢字串和其他引數,如起始位置等。
- 執行查詢:透過
solr.query(query)執行查詢操作,並傳回結果。
建置與佈署
建置微服務
微服務可以透過命令列或整合開發環境(如Eclipse)進行建置。使用Maven時,執行mvn clean package命令即可完成建置並產生可佈署的WAR檔案。
佈署選項
單機多微服務佈署:在同一台實體或虛擬機器上佈署多個微服務,可有效利用資源,但需注意資源競爭問題。
容器化佈署:使用Docker等容器技術佈署微服務,能夠進一步提高佈署彈性和資源利用率。
將單體式應用程式遷移至微服務架構的實務案例分析
在現代軟體開發中,將單體式應用程式遷移至微服務架構已成為提升系統彈性、可擴充套件性和維護性的重要策略。本章節將透過一個實際的案例研究,詳細介紹如何將一個傳統的客戶服務應用程式——「Helpdesk」——從單體式架構遷移至微服務架構。
微服務佈署策略
在開始遷移之前,瞭解不同的微服務佈署策略至關重要。常見的佈署方式包括:
多個微服務分享同一台伺服器:這種方式資源利用率較高,但服務間缺乏有效的隔離,可能會因某個服務的異常行為而影響整體系統的穩定性。
每個微服務獨立佈署在虛擬機器中:這種方式提供了良好的服務隔離,每個微服務擁有獨立的資源分配,但可能導致資源利用率不高,且虛擬機器的啟動和管理相對較為耗時。
每個微服務佈署在容器中:使用容器技術(如Docker)佈署微服務,可以實作服務間的隔離,同時具備輕量級、快速啟動等優勢,是目前較為流行的佈署方式。
在本案例中,我們首先將新的微服務佈署在與原單體式應用相同的Tomcat伺服器上,後續將進一步探討如何使用Docker容器化技術進行佈署。
遷移步驟
修改組態檔案:調整單體式應用中的
Application.properties檔案,將對原有服務的呼叫改為呼叫新的微服務。例如,修改搜尋、取得目錄和建立工單的端點。endPoints.serachEndPoint=http://host:port/search-svc/rest/SerachService/search endPoints.getCatalog=http://host:port/ticketing-svc/rest/CatalogService/getCatalog endPoints.createTicket=http://host:port/catalog-svc/rest/TicketService/createTicket更新搜尋檢視:修改
search.jsp檔案,增加進階搜尋按鈕,並透過JavaScript呼叫Solr搜尋服務的端點。function solrsearch() { var solrSearchEndPoint = '<%= props.getProperty("endPoints.solrSearchEndPoint") %>'; var searchText = document.getElementById("searchText").value; // ... }構建和佈署微服務:使用Apache Maven分別構建各個微服務的WAR檔案,並將其佈署到Tomcat伺服器的
webapp目錄下。
Maven 建置輸出範例
[INFO] Processing war project
[INFO] Copying webapp resources [/opt/projects/BOOKCODE/catalog-svc/src/main/webapp]
[INFO] Webapp assembled in [1523 msecs]
[INFO] Building war: /opt/projects/BOOKCODE/catalog-svc/target/catalog-svc.war
[INFO]
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
[INFO] BUILD SUCCESS
[INFO]
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
---
-
新需求與錯誤修復
成功遷移至微服務架構後,系統的可擴充套件性和維護性得到了顯著提升。當出現新的業務需求或需要修復錯誤時,微服務架構能夠提供更大的靈活性。例如,若需要在檢視工單服務中增加一個額外引數,且該引數對其他元件的依賴極小,可以直接修改相應的微服務程式碼。
public TicketResponse createHdTicket(
@Context HttpHeaders headers,
TicketRequest ticketRequest)
throws ServiceInvocationException {
// 修改邏輯以適應新需求
}
內容解密:
此段程式碼展示瞭如何在微服務中處理工單建立請求。其中,@Context HttpHeaders headers 用於取得HTTP請求頭資訊,而 TicketRequest ticketRequest 則代表了建立工單的請求內容。開發者可以根據具體需求修改該方法內的邏輯,例如增加額外的引數處理或業務邏輯。