Spring Boot微服務中的十二因子方法論(12Factor) - Baeldung

banq發表於2019-09-30

在本教程中,我們將瞭解瞭解如何在Spring Boot的幫助下應用十二因子方法開發微服務。

什麼是十二因子方法論?

十二因子方法論是一組十二種最佳實踐,用於開發開發為作為服務執行的應用程式。該檔案最初是由Heroku在2011年為在其雲平臺上部署為服務的應用程式起草的。隨著時間的推移,事實證明,這對於任何軟體即服務(SaaS)開發都是通用的。

那麼,我們所說的軟體即服務是什麼意思?傳統上,我們設計,開發,部署和維護軟體解決方案以從中獲取業務價值。但是,我們不必親自自己參與這個過程構建,可以訂閱此類業務服務產品,此類服務產品就是我們所說的軟體即服務。

儘管軟體即服務對它所開發的體系結構沒有任何限制;採用一些最佳做法非常有用。

如果我們將軟體設計為模組化,可移植且可在現代雲平臺上擴充套件,則該軟體非常適合我們的服務產品。這就是十二因子方法的作用所在。我們將在本教程的後面部分看到它們的實際應用。

使用Spring Boot的微服務

微服務是一種架構風格,可以將軟體開發為鬆散耦合的服務。這裡的關鍵要求是服務應該圍繞業務域邊界進行組織。這通常是最難識別的部分。

而且,這裡的服務對其資料擁有唯一的許可權,並將操作公開給其他服務。服務之間的通訊通常通過輕量級協議(例如HTTP)進行。這導致可獨立部署和可擴充套件的服務。

現在,微服務架構和軟體即服務不再相互依賴。但是,不難理解,在開發軟體即服務時,利用微服務架構是非常有益的。它有助於實現我們前面討論的許多目標,例如模組化和可伸縮性。

Spring Boot是基於Spring的應用程式框架,它消除了與開發企業應用程式相關的許多樣板。它為我們提供了一個具有高度針對性但又靈活的平臺來開發微服務。在本教程中,我們將利用Spring Boot通過十二因素方法來提供微服務。

應用十二因子方法

我們需要一個簡單的服務來記錄和查詢我們觀看的電影,這是具有資料儲存和REST端點的相當簡單和標準的微服務。我們需要定義一個模型,該模型也將對映到永續性:

@Entity
public class Movie {
    @Id
    private Long id;
    private String title;
    private String year;
    private String rating;
    // getters and setters
}

我們已經定義了一個具有ID和其他一些屬性的JPA實體。現在讓我們看一下REST控制器的外觀:

@RestController
public class MovieController {
  
    @Autowired
    private MovieRepository movieRepository;
    @GetMapping("/movies")
    public List<Movie> retrieveAllStudents() {
        return movieRepository.findAll();
    }
 
    @GetMapping("/movies/{id}")
    public Movie retrieveStudent(@PathVariable Long id) {
        return movieRepository.findById(id).get();
    }
 
    @PostMapping("/movies")
    public Long createStudent(@RequestBody Movie movie) {
        return movieRepository.save(movie).getId();
    }
}

這涵蓋了我們簡單服務的基礎。在下面的小節中,我們將討論本應用程式的其餘部分,以討論如何實現十二因子方法。

1.程式碼庫

十二因子應用程式的第一個最佳實踐是在版本控制系統中對其進行跟蹤。Git是當今使用最廣泛的版本控制系統,幾乎無處不在。該原則指出,應在單個程式碼儲存庫中跟蹤某個應用程式,並且不得與任何其他應用程式共享該儲存庫。

Spring Boot提供了許多引導應用程式的便捷方法,包括命令列工具和Web介面。生成引導應用程式後,可以將其轉換為git儲存庫:

git init

此命令應從應用程式的根目錄執行。此階段的應用程式已經包含一個.gitignore檔案,該檔案有效地限制了生成檔案的版本控制。因此,我們可以立即建立一個初始提交:

git add .
git commit -m "Adding the bootstrap of the application."

最後,如果需要,我們可以新增一個遠端並將提交提交到該遠端(這不是嚴格的要求):

git remote add origin https://github.com/<username>/12-factor-app.git
git push -u origin master

2. 依賴

接下來,十二因子應用程式應始終顯式宣告其所有依賴項。我們應該使用依賴宣告清單來實現。Java有多個依賴管理工具,例如Maven和Gradle。我們可以使用其中之一來實現這一目標。

因此,我們的簡單應用程式依賴於一些外部庫,例如方便REST API和連線資料庫的庫。讓我們看看如何使用Maven宣告性地定義它們。

Maven要求我們在XML檔案(通常稱為專案物件模型(POM))中描述專案的依賴項:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
</dependencies>

儘管這看起來很簡單,但是這些依賴項通常還具有其他傳遞性依賴項。這在一定程度上使其複雜化,但可以幫助我們實現目標。現在,我們的應用程式沒有直接依賴關係,因此沒有明確描述。

3.配置

一個應用程式通常具有許多配置,其中一些配置可能因部署而異,而其他配置則保持不變。

在我們的示例中,我們有一個持久資料庫。我們需要資料庫的地址和憑據才能連線。這在部署之間最有可能改變。

十二個因子的應用程式應該外部化所有因部署而異的配置。建議在此配置中使用環境變數。這導致配置和程式碼的清晰分離。

Spring提供了一個配置檔案,我們可以在其中宣告此類配置並將其附加到環境變數:

spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/movies
spring.datasource.username=${MYSQL_USER}
spring.datasource.password=${MYSQL_PASSWORD}

在這裡,我們將資料庫URL和憑據定義為配置,並對映了要從環境變數中選取的實際值。

在Windows上,我們可以在啟動應用程式之前設定環境變數:

set MYSQL_HOST=localhost
set MYSQL_PORT=3306
set MYSQL_USER=movies
set MYSQL_PASSWORD=password

我們可以使用AnsibleChef等配置管理工具來自動執行此過程。

4.支援服務

支援服務是應用程式依賴其進行操作的服務。例如資料庫或訊息代理。十二要素應用程式應將所有此類支援服務視為附加資源。這實際上意味著,它不需要更改任何程式碼即可交換相容的支援服務。唯一的變化應該是配置。

在我們的應用程式中,我們使用MySQL作為提供永續性的支援服務。

Spring JPA使程式碼與實際的資料庫提供程式完全無關。我們只需要定義一個儲存庫即可提供所有標準操作:

@Repository
public interface MovieRepository extends JpaRepository<Movie, Long> {
}

如我們所見,這並不直接依賴於MySQL。Spring在類路徑上檢測MySQL驅動程式,並動態提供該介面的MySQL特定實現。而且,它直接從配置中提取其他細節。

因此,如果必須從MySQL更改為Oracle,我們要做的就是替換依賴項中的驅動程式並替換配置。

5.構建、釋出和執行

十二要素方法嚴格將將程式碼庫轉換為正在執行的應用程式的過程分為三個不同的階段:

  • 構建階段:在這裡,我們獲取程式碼庫,執行靜態和動態檢查,然後生成可執行檔案包,例如JAR。使用Maven之類的工具,這很簡單:

    mvn clean compile test package

  • 釋出階段:這是我們獲取可執行包並將其與正確的配置結合在一起的階段。在這裡,我們可以將Packer與類似Ansible的供應器一起使用來建立Docker映象:

    packer build application.json

  • 執行階段:最後,這是我們在目標執行環境中執行應用程式的階段。如果我們使用Docker作為釋出應用程式的容器,則執行該應用程式可能非常簡單:

    docker run --name <container_id> -it <image_id>

最後,我們不必手動執行這些步驟。這就是Jenkins 的宣告式管道非常方便的地方。

6. 處理程式

一個十二因子應用程式將作為無狀態程式在執行環境中執行。換句話說,它們不能在請求之間本地儲存持久狀態。它們可能會生成需要儲存在一個或多個有狀態支援服務中的永續性資料。

在我們的示例中,我們暴露了多個端點。這些端點中的任何一個上的請求完全獨立於在其之前進行的任何請求。例如,如果我們跟蹤記憶體中的使用者請求並使用該資訊來滿足將來的請求,則它違反了十二因素應用程式。

因此,十二要素應用程式不會像粘性會話httpsession那樣對應用程式產生任何限制。這使得此類應用程式具有高度的可移植性和可擴充套件性。在提供自動擴充套件的雲執行環境中,這是應用程式非常理想的行為。

7.埠繫結

Java中的傳統Web應用程式被開發為WAR或Web存檔。這通常是具有相關性的Servlet的集合,並且它期望像Tomcat這樣的一致的容器執行時。相反,十二要素應用程式不希望有此類執行時依賴項。它是完全獨立的,只需要像Java這樣的執行執行時。

在我們的案例中,我們使用Spring Boot開發了一個應用程式。除了許多其他優點之外,Spring Boot還為我們提供了預設的嵌入式應用程式伺服器。因此,我們早先使用Maven生成的JAR僅具有相容的Java執行時就完全能夠在任何環境中執行:

java -jar application.jar

在這裡,我們的簡單應用程式通過HTTP繫結將其端點公開給特定埠,例如8080。像上面一樣啟動應用程式後,應該可以訪問匯出的服務,例如HTTP。

應用程式可以通過繫結到多個埠來匯出FTP或WebSocket之類的多個服務。

8. 併發

Java提供了Thread作為經典模型來處理應用程式中的併發性。執行緒就像輕量級程式,代表程式中的多個執行路徑。執行緒功能強大,但是在幫助應用程式擴充套件方面有一定的限制。

十二要素方法論建議應用程式依靠程式進行擴充套件。這實際上意味著應將應用程式設計為在多個程式之間分配工作負載。但是,各個程式可以在內部自由利用諸如Thread的併發模型。

Java應用程式在啟動時會獲得繫結到底層JVM的單個程式。我們實際上需要的是一種啟動應用程式的多個例項並在它們之間進行智慧負載分配的方法。由於我們已經將應用程式打包為Docker容器,因此Kubernetes是這種編排的自然選擇。

9. Disposability可處理性/冪等性

可以有意或通過意外事件關閉應用程式程式。無論哪種情況,都應該由十二個因素的應用程式優雅地處理。換句話說,關閉程式應完全是一次性的,沒有任何不希望的副作用。此外,程式應迅速啟動

例如,在我們的應用程式中,端點之一是為電影建立新的資料庫記錄。現在,處理此類請求的應用程式可能會意外崩潰。但是,這不應影響應用程式的狀態。當客戶端再次傳送相同的請求時,它不應導致重複的記錄。

總之,應用程式應公開冪等性的服務。這是用於雲部署的服務的另一個非常理想的屬性。這提供了隨時停止,移動或旋轉新服務的靈活性,而無需任何其他考慮。

10. Dev/Prod完整性

通常應用程式是在本地計算機上開發,然在其他一些環境上進行測試並最終部署到生產環境中。這些環境通常是不同的情況。例如,開發團隊在Windows計算機上工作,而生產部署在Linux計算機上進行。

十二要素方法論建議將開發和生產環境之間的差距保持在最小。這些差距可能是由於開發週期長,涉及的團隊不同或使用的技術堆疊不同而導致的。

現在,諸如Spring Boot和Docker之類的技術會在很大程度上彌補這一差距。無論我們在何處執行,容器化應用程式的行為都應相同。我們還必須使用相同的支援服務,例如資料庫。

此外,我們應該有正確的流程,例如持續整合和交付,以促進進一步彌合這一差距。

11. 日誌

日誌是應用程式在其生命週期內生成的基本資料。他們提供了有關應用程式工作的寶貴見解。通常,應用程式可以在多個級別上生成具有不同詳細資訊的日誌,並以多種不同格式輸出。

但是,有十二個因素的應用程式將自身與日誌生成及其處理分開。對於這樣的應用程式,日誌只不過是按時間順序排列的事件流。它只是將這些事件寫到執行環境的標準輸出中。此類流的捕獲,儲存,管理和存檔應由執行環境處理。

為此目的,我們可以使用多種工具。首先,我們可以使用SLF4J在我們的應用程式中抽象地處理日誌。此外,我們可以使用Fluentd之類的工具來收集來自應用程式和支援服務的日誌流。

我們可以將其輸入到Elasticsearch中進行儲存和索引。最後,我們可以為Kibana中的視覺化生成有意義的儀表板。

12. 管理例程

通常,我們需要根據應用程式狀態執行一些一次性任務或例行程式。例如,修復不良記錄。現在,有多種方法可以實現這一目標。由於我們可能並不經常需要它,因此我們可以編寫一個小指令碼來與其他環境分開執行。

現在,十二因素方法強烈建議將此類管理指令碼與應用程式程式碼庫一起保留。這樣,它應遵循與應用於主應用程式程式碼庫相同的原則。還建議使用執行環境的內建REPL工具在生產伺服器上執行此類指令碼。

在我們的示例中,我們如何搜尋到目前為止已經看過的電影?雖然我們可以使用我們的小端點,但這似乎是不切實際的。我們需要一個指令碼來執行一次性載入。我們可以編寫一個小的Java函式來從檔案中讀取電影列表並將其批量儲存到資料庫中。

此外,我們可以使用與Java執行時整合的Groovy來啟動此類過程。

實際應用

因此,現在我們已經看到了十二因素方法建議的所有因素。將應用程式開發為包含十二個要素的應用程式無疑具有其優勢,尤其是當我們希望將它們作為服務部署在雲上時。但是,像所有其他準則,框架,模式一樣,我們必須要問,這是靈丹妙藥嗎?

坦白說,沒有任何軟體設計和開發方法論可以說是萬靈丹。十二因素方法論也不例外。儘管其中一些因素非常直觀,並且很可能我們已經在做這些,但其他因素可能不適用於我們。在我們的目標背景下評估這些因素,然後進行明智的選擇至關重要。

重要的是要注意,所有這些因素都可以幫助我們開發模組化,獨立,可移植,可伸縮和可觀察的應用程式。根據應用程式的不同,我們也許可以通過其他方式更好地實現它們。也沒有必要將所有因素放在一起,即使採用其中一些因素也可能使我們比以前更好。

最後,這些因素非常簡單而優雅。在我們要求應用程式具有更高的吞吐量和更低的延遲而幾乎沒有停機和故障的時代,它們顯得尤為重要。採用這些因素為我們從一開始就提供了正確的開始。結合微服務體系結構和應用程式容器化,它們似乎是正確的選擇。

 

相關文章