本文主要內容java
學習微服務的關鍵特徵spring
瞭解微服務是如何適應雲架構的數據庫
將業務領域分解成一組微服務編程
使用Spring Boot實現簡單的微服務數組
掌握基於微服務架構構建應用程序的視角安全
學習何時不該該使用微服務服務器
軟件開發的歷史充斥着大型開發項目崩潰的故事,這些項目可能投資了數百萬美圓、集中了行業裏衆多的頂尖人才、消耗了開發人員成千上萬的工時,但從未給客戶交付任何有價值的東西,最終因爲其複雜性和負擔而轟然倒塌。微信
這些龐大的項目傾向於遵循大型傳統的瀑布開發方法,堅持在項目開始時界定應用的全部需求和設計。這些項目的開發人員很是重視軟件說明書的「正確性」,卻不多可以知足新的業務需求,也不多可以重構並從開發初期的錯誤中從新思考和學習。數據結構
但現實狀況是,軟件開發並非一個由定義和執行所組成的線性過程,而是一個演化過程,在開發團隊真正明白手頭的問題前,須要經歷與客戶溝通、向客戶學習和向客戶交付的數次迭代。架構
使用傳統的瀑布方法所面臨的挑戰在於,許多時候,這些項目交付的軟件製品的粒度具備如下特色。
緊耦合的——業務邏輯的調用發生在編程語言層面,而不是經過實現中立的協議(如SOAP和REST)。這大大增長了即便對應用程序組件進行小的修改也可能打破應用程序的其餘部分並引入新漏洞的機會。
有漏洞的——大多數大型軟件應用程序都在管理着不一樣類型的數據。例如,客戶關係管理(CRM)應用程序可能會管理客戶、銷售和產品信息。在傳統的模型裏,這些數據位於相同的數據模型中並在同一個數據存儲中保存。即便數據之間存在明顯的界限,在絕大多數的狀況下,來自一個領域的團隊也很容易直接訪問屬於另外一個團隊的數據。這種對數據的輕鬆訪問引入了隱藏的依賴關係,並讓組件的內部數據結構的實現細節泄漏到整個應用程序中。即便對單個數據庫表的更改也可能須要在整個應用程序中進行大量的代碼更改和迴歸測試。
單體的——因爲傳統應用程序的大多數組件都存放在多個團隊共享的單個代碼庫中,任什麼時候候更改代碼,整個應用程序都必須從新編譯、從新運行而且須要經過一個完整的測試周期並從新部署。不管是新客戶的需求仍是修復錯誤,應用程序代碼庫的微小變化都將變得昂貴和耗時,而且幾乎不可能及時實現大規模的變化。
基於微服務的架構採用不一樣的方法來交付功能。具體來講,基於微服務的架構具備如下特色。
有約束的——微服務具備範圍有限的單一職責集。微服務遵循UNIX的理念,即應用程序是服務的集合,每一個服務只作一件事,並只作好一件事。
鬆耦合的——基於微服務的應用程序是小型服務的集合,服務之間使用非專屬調用協議(如HTTP和REST)經過非特定實現的接口彼此交互。與傳統的應用程序架構相比,只要服務的接口沒有改變,微服務的全部者能夠更加自由地對服務進行修改。
抽象的——微服務徹底擁有本身的數據結構和數據源。微服務所擁有的數據只能由該服務修改。能夠鎖定微服務數據的數據庫訪問控制,僅容許該服務訪問它。
獨立的——微服務應用程序中的每一個微服務能夠獨立於應用程序中使用的其餘服務進行編譯和部署。這意味着,與依賴更重的單體應用程序相比,這樣對變化進行隔離和測試更容易。
爲何這些微服務架構屬性對基於雲的開發很重要?基於雲的應用程序一般有如下特色。
擁有龐大而多樣化的用戶羣——不一樣的客戶須要不一樣的功能,他們不想在開始使用這些功能以前等待漫長的應用程序發佈週期。微服務容許功能快速交付,由於每一個服務的範圍很小,並經過一個定義明確的接口進行訪問。
極高的運行時間要求——因爲微服務的分散性,基於微服務的應用程序能夠更容易地將故障和問題隔離到應用程序的特定部分之中,而不會使整個應用程序崩潰。這能夠減小應用程序的總體宕機時間,並使它們對問題更有抵禦能力。
不均勻的容量需求——在企業數據中心內部部署的傳統應用程序一般具備一致的使用模式,這些模式會隨着時間的推移而按期出現,這使這種類型的應用程序的容量規劃變得很簡單。但在一個基於雲的應用中,Twitter上的一條簡單推文或Slashdot上的一篇文章就可以極大帶動對基於雲計算的應用的需求。
由於微服務應用程序被分解成能夠彼此獨立部署的小組件,因此可以更容易將重點放在正處於高負載的組件上,並將這些組件在雲中的多個服務器上進行水平伸縮。
本文的內容會包含在業務問題中構建和識別微服務的基礎知識,構建微服務的骨架,而後理解在生產環境中成功部署和管理微服務的運維屬性。若是你想要更深刻了解,請直接讀《Spring微服務實戰》
要想成功設計和構建微服務,開發人員須要像警察向目睹證人訊問犯罪活動同樣着手處理微服務。即便每一個證人看到同一事件發生,他們對犯罪活動的解釋也是根據他們的背景、他們所看重的東西(例如,給予他們動機的東西),以及在那個時刻目擊這個事件所帶來的環境壓力塑造出來的。每一個參與者都有他們本身認爲重要的視角(和偏見)。
就像一名成功的警察試圖探尋真相同樣,構建一個成功的微服務架構的過程須要結合軟件開發組織內多我的的視角。儘管交付整個應用程序須要的不只僅是技術人員,但我相信,成功的微服務開發的基礎是從如下3個關鍵角色的視角開始的。
架構師——架構師的工做是看到大局,瞭解應用程序如何分解爲單個微服務,以及微服務如何交互以交付解決方案。
軟件開發人員——軟件開發人員編寫代碼並詳細瞭解如何將編程語言和該語言的開發框架用於交付微服務。
DevOps工程師——DevOps工程師不只爲生產環境並且爲全部非生產環境提供服務部署和管理的智慧。DevOps工程師的口號是:保障每一個環境中的一致性和可重複性。
本文將演示如何從這些角色的視角使用Spring Boot和Java設計和構建一組微服務。到本文結束時,讀者將有一個能夠打包並部署到雲的服務。
1.1架構師的故事:設計微服務架構
架構師在軟件項目中的做用是提供待解決問題的工做模型。架構師的工做是提供腳手架,開發人員將根據這些腳手架構建他們的代碼,使應用程序全部部件都組合在一塊兒。
在構建微服務架構時,項目的架構師主要關注如下3個關鍵任務:
(1)分解業務問題;
(2)創建服務粒度;
(3)定義服務接口。
1.1.1分解業務問題
面對複雜性,大多數人試圖將他們正在處理的問題分解成可管理的塊。由於這樣他們就沒必要努力把問題的全部細節都考慮進來。他們將問題抽象地分解成幾個關鍵部分,而後尋找這些部分之間存在的關係。
在微服務架構中,架構師將業務問題分解成表明離散活動領域的塊。這些塊封裝了與業務域特定部分相關聯的業務規則和數據邏輯。
雖然咱們但願微服務封裝執行單個事務的全部業務規則,但這並不老是行得通。咱們常常會遇到須要跨業務領域不一樣部分的一組微服務來完成整個事務的狀況。架構師經過查看數據域中那些不適合放到一塊兒的地方來劃分一組微服務的服務邊界。
例如,架構師可能會看到代碼執行的業務流程,並意識到它們同時須要客戶和產品信息。存在兩個離散的數據域時,一般就意味着須要使用多個微服務。業務事務的兩個不一樣部分如何交互一般成爲微服務的服務接口。
分離業務領域是一門藝術,而不是非黑即白的科學。讀者可使用如下指導方針將業務問題識別和分解爲備選的微服務。
(1)描述業務問題,並聆聽用來描述問題的名詞。在描述問題時,反覆使用的同一名詞一般意味着它們是核心業務領域而且適合建立微服務。第1章中EagleEye域的目標名詞可能會是合同、許可證和資產。
(2)注意動詞。動詞突出了動做,一般表明問題域的天然輪廓。若是發現本身說出「事務X須要從事物A和事物B獲取數據」這樣的話,一般代表多個服務正在起做用。若是把注意動詞的方法應用到EagleEye上,那麼就可能會查找像「來自桌面服務的Mike安裝新PC時,他會查找軟件X可用的許可證數量,若是有許可證,就安裝軟件。而後他更新了跟蹤電子表格中使用的許可證的數量」這樣的陳述句。這裏的關鍵動詞是查找和更新。
(3)尋找數據內聚。將業務問題分解成離散的部分時,要尋找彼此高度相關的數據。若是在會話過程當中,忽然讀取或更新與迄今爲止所討論的內容徹底不一樣的數據,那麼就可能還存在其餘候選服務。微服務應徹底擁有本身的數據。
讓咱們將這些指導方針應用到現實世界的問題中。第1章介紹了一種名爲EagleEye的現有軟件產品,該軟件產品用於管理軟件資產,如軟件許可證和安全套接字層(SSL)證書。這些軟件資產被部署到組織中的各類服務器上。
EagleEye是一個傳統的單體Web應用程序,部署在位於客戶數據中心內的J2EE應用程序服務器。咱們的目標是將現有的單體應用程序梳理成一組服務。
首先,咱們要採訪EagleEye應用程序的全部用戶,並討論他們是如何交互和使用EagleEye的。圖1-1描述了與不一樣業務客戶進行的對話的總結。經過查看EagleEye的用戶是如何與應用程序進行交互的,以及如何將應用程序的數據模型分解出來,能夠將EagleEye問題域分解爲如下備選微服務。
圖1-1 採訪EagleEye用戶,瞭解他們如何作平常工做
圖1-1強調了與業務用戶對話時出現的一些名詞和動詞。由於這是現有的應用程序,因此能夠查看應用程序並將主要名詞映射到物理數據模型中的表。現有應用程序可能有數百張表,但每張表一般會映射回一組邏輯實體。
圖1-2展現了基於與EagleEye客戶對話的簡化數據模型。基於業務對話和數據模型,備選微服務是組織、許可證、合同和資產服務。
圖1-2 簡化的EagleEye數據模型
1.1.2創建服務粒度
擁有了一個簡化的數據模型,就能夠開始定義在應用程序中須要哪些微服務。根據圖1-2中的數據模型,能夠看到潛在的4個微服務基於如下元素:
資產;
許可證;
合同;
組織。
咱們的目標是將這些主要的功能部件提取到徹底獨立的單元中,這些單元能夠獨立構建和部署。可是,從數據模型中提取服務須要的不僅是將代碼從新打包到單獨的項目中,還涉及梳理出服務訪問的實際數據庫表,而且只容許每一個單獨的服務訪問其特定域中的表。圖1-3展現了應用程序代碼和數據模型如何被「分塊」到各個部分。
圖1-3 將數據模型做爲把單體應用程序分解爲微服務的基礎
將問題域分解成不一樣的部分後,開發人員一般會發現本身不肯定是否爲服務劃分了適當的粒度級別。一個太粗粒度或太細粒度的微服務將具備不少的特徵,咱們將在稍後討論。
構建微服務架構時,粒度的問題很重要,能夠採用如下思想來肯定正確的解決方案。
(1)開始的時候可讓微服務涉及的範圍更普遍一些,而後將其重構到更小的服務——在開始微服務旅程之初,容易出現的一個極端狀況就是將全部的事情都變成微服務。可是將問題域分解爲小型的服務一般會致使過早的複雜性,由於微服務變成了細粒度的數據服務。
(2)重點關注服務如何相互交互——這有助於創建問題域的粗粒度接口。從粗粒度重構到細粒度是比較容易的。
(3)隨着對問題域的理解不斷增加,服務的職責將隨着時間的推移而改變——一般來講,當須要新的應用功能時,微服務就會承擔起職責。最初的微服務可能會發展爲多個服務,原始的微服務則充當這些新服務的編排層,負責將應用的其餘部分的功能封裝起來。
糟糕的微服務的「味道」
如何知道微服務的劃分是否正確?若是微服務過於粗粒度,可能會看到如下現象。
服務承擔過多的職責——服務中的業務邏輯的通常流程很複雜,而且彷佛正在執行一組過於多樣化的業務規則。
該服務正在跨大量表來管理數據——微服務是它管理的數據的記錄系統。若是發現本身將數據持久化存儲到多個表或接觸到當前數據庫之外的表,那麼這就是一條服務過於粗粒度的線索。我喜歡使用這麼一個指導方針——微服務應該不超過3~5個表。再多一點,服務就可能承擔了太多的職責。
測試用例太多——隨着時間的推移,服務的規模和職責會增加。若是一開始有一個只有少許測試用例的服務,到了最後該服務須要數百個單元測試用例和集成測試用例,那麼就可能須要重構。
若是微服務過於細粒度呢?
問題域的一部分微服務像兔子同樣繁殖——若是一切都成爲微服務,將服務中的業務邏輯組合起來會變得複雜和困難,由於完成一項工做所需的服務數量會快速增加。一種常見的「壞味道」出如今應用程序有幾十個微服務,而且每一個服務只與一個數據庫表進行交互時。
微服務彼此間嚴重相互依賴——在問題域的某一部分中,微服務相互來回調用以完成單個用戶請求。
微服務成爲簡單CRUD(Create,Read,Update,Delete)服務的集合——微服務是業務邏輯的表達,而不是數據源的抽象層。若是微服務除了CRUD相關邏輯以外什麼都不作,那麼它們可能被劃分得太細粒度了。
應該經過演化思惟的過程來開發一個微服務架構,在這個過程當中,你知道不會第一次就獲得正確的設計。這就是最好從一組粗粒度的服務而不是一組細粒度的服務開始的緣由。一樣重要的是,不要對設計帶有教條主義。咱們可能會面臨兩個單獨的服務之間交互過於頻繁,或者服務的域之間不存在明確的邊界這樣的物理約束,當面臨這樣的約束時,須要建立一個聚合服務來將數據鏈接在一塊兒。
最後,採起務實的作法並進行交付,而不是浪費時間試圖讓設計變得完美,最終致使沒有東西能夠展示你的努力。
1.1.3互相交流:定義服務接口
架構師須要關心的最後一部分,是應用程序中的微服務該如何彼此交流。使用微服務構建業務邏輯時,服務的接口應該是直觀的,開發人員應該經過學習應用程序中的一兩個服務來得到應用程序中全部服務的工做節奏。
通常來講,可以使用如下指導方針思考服務接口設計。
(1)擁抱REST的理念——REST對服務的處理方式是將HTTP做爲服務的調用協議並使用標準HTTP動詞(GET、PUT、POST和DELETE)。圍繞這些HTTP動詞對基本行爲進行建模。
(2)使用URI來傳達意圖——用做服務端點的URI應描述問題域中的不一樣資源,併爲問題域內的資源的關係提供一種基本機制。
(3)請求和響應使用JSON——JavaScript對象表示法(JavaScript Object Notation,JSON)是一個很是輕量級的數據序列化協議,而且比XML更容易使用。
(4)使用HTTP狀態碼來傳達結果——HTTP協議具備豐富的標準響應代碼,以指示服務的成功或失敗。學習這些狀態碼,而且最重要的是在全部服務中始終如一地使用它們。
全部這些指導方針都是爲了完成一件事,那就是使服務接口易於理解和使用。咱們但願開發人員坐下來查看一下服務接口就能開始使用它們。若是微服務不容易使用,開發人員就會另闢道路,破壞架構的意圖。
務。到本文結束時,讀者將有一個能夠打包並部署到雲的服務。
1.2什麼時候不該該使用微服務
接下來,讓咱們瞭解一下其中的考量因素:
(1)構建分佈式系統的複雜性;
(2)虛擬服務器/容器散亂;
(3)應用程序的類型;
(4)數據事務和一致性。
務。到本文結束時,讀者將有一個能夠打包並部署到雲的服務。
1.2.1構建分佈式系統的複雜性
由於微服務是分佈式和細粒度(小)的,因此它們在應用程序中引入了一層複雜性,而在單體應用程序中就不會出現這樣的狀況。微服務架構須要高度的運維成熟度。除非組織願意投入高分佈式應用程序得到成功所需的自動化和運維工做(監控、伸縮),不然不要考慮使用微服務。
1.2.2服務器散亂
微服務最經常使用的部署模式之一就是在一個服務器上部署一個微服務實例。在基於微服務的大型應用程序中,最終可能須要50~100臺服務器或容器(一般是虛擬的),這些服務器或容器必須單獨搭建和維護。即便在雲中運行這些服務的成本較低,管理和監控這些服務器的操做複雜性也是巨大的。
2.2.3 應用程序的類型
微服務面向可複用性,而且對構建須要高度彈性和可伸縮性的大型應用程序很是有用。這就是這麼多雲計算公司採用微服務的緣由之一。若是讀者正在構建小型的、部門級的應用程序或具備較小用戶羣的應用程序,那麼搭建一個分佈式模型(如微服務)的複雜性可能太昂貴了,不值得。
2.2.4 數據事務和一致性
開始關注微服務時,須要考慮服務的數據使用模式以及服務消費者如何使用它們。微服務包裝並抽象出少許的表,做爲執行「操做型」任務的機制,如建立、添加和執行鍼對存儲的簡單(非複雜的)查詢,其工做效果很好。
若是應用程序須要跨多個數據源進行復雜的數據聚合或轉換,那麼微服務的分佈式性質會讓這項工做變得很困難。這樣的微服務老是承擔太多的職責,也可能變得容易受到性能問題的影響。
還要記住,在微服務間執行事務沒有標準。若是須要事務管理,那就須要本身構建邏輯。另外,如第7章所述,微服務能夠經過使用消息進行通訊。消息傳遞在數據更新中引入了延遲。應用程序須要處理最終的一致性,數據的更新可能不會當即出現。
1.3開發人員的故事:用Spring Boot和Java構建微服務
在構建微服務時,從概念到實現,須要視角的轉換。具體來講,開發人員須要創建一個實現應用程序中每一個微服務的基本模式。雖然每項服務都將是獨一無二的,但咱們但願確保使用的是一個移除樣板代碼的框架,而且微服務的每一個部分都採用相同的佈局。
在本節中,咱們將探討開發人員從EagleEye域模型構建許可證微服務的優先事項。許可證服務將使用Spring Boot編寫。Spring Boot是標準Spring庫之上的一個抽象層,它容許開發人員快速構建基於Groovy和Java的Web應用程序和微服務,比成熟的Spring應用程序可以節省大量的配置。
對於許可證服務示例,這裏將使用Java做爲核心編程語言並使用Apache Maven做爲構建工具。
在接下來的幾節中,咱們將要完成如下幾項工做。
(1)構建微服務的基本框架並構建應用程序的Maven腳本。
(2)實現一個Spring引導類,它將啓動用於微服務的Spring容器,並啓動類的全部初始化工做。
(3)實現映射端點的Spring Boot控制器類,以公開服務的端點。
1.3.1 從骨架項目開始
首先,要爲許可證服務建立一個骨架項目。讀者能夠從本文的GitHub存儲庫拉取源代碼,也能夠建立具備如下目錄結構的許可證服務項目目錄:
licensing-service
src/main/java/com/thoughtmechanix/licenses
controllers
model
services
resources
一旦拉取或建立了這個目錄結構,就能夠開始爲項目編寫Maven腳本。這就是位於項目根目錄下的pom.xml文件。代碼清單1-1展現了許可證服務的Maven POM文件。
代碼清單1-1 許可證服務的Maven POM文件
這裏不會詳細討論整個腳本,可是在開始的時候要注意幾個關鍵的地方。Spring Boot被分解成許多個獨立的項目。其理念是,若是不須要在應用程序中使用Spring Boot的各個部分,那麼就不該該「拉取整個世界」。這也使不一樣的Spring Boot項目可以獨立地發佈新版本的代碼。爲了簡化開發人員的開發工做,Spring Boot團隊將相關的依賴項目收集到各類「起步」(starter)工具包中。Maven POM的第一部分告訴Maven須要拉取Spring Boot框架的1.4.4版本。
Maven文件的第二部分和第三部分肯定了要拉取Spring Web和Spring Actuator起步工具包。這兩個項目幾乎是全部基於Spring Boot REST服務的核心。讀者會發現,服務中構建功能越多,這些依賴項目的列表就會變得越長。
此外,Spring Source還提供了Maven插件,可簡化Spring Boot應用程序的構建和部署。第四部分告訴Maven構建腳本安裝最新的Spring Boot Maven插件。此插件包含許多附加任務(如spring-boot:run),能夠簡化Maven和Spring Boot之間的交互。
最後,讀者將看到一條註釋,說明Maven文件的哪些部分已被刪除。爲了簡化,本書沒有在代碼清單1-1中包含Spotify Docker插件。
1.3.2 引導Spring Boot應用程序:編寫引導類
咱們的目標是在Spring Boot中運行一個簡單的微服務,而後重複這個步驟以提供功能。爲此,咱們須要在許可證服務微服務中建立如下兩個類。
一個Spring引導類,可被Spring Boot用於啓動和初始化應用程序。
一個Spring控制器類,用來公開能夠被微服務調用的HTTP端點。
如剛纔所見,Spring Boot使用註解來簡化設置和配置服務。在代碼清單2-2中查看引導類時,這一點就變得顯然易見。這個引導類位於src/main/java/com/thoughtmechanix/licenses/Application.
java文件。
代碼清單1-2 @SpringBootApplication註解簡介
在這段代碼中須要注意的第一件事是@SpringBootApplication的用法。Spring Boot使用這個註解來告訴Spring容器,這個類是在Spring中使用的bean定義的源。在Spring Boot應用程序中,能夠經過如下方法定義Spring Bean。
(1)用@Component、@Service或@Repository註解標籤來標註一個Java類。
(2)用@Configuration註解標籤來標註一個類,而後爲每一個咱們想要構建的Spring Bean定義一個構造器方法併爲方法添加上@Bean標籤。
在幕後,@SpringBootApplication註解將代碼清單2-2中的Application類標記爲配置類,而後開始自動掃描Java類路徑上全部的類以造成其餘的Spring Bean。
第二件須要注意的事是Application類的main()方法。在main()方法中,Spring Application.run(Application.class, args)調用啓動了Spring容器,而後返回了一個SpringApplicationContext對象(這裏沒有使用ApplicationContext作任何事情,所以它沒有在代碼中展現。)。
關於@SpringBootApplication註解及其對應的Application類,最容易記住的是,它是整個微服務的引導類。服務的核心初始化邏輯應該放在這個類中。
1.3.3 構建微服務的入口:Spring Boot控制器
如今已經有了構建腳本,並實現了一個簡單的Spring Boot引導類,接下來就能夠開始編寫第一個代碼來作一些事情。這個代碼就是控制器類。在Spring Boot應用程序中,控制器類公開了服務端點,並將數據從傳入的HTTP請求映射到將處理該請求的Java方法。
第一個控制器類LicenseSerriceController位於src/main/java/com/thoughtmechanix/licenses/controllers/LicenseServiceController.java中。這個類將公開4個HTTP端點,這些端點將映射到POST、GET、PUT和DELETE動詞。
讓咱們看一下控制器類,看看Spring Boot如何提供一組註解,以保證花最少的努力公開服務端點,使開發人員可以集中精力構建服務的業務邏輯。咱們將從沒有任何類方法的基本控制器類定義開始。代碼清單1-3展現了爲許可證服務構建的控制器類。
代碼清單1-3 標記LicenseServiceController爲Spring RestController
咱們經過查看@RestController註解來開始探索。@RestController是一個類級Java註解,它告訴Spring容器這個Java類將用於基於REST的服務。此註解自動處理以JSON或XML方式傳遞到服務中的數據的序列化(在默認狀況下,@RestController類將返回的數據序列化爲JSON)。與傳統的Spring @Controller註解不一樣,@RestController註解並不須要開發者從控制器類返回ResponseBody類。這一切都由@RestController註解進行處理,它包含了@ResponseBody註解。
代碼清單1-3中展現的第二個註解是@RequestMapping。可使用@RequestMapping做爲類級註解和方法級註解。@RequestMapping註解用於告訴Spring容器該服務將要公開的HTTP端點。使用類級的@RequestMapping註解時,將爲該控制器公開的全部其餘端點創建URL的根。
在代碼清單1-3中,@RequestMapping(value="/v1/organizations/{organizationId}/licenses")使用value屬性爲控制器類中公開的全部端點創建URL的根。在此控制器中公開的全部服務端點將以/v1/organizations/{organizationId}/licenses做爲其端點的根。{organizationId}是一個佔位符,代表如何使用在每一個調用中傳遞的organizationId來參數化URL。在URL中使用organizationId能夠區分使用服務的不一樣客戶。
如今將添加控制器的第一個方法。這一方法將實現REST調用中的GET動詞,並返回單個License類實例,如代碼清單1-4所示(爲了便於討論,將實例化一個名爲License的Java類)。
代碼清單1-4 公開一個GET HTTP端點
這一代碼清單中完成的第一件事是,使用方法級的@RequestMapping註解來標記getLicenses()方法,將兩個參數傳遞給註解,即value和method。經過方法級的@Request Mapping註解,再結合類頂部指定的根級註解,咱們將全部傳入該控制器的HTTP請求與端點/v1/organizations/{organizationId}/licences/{licensedId}匹配起來。該註解的第二個參數method指定該方法將匹配的HTTP動詞。在前面的例子中,以RequestMethod. GET枚舉的形式匹配GET方法。
關於代碼清單 1-4,須要注意的第二件事是getLicenses()方法的參數體中使用了@PathVariable註解。@PathVariable註解用於將在傳入的URL中傳遞的參數值(由{parameterName}語法表示)映射爲方法的參數。在代碼清單1-4所示的示例代碼中,將兩個參數organizationId和licenseId映射到方法中的兩個參數級變量:
如今,能夠將咱們剛剛建立的東西稱爲服務。在命令行窗口中,轉到下載示例代碼的項目目錄,而後執行如下Maven命令:
一旦按下回車鍵,應該會看到Spring Boot啓動一個嵌入式Tomcat服務器,並開始監聽8080端口。
圖1-4 許可證服務成功運行
服務啓動後就能夠直接訪問公開的端點了。由於公開的第一個方法是GET調用,可使用多種方法來調用這一服務。個人首選方法是使用基於Chrome的工具,如POSTMAN或CURL來調用該服務。圖1-5展現了在http://localhost:8080/v1/organizations/
e254f8c-c442-4ebe-a82a-e2fc1d1ff78a/licenses/f3831f8c-c338-4ebe-a82a-e2fc1d1ff78a端點上完成的一個GET請求。
圖1-5 使用POSTMAN調用許可證服務
如今咱們已經有了一個服務的運行骨架。但從開發的角度來看,這服務還不完整。良好的微服務設計不可避免地將服務分紅定義明確的業務邏輯和數據訪問層。
咱們來看看最後一個視角——探索DevOps工程師如何實施服務並將其打包以部署到雲中。
1.4 DevOps工程師的故事:構建運行時的嚴謹性
對於DevOps工程師來講,微服務的設計關乎在投入生產後如何管理服務。編寫代碼一般是很簡單的,而保持代碼運行倒是困難的。
雖然DevOps是一個豐富而新興的IT領域,在本書後面,讀者將基於4條原則開始微服務開發工做並根據這些原則去構建。這些原則具體以下。
(1)微服務應該是獨立的和可獨立部署的,多個服務實例可使用單個軟件製品進行啓動和拆卸。
(2)微服務應該是可配置的。當服務實例啓動時,它應該從中央位置讀取須要配置其自身的數據,或者讓它的配置信息做爲環境變量傳遞。配置服務無需人爲干預。
(3)微服務實例須要對客戶端是透明的。客戶端不該該知道服務的確切位置。相反,微服務客戶端應該與服務發現代理通訊,該代理將容許應用程序定位微服務的實例,而沒必要知道微服務的物理位置。
(4)微服務應該傳達它的健康信息,這是雲架構的關鍵部分。一旦微服務實例沒法正常運行,客戶端須要繞過不良服務實例。
這4條原則揭示了存在於微服務開發中的悖論。微服務在規模和範圍上更小,但使用微服務會在應用程序中引入了更多的活動部件,特別是由於微服務在它本身的分佈式容器中彼此獨立地分佈和運行,引入了高度協調性的同時也更容易爲應用程序帶來故障點。
從DevOps的角度來看,必須解決微服務的運維需求,並將這4條原則轉化爲每次構建和部署微服務到環境中時發生的一系列生命週期事件。這4條原則能夠映射到如下運維生命週期步驟。
服務裝配——如何打包和部署服務以保證可重複性和一致性,以便相同的服務代碼和運行時被徹底相同地部署?
服務引導——如何將應用程序和環境特定的配置代碼與運行時代碼分開,以即可以在任何環境中快速啓動和部署微服務實例,而無需對配置微服務進行人爲干預?
服務註冊/發現——部署一個新的微服務實例時,如何讓新的服務實例能夠被其餘應用程序客戶端發現。
服務監控——在微服務環境中,因爲高可用性需求,同一服務運行多個實例很是常見。從DevOps的角度來看,須要監控微服務實例,並確保繞過微服務中的任何故障,並且情況不佳的服務實例會被拆卸。
圖1-6展現了這4個步驟是如何配合在一塊兒的。
圖1-6 當微服務啓動時,它將在其生命週期中經歷多個步驟
1.4.1 服務裝配:打包和部署微服務
從DevOps的角度來看,微服務架構背後的一個關鍵概念是能夠快速部署微服務的多個實例,以應對變化的應用程序環境(如用戶請求的忽然涌入、基礎設施內部的問題等)。
爲了實現這一點,微服務須要做爲帶有全部依賴項的單個製品進行打包和安裝,而後能夠將這個製品部署到安裝了Java JDK的任何服務器上。這些依賴項還包括承載微服務的運行時引擎(如HTTP服務器或應用程序容器)。
這種持續構建、打包和部署的過程就是服務裝配(圖1-6中的步驟1)。圖1-7展現了有關服務裝配步驟的其餘詳細信息。
圖1-7 在服務裝配步驟中,源代碼與其運行時引擎一塊兒被編譯和打包
幸運的是,幾乎全部的Java微服務框架都包含可使用代碼進行打包和部署的運行時引擎。例如,在圖2-7中的Spring Boot示例中,可使用Maven和Spring Boot構建一個可執行的Java JAR文件,該文件具備嵌入式的Tomcat引擎內置於其中。如下命令行示例將構建許可證服務做爲可執行JAR,而後從命令行啓動JAR文件:
對某些運維團隊來講,將運行時環境嵌入JAR文件中的理念是他們在部署應用程序時的重大轉變。在傳統的J2EE企業組織中,應用程序是被部署到應用程序服務器的。該模型意味着應用程序服務器自己是一個實體,而且一般由一個系統管理員團隊進行管理,這些管理員管理服務器的配置,而與被部署的應用程序無關。
在部署過程當中,應用程序服務器的配置與應用程序之間的分離可能會引入故障點,由於在許多組織中,應用程序服務器的配置不受源控制,而且經過用戶界面和本地管理腳本組合的方式進行管理。這很是容易在應用程序服務器環境中發生配置漂移,並忽然致使表面上看起來是隨機中斷的狀況。
將運行時引擎嵌入可部署製品中的作法消除了許多配置漂移的可能性。它還容許將整個製品置於源代碼控制之下,並容許應用程序團隊更好地思考他們的應用程序是如何構建和部署的。
1.4.2 服務引導:管理微服務的配置
服務引導(圖1-6中的步驟2)發生在微服務首次啓動並須要加載其應用程序配置信息的時候。圖1-8爲引導處理提供了更多的上下文。
圖1-8 服務啓動(引導)時,它會從中央存儲庫讀取其配置
任何應用程序開發人員都知道,有時須要使應用程序的運行時行爲可配置。一般這涉及從應用程序部署的屬性文件讀取應用程序的配置數據,或從數據存儲區(如關係數據庫)讀取數據。
微服務一般會遇到相同類型的配置需求。不一樣之處在於,在雲上運行的微服務應用程序中,可能會運行數百甚至數千個微服務實例。更爲複雜的是,這些服務可能分散在全球。因爲存在大量的地理位置分散的服務,從新部署服務以獲取新的配置數據變得難以實施。
將數據存儲在服務器外部的數據存儲中解決了這個問題,但云上的微服務提出了一系列獨特的挑戰。
(1)配置數據的結構每每是簡單的,一般讀取頻繁但不常常寫入。在這種狀況下,使用關係數據庫就是「殺雞用牛刀」,由於關係數據庫旨在管理比一組簡單的鍵值對更復雜的數據模型。
(2)由於數據是按期訪問的,可是不多更改,因此數據必須具備低延遲的可讀性。
(3)數據存儲必須具備高可用性,而且靠近讀取數據的服務。配置數據存儲不能徹底關閉,不然它將成爲應用程序的單點故障。
1.4.3 服務註冊和發現:客戶端如何與微服務通訊
從微服務消費者的角度來看,微服務應該是位置透明的,由於在基於雲的環境中,服務器是短暫的。短暫意味着承載服務的服務器一般比在企業數據中心運行的服務的壽命更短。能夠經過分配給運行服務的服務器的全新IP地址來快速啓動和拆除基於雲的服務。
經過堅持將服務視爲短暫的可自由處理的對象,微服務架構能夠經過運行多個服務實例來實現高度的可伸縮性和可用性。服務需求和彈性能夠在須要的狀況下儘快進行管理。每一個服務都有一個分配給它的惟一和非永久的IP地址。短暫服務的缺點是,隨着服務的不斷出現和消失,手動或手工管理大量的短暫服務容易形成運行中斷。
微服務實例須要向第三方代理註冊。此註冊過程稱爲服務發現(見圖1-6中的步驟3,以及圖1-9中有關此過程的詳細信息)。當微服務實例使用服務發現代理進行註冊時,微服務實例將告訴發現代理兩件事情:服務實例的物理IP地址或域名地址,以及應用程序能夠用來查找服務的邏輯名稱。某些服務發現代理還要求能訪問到註冊服務的URL,服務發現代理可使用此URL來執行健康檢查。
.圖1-9 服務發現代理抽象出服務的物理位置
而後,服務客戶端與發現代理進行通訊以查找服務的位置。
2.4.4 傳達微服務的「健康情況」
服務發現代理不僅是扮演了一名引導客戶端到服務位置的交通警察的角色。在基於雲的微服務應用程序中,一般會有多個服務實例運行,其中某些服務實例早晚會出現一些問題。服務發現代理監視其註冊的每一個服務實例的健康情況,並從其路由表中移除有問題的服務實例,以確保客戶端不會訪問已經發生故障的服務實例。
在發現微服務後,服務發現代理將繼續監視和ping健康檢查接口,以確保該服務可用。這是圖1-6中的步驟4。圖1-10提供了此步驟的上下文。
圖1-10 服務發現代理使用公開的健康情況URL來檢查微服務的「健康情況」
經過構建一致的健康檢查接口,咱們可使用基於雲的監控工具來檢測問題並對其進行適當的響應。
若是服務發現代理髮現服務實例存在問題,則能夠採起糾正措施,如關閉出現故障的實例或啓動另外的服務實例。
在使用REST的微服務環境中,構建健康檢查接口的最簡單的方法是公開可返回JSON淨荷和HTTP狀態碼的HTTP端點。在基於非Spring Boot的微服務中,開發人員一般須要編寫一個返回服務健康情況的端點。
在Spring Boot中,公開一個端點是很簡單的,只涉及修改Maven構建文件以包含Spring Actuator模塊。Spring Actuator提供了開箱即用的運維端點,可幫助用戶瞭解和管理服務的健康情況。要使用Spring Actuator,須要確保在Maven構建文件中包含如下依賴項:
若是訪問許可證服務上的http://localhost:8080/health端點,則應該會看到返回的健康情況數據。圖1-11提供了返回數據的示例。
圖1-11 服務實例的健康情況檢查使監視工具能肯定服務實例是否正在運行
如圖1-11所示,健康情況檢查不只僅是微服務是否在運行的指示器,它還能夠提供有關運行微服務實例的服務器狀態的信息,這樣能夠得到更豐富的監控體驗。
1.5將視角綜合起來
雲中的微服務看起來很簡單,但要想成功,卻須要有一個綜合的視角,將架構師、開發人員和DevOps工程師的視角融到一塊兒,造成一個緊密結合的視角。每一個視角的關鍵結論歸納以下。
(1)架構師——專一於業務問題的天然輪廓。描述業務問題域,並聽取別人所講述的故事,按照這種方式,篩選出目標備選微服務。還要記住,最好從「粗粒度」的微服務開始,並重構到較小的服務,而不是從一大批小型服務開始。微服務架構像大多數優秀的架構同樣,是按需調整的,而不是預先計劃好的。
(2)軟件工程師——儘管服務很小,但並不意味着就應該把良好的設計原則拋於腦後。專一於構建分層服務,服務中的每一層都有離散的職責。避免在代碼中構建框架的誘惑,並嘗試使每一個微服務徹底獨立。過早的框架設計和採用框架可能會在應用程序生命週期的後期產生巨大的維護成本。
(3)DevOps工程師——服務不存在於真空中。儘早創建服務的生命週期。DevOps視角不只要關注如何自動化服務的構建和部署,還要關注如何監控服務的健康情況,並在出現問題時作出反應。實施服務一般須要比編寫業務邏輯更多的工做,也更須要深謀遠慮。
1.6 小結
要想經過微服務得到成功,須要綜合架構師、軟件開發人員和DevOps的視角。
微服務是一種強大的架構範型,它有優勢和缺點。並不是全部應用程序都應該是微服務應用程序。
從架構師的角度來看,微服務是小型的、獨立的和分佈式的。微服務應具備狹窄的邊界,並管理一小組數據。
從開發人員的角度來看,微服務一般使用REST風格的設計構建,JSON做爲服務發送和接收數據的淨荷。
Spring Boot是構建微服務的理想框架,由於它容許開發人員使用幾個簡單的註解便可構建基於REST的JSON服務。
從DevOps的角度來看,微服務如何打包、部署和監控相當重要。
開箱即用。Spring Boot容許用戶用單個可執行JAR文件交付服務。JAR文件中的嵌入式Tomcat服務器承載該服務。
Spring Boot框架附帶的Spring Actuator會公開有關服務運行健康情況的信息以及有關服務運行時的信息。
《Spring微服務實戰》
[美]約翰•卡內爾(John Carnell)著
本書詳細介紹了微服務架構下Spring體系(Spring ->Spring Boot->Spring Cloud),幫助 Java 開發人員快速拆分單體應用,並對微服務的全生命流程進行了封裝,大大簡化了開發流程。
本書在構建和部署Spring雲應用程序的同時,讓讀者掌握如何進行微服務設計。整本書是一個完整的例子,傳授做者多年的寶貴經驗。
本書以一個名爲EagleEye的項目爲主線,介紹雲、微服務等概念以及Spring Boot和Spring Cloud等諸多Spring項目,並介紹如何將EagleEye項目一步一步地從單體架構重構成微服務架構,最終將這個項目拆分紅衆多微服務,讓它們運行在各自的Docker容器中,實現持續集成/持續部署,並最終自動部署到雲環境(Amazon)中。針對在重構過程當中遇到的各類微服務開發會面臨的典型問題(包括開發、測試和運維等問題),本書介紹瞭解決這些問題的核心模式,而後在實戰中選擇特定Spring Cloud子項目或其餘工具解決這些問題。
本文摘自異步社區,做品使用Spring Boot構建微服務(文末福利)未經受權,禁止轉載。
小福利
關注【異步社區】服務號,轉發本文至朋友圈或 50 人以上微信羣,截圖發送至異步社區服務號後臺,並在文章底下留言你學習微服務/架構或者試讀本書感覺,咱們將選出3名讀者贈送《Spring微服務實戰》1本,趕快積極參與吧!
活動截止時間:2018年6月14日
掃碼關注咱們
在「異步社區」後臺回覆「關注」,便可免費得到2000門在線視頻課程
閱讀原文,購買《Spring 微服務實戰》