| 在本教程中,我們將了解了解如何在Spring Boot的幫助下應(yīng)用十二因子方法開發(fā)微服務(wù)。 
 什么是十二因子方法論? 十二因子方法論是一組十二種最佳實(shí)踐,用于開發(fā)開發(fā)為作為服務(wù)運(yùn)行的應(yīng)用程序。該文件最初是由Heroku在2011年為在其云平臺上部署為服務(wù)的應(yīng)用程序起草的。隨著時(shí)間的推移,事實(shí)證明,這對于任何軟件即服務(wù)(SaaS)開發(fā)都是通用的。 那么,我們所說的軟件即服務(wù)是什么意思?傳統(tǒng)上,我們設(shè)計(jì),開發(fā),部署和維護(hù)軟件解決方案以從中獲取業(yè)務(wù)價(jià)值。但是,我們不必親自自己參與這個(gè)過程構(gòu)建,可以訂閱此類業(yè)務(wù)服務(wù)產(chǎn)品,此類服務(wù)產(chǎn)品就是我們所說的軟件即服務(wù)。 盡管軟件即服務(wù)對它所開發(fā)的體系結(jié)構(gòu)沒有任何限制;采用一些最佳做法非常有用。 如果我們將軟件設(shè)計(jì)為模塊化,可移植且可在現(xiàn)代云平臺上擴(kuò)展,則該軟件非常適合我們的服務(wù)產(chǎn)品。這就是十二因子方法的作用所在。我們將在本教程的后面部分看到它們的實(shí)際應(yīng)用。 
 使用Spring Boot的微服務(wù) 微服務(wù)是一種架構(gòu)風(fēng)格,可以將軟件開發(fā)為松散耦合的服務(wù)。這里的關(guān)鍵要求是服務(wù)應(yīng)該圍繞業(yè)務(wù)域邊界進(jìn)行組織。這通常是最難識別的部分。 而且,這里的服務(wù)對其數(shù)據(jù)擁有唯一的權(quán)限,并將操作公開給其他服務(wù)。服務(wù)之間的通信通常通過輕量級協(xié)議(例如HTTP)進(jìn)行。這導(dǎo)致可獨(dú)立部署和可擴(kuò)展的服務(wù)。 現(xiàn)在,微服務(wù)架構(gòu)和軟件即服務(wù)不再相互依賴。但是,不難理解,在開發(fā)軟件即服務(wù)時(shí),利用微服務(wù)架構(gòu)是非常有益的。它有助于實(shí)現(xiàn)我們前面討論的許多目標(biāo),例如模塊化和可伸縮性。 Spring Boot是基于Spring的應(yīng)用程序框架,它消除了與開發(fā)企業(yè)應(yīng)用程序相關(guān)的許多樣板。它為我們提供了一個(gè)具有高度針對性但又靈活的平臺來開發(fā)微服務(wù)。在本教程中,我們將利用Spring Boot通過十二因素方法來提供微服務(wù)。 
 應(yīng)用十二因子方法 我們需要一個(gè)簡單的服務(wù)來記錄和查詢我們觀看的電影,這是具有數(shù)據(jù)存儲和REST端點(diǎn)的相當(dāng)簡單和標(biāo)準(zhǔn)的微服務(wù)。我們需要定義一個(gè)模型,該模型也將映射到持久性: 
 
 我們已經(jīng)定義了一個(gè)具有ID和其他一些屬性的JPA實(shí)體?,F(xiàn)在讓我們看一下REST控制器的外觀: 
 這涵蓋了我們簡單服務(wù)的基礎(chǔ)。在下面的小節(jié)中,我們將討論本應(yīng)用程序的其余部分,以討論如何實(shí)現(xiàn)十二因子方法。 
 1.代碼庫 十二因子應(yīng)用程序的第一個(gè)最佳實(shí)踐是在版本控制系統(tǒng)中對其進(jìn)行跟蹤。Git是當(dāng)今使用最廣泛的版本控制系統(tǒng),幾乎無處不在。該原則指出,應(yīng)在單個(gè)代碼存儲庫中跟蹤某個(gè)應(yīng)用程序,并且不得與任何其他應(yīng)用程序共享該存儲庫。 Spring Boot提供了許多引導(dǎo)應(yīng)用程序的便捷方法,包括命令行工具和Web界面。生成引導(dǎo)應(yīng)用程序后,可以將其轉(zhuǎn)換為git存儲庫: 
 此命令應(yīng)從應(yīng)用程序的根目錄運(yùn)行。此階段的應(yīng)用程序已經(jīng)包含一個(gè).gitignore文件,該文件有效地限制了生成文件的版本控制。因此,我們可以立即創(chuàng)建一個(gè)初始提交: 
 最后,如果需要,我們可以添加一個(gè)遠(yuǎn)程并將提交提交到該遠(yuǎn)程(這不是嚴(yán)格的要求): 
 
 2. 依賴 接下來,十二因子應(yīng)用程序應(yīng)始終顯式聲明其所有依賴項(xiàng)。我們應(yīng)該使用依賴聲明清單來實(shí)現(xiàn)。Java有多個(gè)依賴管理工具,例如Maven和Gradle。我們可以使用其中之一來實(shí)現(xiàn)這一目標(biāo)。 因此,我們的簡單應(yīng)用程序依賴于一些外部庫,例如方便REST API和連接數(shù)據(jù)庫的庫。讓我們看看如何使用Maven聲明性地定義它們。 Maven要求我們在XML文件(通常稱為項(xiàng)目對象模型(POM))中描述項(xiàng)目的依賴項(xiàng): 
 
 盡管這看起來很簡單,但是這些依賴項(xiàng)通常還具有其他傳遞性依賴項(xiàng)。這在一定程度上使其復(fù)雜化,但可以幫助我們實(shí)現(xiàn)目標(biāo)?,F(xiàn)在,我們的應(yīng)用程序沒有直接依賴關(guān)系,因此沒有明確描述。 
 3.配置 一個(gè)應(yīng)用程序通常具有許多配置,其中一些配置可能因部署而異,而其他配置則保持不變。 在我們的示例中,我們有一個(gè)持久數(shù)據(jù)庫。我們需要數(shù)據(jù)庫的地址和憑據(jù)才能連接。這在部署之間最有可能改變。 十二個(gè)因子的應(yīng)用程序應(yīng)該外部化所有因部署而異的配置。建議在此配置中使用環(huán)境變量。這導(dǎo)致配置和代碼的清晰分離。 Spring提供了一個(gè)配置文件,我們可以在其中聲明此類配置并將其附加到環(huán)境變量: 
 在這里,我們將數(shù)據(jù)庫URL和憑據(jù)定義為配置,并映射了要從環(huán)境變量中選取的實(shí)際值。 在Windows上,我們可以在啟動應(yīng)用程序之前設(shè)置環(huán)境變量: 
 我們可以使用Ansible或Chef等配置管理工具來自動執(zhí)行此過程。 
 4.支持服務(wù) 支持服務(wù)是應(yīng)用程序依賴其進(jìn)行操作的服務(wù)。例如數(shù)據(jù)庫或消息代理。十二要素應(yīng)用程序應(yīng)將所有此類支持服務(wù)視為附加資源。這實(shí)際上意味著,它不需要更改任何代碼即可交換兼容的支持服務(wù)。唯一的變化應(yīng)該是配置。 在我們的應(yīng)用程序中,我們使用MySQL作為提供持久性的支持服務(wù)。 Spring JPA使代碼與實(shí)際的數(shù)據(jù)庫提供程序完全無關(guān)。我們只需要定義一個(gè)存儲庫即可提供所有標(biāo)準(zhǔn)操作: 
 如我們所見,這并不直接依賴于MySQL。Spring在類路徑上檢測MySQL驅(qū)動程序,并動態(tài)提供該接口的MySQL特定實(shí)現(xiàn)。而且,它直接從配置中提取其他細(xì)節(jié)。 因此,如果必須從MySQL更改為Oracle,我們要做的就是替換依賴項(xiàng)中的驅(qū)動程序并替換配置。 
 5.構(gòu)建、發(fā)布和運(yùn)行 十二要素方法嚴(yán)格將將代碼庫轉(zhuǎn)換為正在運(yùn)行的應(yīng)用程序的過程分為三個(gè)不同的階段: 
 最后,我們不必手動執(zhí)行這些步驟。這就是Jenkins 的聲明式管道非常方便的地方。 
 6. 處理進(jìn)程 一個(gè)十二因子應(yīng)用程序?qū)⒆鳛闊o狀態(tài)進(jìn)程在執(zhí)行環(huán)境中運(yùn)行。換句話說,它們不能在請求之間本地存儲持久狀態(tài)。它們可能會生成需要存儲在一個(gè)或多個(gè)有狀態(tài)支持服務(wù)中的持久性數(shù)據(jù)。 在我們的示例中,我們暴露了多個(gè)端點(diǎn)。這些端點(diǎn)中的任何一個(gè)上的請求完全獨(dú)立于在其之前進(jìn)行的任何請求。例如,如果我們跟蹤內(nèi)存中的用戶請求并使用該信息來滿足將來的請求,則它違反了十二因素應(yīng)用程序。 因此,十二要素應(yīng)用程序不會像粘性會話httpsession那樣對應(yīng)用程序產(chǎn)生任何限制。這使得此類應(yīng)用程序具有高度的可移植性和可擴(kuò)展性。在提供自動擴(kuò)展的云執(zhí)行環(huán)境中,這是應(yīng)用程序非常理想的行為。 
 7.端口綁定 Java中的傳統(tǒng)Web應(yīng)用程序被開發(fā)為WAR或Web存檔。這通常是具有相關(guān)性的Servlet的集合,并且它期望像Tomcat這樣的一致的容器運(yùn)行時(shí)。相反,十二要素應(yīng)用程序不希望有此類運(yùn)行時(shí)依賴項(xiàng)。它是完全獨(dú)立的,只需要像Java這樣的執(zhí)行運(yùn)行時(shí)。 在我們的案例中,我們使用Spring Boot開發(fā)了一個(gè)應(yīng)用程序。除了許多其他優(yōu)點(diǎn)之外,Spring Boot還為我們提供了默認(rèn)的嵌入式應(yīng)用程序服務(wù)器。因此,我們早先使用Maven生成的JAR僅具有兼容的Java運(yùn)行時(shí)就完全能夠在任何環(huán)境中執(zhí)行: 
 在這里,我們的簡單應(yīng)用程序通過HTTP綁定將其端點(diǎn)公開給特定端口,例如8080。像上面一樣啟動應(yīng)用程序后,應(yīng)該可以訪問導(dǎo)出的服務(wù),例如HTTP。 應(yīng)用程序可以通過綁定到多個(gè)端口來導(dǎo)出FTP或WebSocket之類的多個(gè)服務(wù)。 
 8. 并發(fā) Java提供了Thread作為經(jīng)典模型來處理應(yīng)用程序中的并發(fā)性。線程就像輕量級進(jìn)程,代表程序中的多個(gè)執(zhí)行路徑。線程功能強(qiáng)大,但是在幫助應(yīng)用程序擴(kuò)展方面有一定的限制。 十二要素方法論建議應(yīng)用程序依靠進(jìn)程進(jìn)行擴(kuò)展。這實(shí)際上意味著應(yīng)將應(yīng)用程序設(shè)計(jì)為在多個(gè)進(jìn)程之間分配工作負(fù)載。但是,各個(gè)進(jìn)程可以在內(nèi)部自由利用諸如Thread的并發(fā)模型。 Java應(yīng)用程序在啟動時(shí)會獲得綁定到底層JVM的單個(gè)進(jìn)程。我們實(shí)際上需要的是一種啟動應(yīng)用程序的多個(gè)實(shí)例并在它們之間進(jìn)行智能負(fù)載分配的方法。由于我們已經(jīng)將應(yīng)用程序打包為Docker容器,因此Kubernetes是這種編排的自然選擇。 
 9. Disposability可處理性/冪等性 可以有意或通過意外事件關(guān)閉應(yīng)用程序進(jìn)程。無論哪種情況,都應(yīng)該由十二個(gè)因素的應(yīng)用程序優(yōu)雅地處理。換句話說,關(guān)閉進(jìn)程應(yīng)完全是一次性的,沒有任何不希望的副作用。此外,進(jìn)程應(yīng)迅速啟動 例如,在我們的應(yīng)用程序中,端點(diǎn)之一是為電影創(chuàng)建新的數(shù)據(jù)庫記錄?,F(xiàn)在,處理此類請求的應(yīng)用程序可能會意外崩潰。但是,這不應(yīng)影響應(yīng)用程序的狀態(tài)。當(dāng)客戶端再次發(fā)送相同的請求時(shí),它不應(yīng)導(dǎo)致重復(fù)的記錄。 總之,應(yīng)用程序應(yīng)公開冪等性的服務(wù)。這是用于云部署的服務(wù)的另一個(gè)非常理想的屬性。這提供了隨時(shí)停止,移動或旋轉(zhuǎn)新服務(wù)的靈活性,而無需任何其他考慮。 
 10. Dev/Prod完整性 通常應(yīng)用程序是在本地計(jì)算機(jī)上開發(fā),然在其他一些環(huán)境上進(jìn)行測試并最終部署到生產(chǎn)環(huán)境中。這些環(huán)境通常是不同的情況。例如,開發(fā)團(tuán)隊(duì)在Windows計(jì)算機(jī)上工作,而生產(chǎn)部署在Linux計(jì)算機(jī)上進(jìn)行。 十二要素方法論建議將開發(fā)和生產(chǎn)環(huán)境之間的差距保持在最小。這些差距可能是由于開發(fā)周期長,涉及的團(tuán)隊(duì)不同或使用的技術(shù)堆棧不同而導(dǎo)致的。 現(xiàn)在,諸如Spring Boot和Docker之類的技術(shù)會在很大程度上彌補(bǔ)這一差距。無論我們在何處運(yùn)行,容器化應(yīng)用程序的行為都應(yīng)相同。我們還必須使用相同的支持服務(wù),例如數(shù)據(jù)庫。 此外,我們應(yīng)該有正確的流程,例如持續(xù)集成和交付,以促進(jìn)進(jìn)一步彌合這一差距。 
 11. 日志 日志是應(yīng)用程序在其生命周期內(nèi)生成的基本數(shù)據(jù)。他們提供了有關(guān)應(yīng)用程序工作的寶貴見解。通常,應(yīng)用程序可以在多個(gè)級別上生成具有不同詳細(xì)信息的日志,并以多種不同格式輸出。 但是,有十二個(gè)因素的應(yīng)用程序?qū)⒆陨砼c日志生成及其處理分開。對于這樣的應(yīng)用程序,日志只不過是按時(shí)間順序排列的事件流。它只是將這些事件寫到執(zhí)行環(huán)境的標(biāo)準(zhǔn)輸出中。此類流的捕獲,存儲,管理和存檔應(yīng)由執(zhí)行環(huán)境處理。 為此目的,我們可以使用多種工具。首先,我們可以使用SLF4J在我們的應(yīng)用程序中抽象地處理日志。此外,我們可以使用Fluentd之類的工具來收集來自應(yīng)用程序和支持服務(wù)的日志流。 我們可以將其輸入到Elasticsearch中進(jìn)行存儲和索引。最后,我們可以為Kibana中的可視化生成有意義的儀表板。 
 12. 管理例程 通常,我們需要根據(jù)應(yīng)用程序狀態(tài)執(zhí)行一些一次性任務(wù)或例行程序。例如,修復(fù)不良記錄?,F(xiàn)在,有多種方法可以實(shí)現(xiàn)這一目標(biāo)。由于我們可能并不經(jīng)常需要它,因此我們可以編寫一個(gè)小腳本來與其他環(huán)境分開運(yùn)行。 現(xiàn)在,十二因素方法強(qiáng)烈建議將此類管理腳本與應(yīng)用程序代碼庫一起保留。這樣,它應(yīng)遵循與應(yīng)用于主應(yīng)用程序代碼庫相同的原則。還建議使用執(zhí)行環(huán)境的內(nèi)置REPL工具在生產(chǎn)服務(wù)器上運(yùn)行此類腳本。 在我們的示例中,我們?nèi)绾嗡阉鞯侥壳盀橹挂呀?jīng)看過的電影?雖然我們可以使用我們的小端點(diǎn),但這似乎是不切實(shí)際的。我們需要一個(gè)腳本來執(zhí)行一次性加載。我們可以編寫一個(gè)小的Java函數(shù)來從文件中讀取電影列表并將其批量保存到數(shù)據(jù)庫中。 此外,我們可以使用與Java運(yùn)行時(shí)集成的Groovy來啟動此類過程。 
 實(shí)際應(yīng)用 因此,現(xiàn)在我們已經(jīng)看到了十二因素方法建議的所有因素。將應(yīng)用程序開發(fā)為包含十二個(gè)要素的應(yīng)用程序無疑具有其優(yōu)勢,尤其是當(dāng)我們希望將它們作為服務(wù)部署在云上時(shí)。但是,像所有其他準(zhǔn)則,框架,模式一樣,我們必須要問,這是靈丹妙藥嗎? 坦白說,沒有任何軟件設(shè)計(jì)和開發(fā)方法論可以說是萬靈丹。十二因素方法論也不例外。盡管其中一些因素非常直觀,并且很可能我們已經(jīng)在做這些,但其他因素可能不適用于我們。在我們的目標(biāo)背景下評估這些因素,然后進(jìn)行明智的選擇至關(guān)重要。 重要的是要注意,所有這些因素都可以幫助我們開發(fā)模塊化,獨(dú)立,可移植,可伸縮和可觀察的應(yīng)用程序。根據(jù)應(yīng)用程序的不同,我們也許可以通過其他方式更好地實(shí)現(xiàn)它們。也沒有必要將所有因素放在一起,即使采用其中一些因素也可能使我們比以前更好。 最后,這些因素非常簡單而優(yōu)雅。在我們要求應(yīng)用程序具有更高的吞吐量和更低的延遲而幾乎沒有停機(jī)和故障的時(shí)代,它們顯得尤為重要。采用這些因素為我們從一開始就提供了正確的開始。結(jié)合微服務(wù)體系結(jié)構(gòu)和應(yīng)用程序容器化,它們似乎是正確的選擇。 
 | 
|  |