“微服務架構”是現在編程中很流行的概念,未來能和最新趨勢保持同步,我也一直在學習了解這個架構,如果具體點說,我一直在尋找一種使用Spring在Java中實現微服務架構的方法。
為什么我會有這個想法,這是有一定的背景的:我們公司很好但是卻有一個過時的技術堆棧,基本上沒有使用Java 8或者微服務器。所以我想要了解關于微服務器的事情,必須自己去收集學習,為此我創建了一個待辦事項系統,記錄我的學習經歷以供將來參考!
概述
本文的目的是為不同微服務提供源代碼演練,不會深入解讀概念和工具,主要是介紹一個包含用于開發微服務的模式、工具和技術的應用示例。
既然是一個參考應用程序,我會使它盡量簡單,源碼易于理解!本文中,這個待辦事項系統應用程序由8個部分組成:
Reminder
User
Service discovery server
Mailer
OAuth Server
System integration test
API Gateway
Web application client
系統如何與Microservices交互
上圖中顯示了系統與所有微服務器的交互,用戶訪問使用Angular 2編寫的Web應用程序。然后連接到OAuth授權服務器,OAuth授權服務器將是可以分配用戶和權限的中心點。此服務器將返回一個JSON Web令牌,其中包含客戶端及其權限的信息以及格式范圍。在用戶認證并具有令牌之后,Web應用程序與API網關通信。通過JWT來驗證它是否來自授權服務器,然后調用微服務器并構建響應。
OAuth服務器使用用戶服務來獲取用戶的身份驗證詳細信息。此外,API網關使用OAuth服務器來獲取用戶的信息。
Remainder Service由ToDo功能來提供,,ToDo服務有一個計劃工作來檢查并通過電子郵件通知用戶,電子郵件由郵件服務發送,該事件使用Kafka的事件提醒服務觸發。
系統集成測試是一個Java應用程序,負責到達提醒服務的端點。
連接微服務
在微服務體系結構中,我們必須處理許多在不同IP和端口上運行的微服務器。因此,我們需要找到一種無需硬編碼來管理每個地址的方式。
這就需要用到Netflix Eureka,它是客戶端服務發現,允許服務自動查找和相互通信。我們在系統中使用了Spring Cloud Eureka,如果Eureka查找到服務運行的位置,我們就可以添加實例并應用負載平衡來在微服務器之間分配傳入的應用流量。
在系統中,我們使用Netflix Ribbon作為客戶端負載均衡器,實現容錯,并通過冗余增加可靠性和可用性。我們使用Netflix Foreign編寫聲明性REST客戶端,并集成Ribbon和Eureka來提供負載平衡HTTP客戶端。
我們的系統中存在一些依賴。我們正在使用Netflix Hystrix斷路器將應用程序與依賴性故障隔離開來。它有助于阻止級聯故障,并允許快速恢復或添加后備。Hystrix為每個依賴關系維護一個線程池,如果線程池耗盡,它會拒絕請求。它還提供斷路器功能,可以停止對依賴關系的所有請求。當請求失敗,拒絕或超時時,還可以實現備用邏輯。
認證
開發任何類型的系統時,安全性都是非常重要的,微服務架構也沒什么不同。當“如何保持微服務的安全性?”這個問題出現時,我的第一個答案是OAuth2。OAuth2絕對是一個很好的解決方案:它是一種眾所周知的授權技術,廣泛應用于Google,Facebook和Github。
當然沒有Spring Security的情況下,是不可能談論安全的。。在討論分布式系統的安全性時,Spring Security和OAuth2是顯而易見的選擇。
不過,我們在安全問題上還增加了一個元素:JSON Web Token(JWT)。如果僅使用OAuth,我們需要一個OAuth授權服務器來驗證用戶,生成令牌,并充當資源服務器的端點,詢問該令牌是否有效以及授權的權限。與授權服務器相比,這需要兩倍的請求。而JWT提供了一種在訪問令牌中傳輸權限和用戶數據的簡單方法,一旦所有數據都已經在令牌字符串中,資源服務器就不需要請求令牌檢查。所有信息都被序列化為JSON,用base64編碼,最后用私有RSA密鑰簽名。
您可以查看OAuth2授權服務器(OAuth-server)和資源服務器(API Gateway)的代碼實現。
REST
在系統中,我們有兩種交互方式:同步和異步。異步,我們使用了分布式事件與Kafka,遵循發布/訂閱模型。同步,我們支持了JSON和XML的REST風格。
Martin Fowler認為RESTful可以分為四個級別,level 0-level 3。我們的微服務處于level 2,為了簡單起見,這里不使用HATEOAS設計模式實現超媒體控件。
因為我們使用了Spring Cloud,所以必須實現開箱即用的可擴展性模式,將其放在HTTP連接中,例如:斷路器,負載平衡,連接池,超時和重試等。
分布式事件
如上所述,我們通過使用Kafka 進行提醒服務和 Mailer服務之間的通信,并異步地與其他Microservices進行通信。在提醒服務中,我們有一個計劃任務來檢查提醒時間,并發布RemainderFound事件。Mailer服務中將會有一個訂閱的事件,向用戶發送電子郵件。
Event sourcing和 CQRS
單片應用通常具有單個關系數據庫。我們可以使用ACID交易。因此,如果出現問題,我們的應用程序可以簡單地開始一個事務,更改多個行,并提交事務,如果錯誤的話,還可以恢復到之前。不幸的是,處理微服務架構中的數據訪問要復雜得多。這些數據分布在不同的數據庫中,跨多個服務實施業務交易是一個很大的挑戰。
在“ToDo”項目中,我們使用事件來處理跨多個服務的業務事務。 您可以查看在Mailer服務中應用的CQRS實施事件。 可以看到如何分離讀和寫,輕松地縮放每個部分。 我們使用關系數據庫作為事件存儲,然后使用Kafka分發事件。 當然我們需要將這兩個動作定義為原子操作并避免存儲事件,這樣就不會導致JVM崩潰。 不使用Kafka作為事件存儲,是因為從關系數據庫構建聚合更簡單。
未來的計劃
您可能也注意到這個項目中尚且還有很多事情尚未解決,這是一個開發中的項目,未來我們會添加更多的東西,例如Spring云配置,Docker容器,與Jenkins的持續集成,Spring Sleuth的分布式跟蹤,ELK的日志管理等。