使用Spring Boot的消費者驅動合同

karspb發表於2021-09-09

在本文中,我們將討論消費者驅動開發的細節。

#問題

主要問題是基於API介面上的消費者和生產者之間的衝突,當開發任何api時,你應該考慮的是你的客戶的舒適度。如果你所做的更改打破了客戶端的體驗,那完全是一個笑話,本文討論了消費者和生產者服務之間這種協議挑戰。

#解決之道

消費者驅動合同(CDC)是確保生產者和消費者之間、分散式系統或微服務中基於HTTP或基於訊息或基於事件的軟體之間的執行正確的協議合約或合同。

Spring Cloud Contract是基於JVM的語言的CDC開發的實現,它也支援非基於jvm的語言,它將TDD提升到軟體(api)設計和架構的水平,我稱之為CDC - > Client Driven Development,因為是客戶端(消費者)驅動生產者API的變化。

#為什麼?

有關上面提到的一些問題和解決方案,你可能想為什麼我們需要這種方法?消費者和生產者之間的握手在微服務架構設計存在一些挑戰,因為,生產者所做的改變很難在消費者方面進行測試。見下面這個微服務圖片:

圖片描述

當試圖測試與許多其他服務進行通訊的應用程式時,我們可以在沒有消費者驅動合同的情況下做以下事情:

1. 部署所有微服務並執行端到端測試。

2. 在測試中模擬Mock其他服務。

兩種方法都有其優點和缺點。

部署所有微服務;

優點 - >模擬生產,測試實際服務,更可靠

缺點 - >長期執行,難以除錯,許多成本(部署許多應用程式,許多資源,如資料庫,快取等),存在非常晚的反饋

模擬Mock其他服務;

優點 - >非常快速的反饋,無需設定基礎設施

缺點 - >不可靠,你可以對K8s的prod測試和失敗檢查

為解決這些問題,建立了Spring Cloud Contract。

我在開發應用程式的新功能時遵循這些步驟;

1. 準備合同。

2. 從準備好的合同中生成測試。

3. 採用TDD和紅綠色風格編碼。

4. 功能完成後,你可以建立存根(合約)jar。

5. 消費者可以使用這個存根罐來整合api。

好吧,讓我們按這個步驟編碼;

首先,我們需要使用Groovy DSL建立合同,如下所示;

importorg.springframework.cloud.contract.spec.Contract

Contract.make {

description"should retrieve account"

request {

method GET()

headers {

accept(applicationJson())

}

url ('/api/v1/accounts'){

queryParameters {

parameter(

"accountId",1L)

}

}

}

response {

status OK()

headers {

contentType(applicationJson())

}

body(file(

"retrieveAccountResponse.json"))

}

}

這是一個在生產者端檢索帳戶的合同定義,我們為檢索帳戶製作了這個樣本合同。請求'api/v1/accounts'端點是使用HTTP get方法和查詢引數'accountId。

響應中指定Http標頭為json,最後我們期望響應狀態應該是正常的(200)並且響應頭contentType應該是json並且響應體應該等於json響應檔案。響應內容如下;

{

"name": "Name",

"surname": "Surname",

"gender": "Gender",

"gsmNumber": "GsmNumber",

"identifier": "Identifier",

"createdDate":1514851199,

"updatedDate":1514851199

}

在生成測試類之前,我們應該配置合同外掛;


org.springframework.cloud

spring-cloud-starter-contract-verifier

test




org.springframework.boot

spring-boot-maven-plugin


org.springframework.cloud

spring-cloud-contract-maven-plugin

true



com.caysever.producer.ProducerBaseContractTest

EXPLICIT

生產者方面的pom.xml

你可以使用TDD樣式從合同和編碼中生成測試類,這可以透過generateTests maven目標來做到這一點 - > mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:2.0.1.RELEASE:generateTests

生成的測試置於target/generated-test-sources下,生成測試如下程式碼:

publicclassContractVerifierTestextendsProducerBaseContractTest {

@Test

publicvoidvalidate_retrieveAccountContract() throws Exception {

//given:

RequestSpecification request =given()

.header(

"Accept", "application/json");

//when:

Response response =given().spec(request)

.queryParam(

"accountId","1")

.get(

"/api/v1/accounts");

//then:

assertThat(response.statusCode()).isEqualTo(200);

assertThat(response.header(

"Content-Type")).matches("application/json.*");

//and:

DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());

assertThatJson(parsedJson).field(

"['surname']").isEqualTo("Surname");

assertThatJson(parsedJson).field(

"['updatedDate']").isEqualTo(1514851199);

assertThatJson(parsedJson).field(

"['gender']").isEqualTo("Gender");

assertThatJson(parsedJson).field(

"['name']").isEqualTo("Name");

assertThatJson(parsedJson).field(

"['createdDate']").isEqualTo(1514851199);

assertThatJson(parsedJson).field(

"['identifier']").isEqualTo("Identifier");

assertThatJson(parsedJson).field(

"['gsmNumber']").isEqualTo("GsmNumber");

}

你現在可以將其作為正常的junit測試執行,測試透過後,可以與你的客戶和消費者分享你的合同。

注意:

當修改端點(如重新命名url或新增/刪除引數)時,應修改合同,如果你不修改,則構建無法透過。

Spring cloud contract外掛為你生成存根stub的jar包,可以將其部署到artifactory或本地參考local repo,Spring雲契約支援不同的存根模式,例如classpath或本地m2 repo或遠端artifactory(神器?),我們這裡將使用本地m2模式。

讓我們看看如何消費存根stub:

@ExtendWith(SpringExtension.class)

@AutoConfigureWebTestClient

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)

@AutoConfigureStubRunner(ids ="com.caysever:producer:+:8090", stubsMode = StubRunnerProperties.StubsMode.LOCAL)

classProducerVerifierTest {

@Autowired

privateWebTestClient webTestClient;

@Test

voidshould_retrieveAccountById() {

//given

        Long accountId = 1L;


//when

        Account account = webTestClient.get()

                .uri(

"{accountId}", accountId)

                .accept(MediaType.APPLICATION_JSON)

                .exchange()

                .expectStatus().isOk()

                .expectBody(Account.class)

                .returnResult()

                .getResponseBody();


//then

        assertThat(account).isNotNull();

        assertThat(account.getName()).isEqualTo(

"Name");

        assertThat(account.getSurname()).isEqualTo(

"Surname");

        assertThat(account.getGender()).isEqualTo(

"Gender");

        assertThat(account.getGsmNumber()).isEqualTo(

"GsmNumber");

        assertThat(account.getIdentifier()).isEqualTo(

"Identifier");

        assertThat(account.getCreatedDate()).isEqualTo(1514851199);

        assertThat(account.getUpdatedDate()).isEqualTo(1514851199);

    }

}

使用@AutoConfigureStubRunner註釋Spring設定wiremock伺服器,真正的生產者api應該在8090埠,我們建立REST http請求並斷言響應資料。如果任何步驟發生失敗,則測試交付的CI / CD管道都不會透過,即使在你的本地環境而不是CI伺服器上



作者:Java高階技術
連結:


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/2001/viewspace-2820014/,如需轉載,請註明出處,否則將追究法律責任。

相關文章