多雲架構落地設計和實施方案

「不要把雞蛋放在同一個籃子裏」是一條知名的商業準則,在雲平臺選擇上,不少公司也遵循這樣的準則。基於多雲平臺構築「業務中臺」並非一件簡單的事情,須要構建一種快速繼承、可持續迭代的路徑,幫助總體方案落地。本文以實際項目案例爲例,分析項目的架構設計、實施步驟,以及多雲架構面臨的挑戰和機遇。
整體思路
不一樣雲廠商提供的雲服務不盡相同,相同的雲服務在功能、性能上也會有或多或少的差別。越是深度使用某個雲廠商的雲服務,越是難於遷移到其餘雲廠商。選擇本身構建雲服務,則技術門檻,維護成本很高。肯定多雲架構之後,首先須要在技術棧的選型上作好折中。一個基本的原則是經過業務架構的靈活性,去適配不一樣的雲廠商,儘量的使用雲廠商提供的優秀特性,提高運行於該雲平臺的業務系統的可靠性,提高總體業務的競爭力。mysql

上面的思路和一些客戶常見的思路有顯著差異。有些客戶選擇採用開源軟件,搭建本身的PaaS平臺;有些客戶則徹底採用雲廠商的技術棧,開發兩套業務系統。這兩種方式是兩個極端,前者開發和運維難度高,每每因爲技術風險評估不足,項目沒法如期交付,或者產品競爭力太弱,沒有云廠商提供的服務好。後者則須要維護兩套系統,代碼重複度高,還會被雲廠商徹底綁定,失去談判的籌碼,業務發展靈活性下降。還有些客戶指望雲廠商提供足夠兼容性的框架支持,在不改造現有業務系統的邏輯的狀況下實現多雲部署,雲廠商這方面的努力一般因爲客戶系統的複雜性和多樣性得不到落地。spring

開發框架選擇和架構設計
開發框架設計是多雲架構的核心,也是抽象程度最高的部分。華爲雲主推ServiceComb做爲微服務框架,阿里雲主推HSF、Dubbo做爲微服務框架,其餘國外的雲廠商,多數選擇Spring Cloud做爲微服務框架,另外還有其餘不一樣的語言和框架選擇。雖然mesher等技術爲多語言協同工做提供了良好的支持,可是爲了最大限度的利用框架特性,幫助快速構建穩定可靠的業務系統,選擇一個微服務開發框架仍然是必不可少的。sql

基於整體思路,多雲架構指望在華爲雲上使用ServiceComb運行時,在阿里雲上使用HSF運行時,而且支持Spring Cloud運行時。完成這個目標,首先須要對微服務運行框架的運行時和主要組成部分有所瞭解。對於多數中臺系統,對於框架運行時的依賴,通常都是RPC框架,以及基於RPC框架作的服務治理能力,包括服務註冊發現、熔斷容錯、限流等機制。將業務邏輯核心代碼,與微服務框架能力進行解耦,是設計的第一步。數據庫

上面的圖形展示了基本的邏輯架構。api

業務核心:技術選型上使用Spring、Spring Boot。ServiceComb、HSF和Spring Cloud等微服務框架的技術底座,均可以基於Spring、 Spring Boot技術棧來構建。緩存

在邏輯架構下,須要將微服務代碼進行分層,包含下面三個主要目錄:
microservice-api:定義微服務的接口。該目錄包含接口定義(interface)、數據結構定義(models)。爲了支持不一樣的微服務框架,對於接口定義和數據結構定義會有必定的要求。
microservice-service:業務邏輯實現代碼。
microservice-endpoint-servicecomb:發佈爲ServiceComb的微服務項目。
microservice-endpoint-hsf:發佈爲HSF的微服務項目。
microservice-endpoint-springcloud:發佈爲Spring Cloud的微服務項目。服務器

這個代碼分層實施的核心關鍵是api設計,以及業務邏輯實現和服務發佈解耦。api設計須要知足不一樣的微服務框架的設計要求。這裏涉及到RPC編解碼的基礎。RPC編解碼一般分爲語言無關(跨平臺)和語言相關(不跨平臺)。好比HSF、Dubbo的缺省編解碼是語言有關的,只可以支持JAVA程序之間的通訊,ServiceComb缺省採用Jackson編解碼,或者protobuffer編解碼,這兩種方式都基於Open API 2.0進行定義,能夠作到語言無關,Spring Cloud則相對複雜一些,它是一種混合型的編碼格式,能夠經過靈活的序列化定製,知足多樣性須要,也能夠嚴格遵循Jackson和Open API標準。一般語言無關的編解碼能夠徹底包含語言無關的編解碼要求,所以api定義的時候,須要作到語言無關,纔可以保證api可以在不一樣的微服務開發框架下獲得最好的實現。基於本文是描述工程實踐,不詳細探討關於跨平臺設計的原理,下面列出一些接口設計的準則:
使用簡單的類型(好比Integer, String, Boolean等)定義參數或者返回值。或者使用包含簡單類型的,符合Java
Bean規範的POJO Bean定義參數或者返回值。
儘量不使用interface, abstract class, 存在多個實現的基類,模板類做爲參數或者返回值。 不使用運行環境強相關的對象做爲接口參數或者返回值。好比HttpServletRequest,RpcContext,InvocationContext,ResonseEntity等。session

上面的原則從使用者的視角來看,是很是容易理解的。接口語義清晰,沒有歧義,直接經過接口定義就可以理解接口的參數個數以及如何傳遞,不須要提供額外的文檔或者查看源代碼。有利於經過接口定義生成文檔、swagger定義。數據結構

在實際項目中,開發者須要理解microservice-api是微服務之間的接口定義,接口設計須要考慮數據的序列化和反序列化。這個不一樣於內部接口設計。爲了下降業務實現邏輯的重複度,加強內聚性,內部接口設計會更多的使用抽象、繼承進行邏輯封裝。內部接口的數據結構,還會包含一些額外的控制邏輯。好比數據庫訪問層的數據結構,提供懶加載等機制,當訪問到getter方法的時候,實際上調用的是代理對象的getter方法。所以,須要有一些數據結構轉換邏輯,將內部的數據結構轉換爲外部接口的數據結構,以保持服務之間接口和內部接口的界限清晰,防止將內部數據結構做爲參數或者返回值,致使內部信息泄露,形成不可預期的處理結果。架構

api示例

interface LoginService {
SessionInfo login(String username, String password);
}

public class SessionInfo {
private String sessionId;
private String username;
}

service示例

@Service

@Primary

public class LoginServiceImpl implements LoginService {

public SessionInfo login(String username, String password) {

// do login

}
}

ServiceComb Endpoint示例

服務端:

@RpcSchema(schemaId = 「LoginServiceEndpoint」)
public class LoginServiceEndpoint implements LoginService {
@Autowired
private LoginService service;

public SessionInfo login(String username, String password) {
return service.login(username, password);
}
}

客戶端:

@Bean
public LoginService getLoginService() {
return Invoker.createProxy(SERVICE_NAME, "LoginServiceEndpoint", LoginService.class);
}

或者
@RpcReference(microserviceName=SERVICE_NAME, schemaId=」LoginServiceEndpoint」)
private LoginService loginService;

HSF Endpoint示例

服務端:

@HSFProvider(serviceInterface = LoginService.class,serviceVersion = "1.0.0")
public class LoginServiceEndpoint implements LoginService {
@Autowired
private LoginService service;

public SessionInfo login(String username, String password) {
return service.login(username, password);
}
}

客戶端:
@HSFConsumer
private LoginService loginService;

從上面的代碼示例看,Endpoint層須要作到儘量邏輯簡單,實現邏輯所有交給Service層,只包含接口聲明,或者包含必要的數據結構轉換邏輯。上面的方式演示了RPC的接口聲明,還能夠加上REST標籤(Spring MVC,或者JAX RS),來支持REST接口。
遺留系統的改造策略

大部分公司都會存在現有系統運行於某一個雲上,把現有系統推倒重來,進行全新設計,一般不是一個好主意。遺留系統的改造須要遵循持續迭代,繼承性改造的思路。

以阿里雲系統改造爲華爲雲系統爲例,將原來基於HSF開發的微服務應用改造爲基於ServcieComb開發的微服務應用。

按照前面的整體思路,首先須要將原來項目強依賴於HSF的代碼部分分離出來,創建下面的目錄結構:
microservice-api:定義微服務的接口。該目錄包含接口定義(interface)、數據結構定義(models)。爲了支持不一樣的微服務框架,對於接口定義和數據結構定義會有必定的要求。
microservice-service:業務邏輯實現代碼。
microservice-endpoint-hsf:發佈爲HSF的微服務項目。

這個過程相對而言是簡單的,不涉及任何業務邏輯的調整,只是代碼目錄結構的變化和POM依賴關係的調整。調整完成後,能夠對現有功能進行簡單自動化驗證,保證項目自動化測試用例可以經過。調整後的項目功能和遺留系統是一致的,擁有一個始終無損的運行系統,對於功能比較、問題發現都是很是有幫助的。

項目調整後,就能夠增長華爲雲的項目:
microservice-endpoint-servicecomb:發佈爲ServiceComb的微服務項目。

這個項目能夠複製microservice-endpoint-hsf,將POM依賴改成ServiceComb的內容,並將發佈的接口(Endpoint)調整爲ServiceComb的發佈方式。

項目調整後,就能夠一份代碼構建出兩個可執行jar包,兩個jar包分別在華爲雲和阿里雲部署。

這個過程的工做量須要經過原來代碼自己的複雜度、api接口的規範性、代碼規模等進行評估。若是原來代碼結構複雜,api定義不規範,工做量會顯著增長。對於良好組織的代碼,這個過程則會很是容易。實際改造的一些項目,有些一天時間便可完成,有些則花費了1、兩個月時間。獲取到產品的業務結構、代碼規模和經過簡單的識別現有代碼的技術棧和開發方式,可以幫助有效的評估工做量。

遺留系統改造的核心工做在於microservice-api中接口定義的梳理。因爲HSF不是一個跨語言的開發框架,所以在接口定義裏面使用的數據結構ServiceComb可能存在不支持的狀況。採用一個支持跨語言的框架的接口定義做爲基準(通常的,能夠將接口定義經過IDL、WSDL或者Open API描述的接口定義,好比gRPC、WebService、ServiceComb,),其餘框架都實現這個接口,是微服務架構適配的核心關鍵。
數據庫適配

業務系統都會使用數據庫。各個雲廠商支持的數據庫形式多樣,有MySQL、Postgre、Oracle等,此外還有分佈式數據庫,以及分庫分表等特性,數據倉庫支持等。雖然在鏈接池管理、事務管理、ORM框架等方面有大量的開源開發框架,好比dbcp、spring transaction、MyBatis等,它們仍然無法覆蓋全部的應用場景。適配多雲架構對於業務代碼開發有以下建議:

儘量使用符合ANSI SQL Standard的SQL語句。好比MySQL在大小寫、Column引用、Limit、Group by語句等方面都有本身的特殊用法。爲了更好的適配多雲數據庫,應該避免使用特殊用法,除非對於業務價值具有明顯的價值,並且沒有對等的解決方案。

選用一個被普遍採用的ORM框架。好比MyBatis、JPA等。這些框架被普遍支持,對於多數據庫支持獲得比較充分的驗證。在使用數據庫有差別的地方,提供了很是良好的擴展機制。好比使用MyBatis,可使用DatabaseProvider接口,來屏蔽語法差別。在使用JDBC或者JDBCTempate拼接URL的地方,則能夠經過接口的不一樣實現,返回不一樣的SQL語句。

limit #{stratRow},#{rowCount}


limit #{rowCount} offset #{stratRow}

分佈式數據庫、分庫分表、分析型數據庫對於業務開發存在不一樣的限制。好比對於惟一索引使用的限制、對於分庫分表鍵的修改限制等。對於這些場景,儘量符合這些限制,調整業務實現方式,避免爲了知足某個不重要的場景須要,陷入一些分佈式場景沒法解決的問題當中去,而沒法適配其餘雲廠商的數據庫。

緩存適配
各個廠商均支持Redis做爲緩存,可是在Redis發展路徑上,有不一樣的分支。一個分支是Proxy集羣模式,一個分支是Redis的原生集羣方式。Redis提供的客戶端API也存在兩套,一個是Jedis,一個是JedisCluster,兩套支持的命令集合不盡相同。好比Proxy集羣模式可以很是好的支持全部的Jedis命令,而Redis的原生集羣方式只支持JedisCluster命令。不少客戶經常使用的pipeline指令,在Redis原生集羣方式下不支持。

Proxy集羣可以更好的屏蔽底層服務的差別,在沒有特殊須要的狀況下,建議用戶使用Proxy集羣模式,雲廠商須要經過Proxy集羣模式提供對於Jedis不一樣指令的支持。用戶也能夠選擇Redis的原生集羣,這個在不一樣的雲產生也都提供了支持,用戶能夠在業務場景上避免使用原生集羣不支持的命令,這樣就能夠在多雲環境上部署。

總結起來,緩存的多雲支持的最佳實踐:

及時升級Client API版本,使用比較新的Client API,而且只使用JedisCluster提供的指令集合。

消息中間件適配
相對於數據庫和緩存,消息中間件的適配的標準性更弱一點。雖然早期JAVA提出了JMS等標準,可是目前主流的消息中間件都沒有提供JMS接口的實現。阿里的RocketMQ、華爲基於Kafka的DMS的接口均不一致。並且消息中間件的服務質量並不同,好比在消息有序投遞、重複投遞等方面是經過消息中間件配置提供的,而不受客戶端接口控制。有些客戶的業務邏輯依賴於特定消息中間件的機制,所以須要對消息中間件使用的接口、接口行爲進行抽象,分析其餘雲廠商的中間件可否提供對應的接口實現並知足對應的服務質量要求,有時候須要業務代碼作一些補償,以彌補不一樣中間件服務質量的差別。

其餘中間件適配
其餘中間件包括日誌服務器、定時任務服務、對象存儲服務等等。適配的整體思路一致,這裏不詳細描述裏面的細節。

多雲架構的挑戰和建議
和作協議標準同樣,多雲架構除了給客戶帶來商業上的靈活性,還會促進業務系統軟件架構的改善,提高總體的軟件質量。由於多雲架構須要對使用的技術點進行權衡,技術選型上更多采用相對中立和標準的方案,這些方案被普遍驗證,相對於使用一些私有特性來講,更加穩定,同時能夠促進項目開發人員之間的技術交流和能力繼承。在給客戶作多雲架構落地的實踐中,經過架構交流,幫助客戶梳理了總體的架構演進方向,還發現了不少歷史問題,對於客戶的代碼質量提高起到了很大做用。

多雲架構也面臨特別多的挑戰。首先是短時間內企業維護成本的增長和技術成本的增長,須要投入專家解決前期的架構適配問題,爲系統持續演進搭好框架。多雲系統運行,還會增長業務數據同步,不一樣雲上系統如何進行協同的問題。只有當客戶多雲系統相對獨立,沒有數據共享和業務交互的場景才比較簡單。多雲系統還會影響到客戶的交付效率,不一樣雲的持續交付方式存在較大的差別,運維人員的體驗不一樣,會下降平常的效率。

所以,多雲架構更多的是準備「備胎」,客戶的主要業務仍是以單一雲廠商提供。多雲架構給客戶對比不一樣雲廠商的服務質量提供了很是準確的參考,客戶可以更加準確的選擇優質的供應商。

微服務框架層的抽象是作多雲架構最難抽象的部分,也是多數開發者最難把握的一層。ServiceComb 微服務開發框架做爲參考接口規範,能夠很是好的適配到HSF、Spring Cloud等框架。在接口定義的時候,能夠採用它做爲基線,先測試ServiceComb,再測試HSF和Spring Cloud等適配,對於新接口開發、歷史系統遷移都是一個很是好的中間產品。

相關文章
相關標籤/搜索