Docker 專案

在本節中,我們發佈一個 springcloud/spring-cloud-contract Docker 映像檔,其中包含一個專案,該專案會產生測試並在 EXPLICIT 模式下針對正在執行的應用程式執行這些測試。

EXPLICIT 模式表示從合約產生的測試會傳送真實請求,而不是模擬請求。

我們也發佈一個 spring-cloud/spring-cloud-contract-stub-runner Docker 映像檔,該映像檔會啟動 Stub Runner 的獨立版本。

Maven、JAR 和二進位儲存簡介

由於非 JVM 專案可以使用 Docker 映像檔,因此最好解釋 Spring Cloud Contract 套件預設值背後的基本術語。

以下定義的部分內容取自 Maven 詞彙表

  • 專案:Maven 以專案的角度思考。專案是您建置的所有內容。這些專案遵循明確定義的「專案物件模型」。專案可以依賴其他專案,在這種情況下,後者稱為「相依性」。一個專案可能包含數個子專案。但是,這些子專案仍然被視為與專案相同。

  • Artifact:Artifact 是專案產生或使用的東西。Maven 為專案產生的 Artifact 範例包括 JAR 檔案以及原始碼和二進位發行版。每個 Artifact 都由群組 ID 和 Artifact ID 唯一識別,Artifact ID 在群組內是唯一的。

  • JAR:JAR 代表 Java ARchive。其格式基於 ZIP 檔案格式。Spring Cloud Contract 將合約和產生的 Stub 包裝在 JAR 檔案中。

  • GroupId:群組 ID 是專案的通用唯一識別碼。雖然這通常只是專案名稱(例如,commons-collections),但使用完整套件名稱來區分它與其他名稱相似的專案(例如,org.apache.maven)會很有幫助。通常,當發佈到 Artifact Manager 時,GroupId 會以斜線分隔,並構成 URL 的一部分。例如,對於群組 ID 為 com.example 和 Artifact ID 為 application,結果將為 /com/example/application/

  • Classifier:Maven 相依性表示法如下所示:groupId:artifactId:version:classifier。Classifier 是傳遞給相依性的額外後綴,例如,stubssources。相同的相依性(例如,com.example:application)可以產生多個 Artifact,這些 Artifact 因 Classifier 而彼此不同。

  • Artifact manager:當您產生二進位檔案、原始碼或套件時,您會希望它們可供其他人下載、參考或重複使用。在 JVM 環境中,這些 Artifact 通常是 JAR。對於 Ruby,這些 Artifact 是 gem。對於 Docker,這些 Artifact 是 Docker 映像檔。您可以將這些 Artifact 儲存在管理器中。此類管理器的範例包括 ArtifactoryNexus

在生產者端產生測試

映像檔會在 /contracts 資料夾下搜尋合約。執行測試的輸出可在 /spring-cloud-contract/build 資料夾中取得(對於偵錯目的很有用)。

您可以掛載您的合約並傳遞環境變數。然後映像檔會

  • 產生合約測試

  • 針對提供的 URL 執行測試

  • 產生 WireMock Stub

  • 將 Stub 發佈到 Artifact Manager(選用,預設為開啟)

環境變數

Docker 映像檔需要一些環境變數來指向您正在執行的應用程式、Artifact manager 執行個體等等。以下清單說明了環境變數

表 1. Docker 環境變數

名稱

說明

預設值

ADDITIONAL_FLAGS

(僅限 Docker 映像檔)要傳遞給 Gradle 建置的其他旗標

DEBUG

(僅限 Docker 映像檔)適用於 Docker 映像檔 - 開啟 Gradle 建置的偵錯模式

false

EXTERNAL_CONTRACTS_ARTIFACT_ID

具有合約的專案的 Artifact ID

EXTERNAL_CONTRACTS_CLASSIFIER

具有合約的專案的 Classifier

EXTERNAL_CONTRACTS_GROUP_ID

具有合約的專案的群組 ID

com.example

EXTERNAL_CONTRACTS_PATH

給定專案的合約路徑,位於具有合約的專案內。預設為以斜線分隔的 EXTERNAL_CONTRACTS_GROUP_ID 串連 /EXTERNAL_CONTRACTS_ARTIFACT_ID。例如,對於群組 ID `cat-server-side.dog` 和 Artifact ID fish,合約路徑的結果將為 cat/dog/fish

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_PASSWORD

(選用)如果 EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL 需要驗證的密碼。它預設為 `REPO_WITH_BINARIES_PASSWORD,如果未設定,則預設為 `password

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL

您的 Artifact Manager 的 URL。它預設為 REPO_WITH_BINARIES_URL 環境變數的值,如果未設定,則預設為 localhost:8081/artifactory/libs-release-local

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_USERNAME

(選用)如果 EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL 需要驗證的使用者名稱。它預設為 REPO_WITH_BINARIES_USERNAME。如果未設定,則預設為 `admin

EXTERNAL_CONTRACTS_VERSION

具有合約的專案版本。預設值相當於選擇最新版本

+

EXTERNAL_CONTRACTS_WORK_OFFLINE

如果設定為 true,則從容器的 .m2 擷取具有合約的 Artifact。將您的本機 .m2 掛載為可在容器的 /root/.m2 路徑上使用的磁碟區

false

FAIL_ON_NO_CONTRACTS

如果沒有合約,建置是否應該失敗?

false

MESSAGING_TYPE

訊息傳遞類型。可以是 [rabbit] 或 [kafka]。

PRODUCER_STUBS_CLASSIFIER

用於產生生產者 Stub 的封存分類器

stubs

PROJECT_GROUP

您的專案群組 ID

com.example

PROJECT_NAME

您的專案 Artifact ID

example

PROJECT_VERSION

您的專案版本

0.0.1-SNAPSHOT

PUBLISH_ARTIFACTS

如果設定為 true,則將 Artifact 發佈到二進位儲存空間

true

PUBLISH_ARTIFACTS_OFFLINE

如果設定為 true,則將 Artifact 發佈到本機 m2

false

PUBLISH_STUBS_TO_SCM

如果設定為 true,將執行將 Stub 發佈到 scm 的工作

false

REPO_ALLOW_INSECURE_PROTOCOL

(選用)如果 <true> 允許透過不安全的 HTTP 將 Artifact 發佈到 Artifact Manager

false

REPO_WITH_BINARIES_PASSWORD

(選用)Artifact Manager 受保護時的密碼

password

REPO_WITH_BINARIES_URL

您的 Artifact Manager 的 URL(預設為本機執行時 Artifactory 的預設 URL)

localhost:8081/artifactory/libs-release-local

REPO_WITH_BINARIES_USERNAME

(選用)Artifact Manager 受保護時的使用者名稱

admin

STANDALONE_PROTOCOL

對於獨立版本,應新增哪個額外協定

執行測試時會使用下列環境變數

表 2. Docker 環境變數 - 執行時讀取

名稱

說明

預設值

APPLICATION_BASE_URL

應用程式正在執行的 URL。

APPLICATION_PASSWORD

存取應用程式的選用密碼。

APPLICATION_USERNAME

存取應用程式的選用使用者名稱。

MESSAGING_TRIGGER_CONNECT_TIMEOUT

連線到應用程式以觸發訊息的逾時。

5000

MESSAGING_TRIGGER_READ_TIMEOUT

從應用程式讀取回應以觸發訊息的逾時。

5000

MESSAGING_TYPE

定義處理基於訊息的合約時的訊息傳遞類型。

MESSAGING_TYPE

訊息傳遞類型。可以是 [rabbit] 或 [kafka]。

SPRING_KAFKA_BOOTSTRAP_SERVERS

對於 Kafka - Broker 位址。

SPRING_RABBITMQ_ADDRESSES

對於 RabbitMQ - Broker 位址。

自訂 Gradle 建置

您可以提供自訂的 gradle.build 以在容器中執行,方法是在執行容器時將您的自訂建置檔案掛載為磁碟區

$ docker run -v <absolute-path-of-your-custom-file>:/spring-cloud-contract/build.gradle springcloud/spring-cloud-contract:<version>

透過 HTTP 使用範例

在本節中,我們將探索一個簡單的 MVC 應用程式。若要開始使用,請複製以下 Git 儲存庫並 cd 到產生的目錄,方法是執行以下命令

$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore

合約位於 /contracts 資料夾中。

由於我們要執行測試,因此我們可以執行以下命令

$ npm test

但是,為了學習目的,我們將其分為幾個部分,如下所示

# Stop docker infra (nodejs, artifactory)
$ ./stop_infra.sh
# Start docker infra (nodejs, artifactory)
$ ./setup_infra.sh

# Kill & Run app
$ pkill -f "node app"
$ nohup node app &

# Prepare environment variables
$ SC_CONTRACT_DOCKER_VERSION="..."
$ APP_IP="192.168.0.100"
$ APP_PORT="3000"
$ ARTIFACTORY_PORT="8081"
$ APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
$ ARTIFACTORY_URL="http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local"
$ CURRENT_DIR="$( pwd )"
$ CURRENT_FOLDER_NAME=${PWD##*/}
$ PROJECT_VERSION="0.0.1.RELEASE"

# Run contract tests
$ docker run  --rm -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" -e "PUBLISH_ARTIFACTS=true" -e "PROJECT_NAME=${CURRENT_FOLDER_NAME}" -e "REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}" -e "PROJECT_VERSION=${PROJECT_VERSION}" -v "${CURRENT_DIR}/contracts/:/contracts:ro" -v "${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/" springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"

# Kill app
$ pkill -f "node app"

透過 Bash 指令碼,會發生以下情況

  • 基礎結構(MongoDb 和 Artifactory)已設定完成。在實際情境中,您將使用模擬資料庫執行 NodeJS 應用程式。在本範例中,我們想要展示如何在極短的時間內從 Spring Cloud Contract 受益。

  • 由於這些限制,合約也代表有狀態的情況。

    • 第一個請求是 POST,它會導致資料插入資料庫。

    • 第二個請求是 GET,它會傳回包含 1 個先前插入元素的資料清單。

  • NodeJS 應用程式已啟動(在連接埠 3000 上)。

  • 合約測試是透過 Docker 產生的,並且測試是針對正在執行的應用程式執行的。

    • 合約取自 /contracts 資料夾。

    • 測試的輸出可在 node_modules/spring-cloud-contract/output 下取得。

  • Stub 已上傳到 Artifactory。您可以在 localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/ 中找到它們。Stub 位於 localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar

透過訊息傳遞使用範例

如果您想要透過 Docker 映像檔將 Spring Cloud Contract 與訊息傳遞搭配使用(例如,在多語言應用程式的情況下),那麼您必須符合以下先決條件

  • 中介軟體(例如 RabbitMQ 或 Kafka)必須在產生測試之前執行

  • 您的合約需要呼叫方法 triggerMessage(…​),其 String 參數等於合約的 label

  • 您的應用程式需要具有 HTTP 端點,我們可以透過該端點觸發訊息

    • 該端點不應在生產環境中可用(可以透過環境變數啟用)

訊息傳遞合約範例

合約需要呼叫 triggerMessage(…​) 方法。該方法已在 Docker 映像檔中所有測試的基底類別中提供,並將向生產者端的 HTTP 端點傳送請求。您可以在下面找到此類合約的範例。

  • Groovy

  • YAML

import org.springframework.cloud.contract.spec.Contract

Contract.make {
    description 'Send a pong message in response to a ping message'
    label 'ping_pong'
    input {
        // You have to provide the `triggerMessage` method with the `label`
        // as a String parameter of the method
        triggeredBy('triggerMessage("ping_pong")')
    }
    outputMessage {
        sentTo('output')
        body([
            message: 'pong'
        ])
    }
    metadata(
        [amqp:
         [
           outputMessage: [
               connectToBroker: [
                   declareQueueWithName: "queue"
               ],
                messageProperties: [
                    receivedRoutingKey: '#'
                ]
           ]
         ]
        ])
}
description: 'Send a pong message in response to a ping message'
label: 'ping_pong'
input:
    # You have to provide the `triggerMessage` method with the `label`
    # as a String parameter of the method
    triggeredBy: 'triggerMessage("ping_pong")'
outputMessage:
    sentTo: 'output'
    body:
        message: 'pong'
metadata:
    amqp:
        outputMessage:
            connectToBroker:
                declareQueueWithName: "queue"
            messageProperties:
                receivedRoutingKey: '#'

觸發訊息的 HTTP 端點

為何需要開發這樣的端點?Spring Cloud Contract 必須以各種語言(如 Java 中所做的那樣)產生程式碼,才能觸發將訊息傳送到 Broker 的生產程式碼。如果未產生此類程式碼,那麼我們仍然需要能夠觸發訊息,而執行此操作的方法是提供使用者將以其選擇的語言準備的 HTTP 端點。

端點必須具有以下組態

  • URL: /springcloudcontract/{label},其中 label 可以是任何文字

  • 方法:POST

  • 根據 label,將產生一則訊息,該訊息將根據合約定義傳送到給定的目的地

以下是此類端點的範例。如果您有興趣以您的語言提供範例,請隨時在 Github 上的 Spring Cloud Contract 儲存庫中提交問題。

Python
#!/usr/bin/env python

from flask import Flask
from flask import jsonify
import pika
import os

app = Flask(__name__)

# Production code that sends a message to RabbitMQ
def send_message(cmd):
    connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
    channel = connection.channel()
    channel.basic_publish(
        exchange='output',
        routing_key='#',
        body=cmd,
        properties=pika.BasicProperties(
            delivery_mode=2,  # make message persistent
        ))
    connection.close()
    return " [x] Sent via Rabbit: %s" % cmd

# This should be ran in tests (shouldn't be publicly available)
if 'CONTRACT_TEST' in os.environ:
    @app.route('/springcloudcontract/<label>', methods=['POST'])
    def springcloudcontract(label):
        if label == "ping_pong":
            return send_message('{"message":"pong"}')
        else:
            raise ValueError('No such label expected.')

在生產者端執行訊息測試

現在,讓我們從合約產生測試以測試生產者端。我們將執行 Bash 程式碼以啟動附加了合約的 Docker 映像檔,但是我們也將為訊息傳遞程式碼新增變數以使其運作。在本例中,讓我們假設合約儲存在 Git 儲存庫中。

#!/bin/bash
set -x

CURRENT_DIR="$( pwd )"

export SC_CONTRACT_DOCKER_VERSION="${SC_CONTRACT_DOCKER_VERSION:-4.0.1-SNAPSHOT}"
export APP_IP="$( ./whats_my_ip.sh )"
export APP_PORT="${APP_PORT:-8000}"
export APPLICATION_BASE_URL="http://${APP_IP}:${APP_PORT}"
export PROJECT_GROUP="${PROJECT_GROUP:-group}"
export PROJECT_NAME="${PROJECT_NAME:-application}"
export PROJECT_VERSION="${PROJECT_VERSION:-0.0.1-SNAPSHOT}"
export PRODUCER_STUBS_CLASSIFIER="${PRODUCER_STUBS_CLASSIFIER:-stubs}"
export FAIL_ON_NO_CONTRACTS="${FAIL_ON_NO_CONTRACTS:-false}"
# In our Python app we want to enable the HTTP endpoint
export CONTRACT_TEST="true"
# In the Verifier docker container we want to add support for RabbitMQ
export MESSAGING_TYPE="rabbit"

# Let's start the infrastructure (e.g. via Docker Compose)
yes | docker-compose kill || echo "Nothing running"
docker-compose up -d

echo "SC Contract Version [${SC_CONTRACT_DOCKER_VERSION}]"
echo "Application URL [${APPLICATION_BASE_URL}]"
echo "Project Version [${PROJECT_VERSION}]"

# Let's run python app
gunicorn -w 4 --bind 0.0.0.0 main:app &
APP_PID=$!

# Generate and run tests
docker run  --rm \
                --name verifier \
                # For the image to find the RabbitMQ running in another container
                -e "SPRING_RABBITMQ_ADDRESSES=${APP_IP}:5672" \
                # We need to tell the container what messaging middleware we will use
                -e "MESSAGING_TYPE=${MESSAGING_TYPE}" \
                -e "PUBLISH_STUBS_TO_SCM=false" \
                -e "PUBLISH_ARTIFACTS=false" \
                -e "APPLICATION_BASE_URL=${APPLICATION_BASE_URL}" \
                -e "PROJECT_NAME=${PROJECT_NAME}" \
                -e "PROJECT_GROUP=${PROJECT_GROUP}" \
                -e "PROJECT_VERSION=${PROJECT_VERSION}" \
                -e "EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL=git://https://github.com/marcingrzejszczak/cdct_python_contracts.git" \
                -e "EXTERNAL_CONTRACTS_ARTIFACT_ID=${PROJECT_NAME}" \
                -e "EXTERNAL_CONTRACTS_GROUP_ID=${PROJECT_GROUP}" \
                -e "EXTERNAL_CONTRACTS_VERSION=${PROJECT_VERSION}" \
                -v "${CURRENT_DIR}/build/spring-cloud-contract/output:/spring-cloud-contract-output/" \
                springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"

kill $APP_PID

yes | docker-compose kill

將會發生什麼事

  • 測試將從取自 Git 的合約產生

  • 在合約中,我們提供了一個名為 declareQueueWithName 的中繼資料項目,這將導致在發送觸發訊息的要求之前,在 RabbitMQ 中建立具有給定名稱的佇列

  • 透過 triggerMessage("ping_pong") 方法呼叫,將向 Python 應用程式的 /springcloudcontract/ping_pong 端點發出 POST 請求

  • Python 應用程式將產生並透過 RabbitMQ 將 '{"message":"pong"}' JSON 傳送到名為 output 的交換器

  • 產生的測試將輪詢傳送到 output 交換器的訊息

  • 收到訊息後,將判斷其內容

測試通過後,我們知道訊息已從 Python 應用程式正確傳送到 RabbitMQ。

在消費者端執行 Stub

本節說明如何在消費者端使用 Docker 來擷取和執行 Stub。

我們發佈一個 spring-cloud/spring-cloud-contract-stub-runner Docker 映像檔,該映像檔會啟動 Stub Runner 的獨立版本。

安全性

由於 Spring Cloud Contract Stub Runner Docker 映像檔使用 Stub Runner 的獨立版本,因此需要考慮相同的安全性考量。您可以在 文件的這個章節中閱讀更多相關資訊。

環境變數

您可以執行 Docker 映像檔並傳遞任何 JUnit 和 Spring 的通用屬性作為環境變數。慣例是所有字母都應該大寫。點 (.) 應替換為底線 (_) 字元。例如,stubrunner.repositoryRoot 屬性應表示為 STUBRUNNER_REPOSITORY_ROOT 環境變數。

除了這些變數之外,您還可以設定以下變數

  • MESSAGING_TYPE - 您正在使用的訊息傳遞系統類型(目前支援 rabbitkafka

  • ADDITIONAL_OPTS - 您想要傳遞給應用程式的任何其他屬性

使用範例

我們想要使用在此 [docker-server-side] 步驟中建立的 Stub。假設我們想要在連接埠 9876 上執行 Stub。您可以透過複製儲存庫並變更為以下命令中指示的目錄來查看 NodeJS 程式碼

$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ cd bookstore

現在我們可以透過執行以下命令,使用 Stub 執行 Stub Runner Boot 應用程式

# Provide the Spring Cloud Contract Docker version
$ SC_CONTRACT_DOCKER_VERSION="..."
# The IP at which the app is running and Docker container can reach it
$ APP_IP="192.168.0.100"
# Spring Cloud Contract Stub Runner properties
$ STUBRUNNER_PORT="8083"
# Stub coordinates 'groupId:artifactId:version:classifier:port'
$ STUBRUNNER_IDS="com.example:bookstore:0.0.1.RELEASE:stubs:9876"
$ STUBRUNNER_REPOSITORY_ROOT="http://${APP_IP}:8081/artifactory/libs-release-local"
# Run the docker with Stub Runner Boot
$ docker run  --rm \
    -e "STUBRUNNER_IDS=${STUBRUNNER_IDS}" \
    -e "STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}" \
    -e "STUBRUNNER_STUBS_MODE=REMOTE" \
    -p "${STUBRUNNER_PORT}:${STUBRUNNER_PORT}" \
    -p "9876:9876" \
    springcloud/spring-cloud-contract-stub-runner:"${SC_CONTRACT_DOCKER_VERSION}"

當先前的命令執行時,

  • 獨立的 Stub Runner 應用程式已啟動。

  • 它會在連接埠 9876 上下載座標為 com.example:bookstore:0.0.1.RELEASE:stubs 的 Stub。

  • 它從在 192.168.0.100:8081/artifactory/libs-release-local 執行的 Artifactory 下載。

  • 稍後,Stub Runner 會在連接埠 8083 上執行。

  • Stub 在連接埠 9876 上執行。

在伺服器端,我們建置了一個有狀態的 Stub。我們可以使用 curl 來判斷 Stub 是否設定正確。若要執行此操作,請執行以下命令

# let's run the first request (no response is returned)
$ curl -H "Content-Type:application/json" -X POST --data '{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }' http://localhost:9876/api/books
# Now time for the second request
$ curl -X GET http://localhost:9876/api/books
# You will receive contents of the JSON
如果您想要使用您在本機主機上建置的 Stub,您應該設定 -e STUBRUNNER_STUBS_MODE=LOCAL 環境變數並掛載本機 m2 的磁碟區 (-v "${HOME}/.m2/:/home/scc/.m2:rw")。

搭配訊息傳遞使用範例

為了使訊息傳遞運作,只需傳遞具有 kafkarabbit 值的 MESSAGING_TYPE 環境變數就足夠了。這將導致使用連線到 Broker 所需的相依性來設定 Stub Runner Boot Docker 映像檔。

為了設定連線屬性,您可以查看 Spring Cloud Stream 屬性頁面以設定正確的環境變數。

您將設定的最常見屬性是正在執行的中介軟體的位置。如果設定它的屬性稱為 spring.rabbitmq.addressesspring.kafka.bootstrap-servers,那麼您應該將環境變數命名為 SPRING_RABBITMQ_ADDRESSESSPRING_KAFKA_BOOTSTRAP_SERVERS

針對現有中介軟體執行合約測試

有合理的理由針對現有的中介軟體執行您的合約測試。某些測試架構可能會給您錯誤的正面結果 - 建置中的測試通過,但在生產環境中,通訊失敗。

在 Spring Cloud Contract Docker 映像檔中,我們提供連線到現有中介軟體的選項。如先前小節所示,我們確實開箱即用地支援 Kafka 和 RabbitMQ。但是,透過 Apache Camel Components,我們也可以支援其他中介軟體。讓我們看一下以下使用範例。

Spring Cloud Contract Docker 和執行中介軟體

為了連線到任意中介軟體,我們將利用合約區段中的 standalone 中繼資料項目。

description: 'Send a pong message in response to a ping message'
label: 'standalone_ping_pong' (1)
input:
  triggeredBy: 'triggerMessage("ping_pong")' (2)
outputMessage:
  sentTo: 'rabbitmq:output' (3)
  body: (4)
    message: 'pong'
metadata:
  standalone: (5)
    setup: (6)
      options: rabbitmq:output?queue=output&routingKey=(7)
    outputMessage: (8)
      additionalOptions: routingKey=#&queue=output (9)
1 我們將能夠透過 Stub Runner 觸發訊息的標籤
2 與先前的訊息傳遞範例一樣,我們需要觸發正在執行的應用程式中的 HTTP 端點,以使其根據提供的協定傳送訊息
3 Apache Camel 要求的 protocol:destination
4 輸出訊息本文
5 獨立中繼資料項目
6 設定部分將包含有關如何在實際呼叫正在執行的應用程式的 HTTP 端點之前準備執行合約測試的資訊
7 設定階段中要呼叫的 Apache Camel URI。在本例中,我們將嘗試輪詢 output 交換器的訊息,並且由於具有 queue=outputroutingKey=,因此將設定名稱為 output 的佇列,並將其繫結到路由金鑰為 output 交換器
8 要附加到點 (3) 中的 protocol:destination 的其他選項(更技術性的選項)- 將以以下格式組合在一起 rabbitmq:output?routingKey=#&queue=output

為了使合約測試通過,我們將需要像往常一樣在多語言環境中進行訊息傳遞,一個正在執行的應用程式和一個正在執行的中介軟體。這次我們將為 Spring Cloud Contract Docker 映像檔設定不同的環境變數。

#!/bin/bash
set -x

# Setup
# Run the middleware
docker-compose up -d rabbitmq (1)

# Run the python application
gunicorn -w 4 --bind 0.0.0.0 main:app & (2)
APP_PID=$!

docker run  --rm \
                --name verifier \
                -e "STANDALONE_PROTOCOL=rabbitmq" \ (3)
                -e "CAMEL_COMPONENT_RABBITMQ_ADDRESSES=172.18.0.1:5672" \ (4)
                -e "PUBLISH_STUBS_TO_SCM=false" \
                -e "PUBLISH_ARTIFACTS=false" \
                -e "APPLICATION_BASE_URL=172.18.0.1" \
                -e "PROJECT_NAME=application" \
                -e "PROJECT_GROUP=group" \
                -e "EXTERNAL_CONTRACTS_ARTIFACT_ID=application" \
                -e "EXTERNAL_CONTRACTS_GROUP_ID=group" \
                -e "EXTERNAL_CONTRACTS_VERSION=0.0.1-SNAPSHOT" \
                -v "${CURRENT_DIR}/build/spring-cloud-contract/output:/spring-cloud-contract-output/" \
                springcloud/spring-cloud-contract:"${SC_CONTRACT_DOCKER_VERSION}"


# Teardown
kill $APP_PID
yes | docker-compose kill
1 我們需要先執行中介軟體
2 應用程式需要啟動並執行
3 透過 STANDALONE_PROTOCOL 環境變數,我們將擷取 Apache Camel Component。我們將擷取的 Artifact 是 org.apache.camel.springboot:camel-${STANDALONE_PROTOCOL}-starter。換句話說,STANDALONE_PROTOCOL 與 Camel 的元件相符。
4 我們透過 Camel 的 Spring Boot Starter 機制設定位址(我們可以設定認證)。Apache Camel 的 RabbitMQ Spring Boot 自動組態範例

Stub Runner Docker 和執行中介軟體

為了針對正在執行的中介軟體觸發 Stub 訊息,我們可以透過以下方式執行 Stub Runner Docker 映像檔。

使用範例

$ docker run \
    -e "CAMEL_COMPONENT_RABBITMQ_ADDRESSES=172.18.0.1:5672" \ (1)
    -e "STUBRUNNER_IDS=group:application:0.0.1-SNAPSHOT" \ (2)
    -e "STUBRUNNER_REPOSITORY_ROOT=git://https://github.com/marcingrzejszczak/cdct_python_contracts.git" \ (3)
    -e ADDITIONAL_OPTS="--thin.properties.dependencies.rabbitmq=org.apache.camel.springboot:camel-rabbitmq-starter:3.4.0" \ (4)
    -e "STUBRUNNER_STUBS_MODE=REMOTE" \ (5)
    -v "${HOME}/.m2/:/home/scc/.m2:rw" \ (6)
    -p 8750:8750 \ (7)
    springcloud/spring-cloud-contract-stub-runner:3.0.4-SNAPSHOT (8)
1 我們透過 Apache Camel 的 RabbitMQ Spring Boot 自動組態注入 RabbitMQ 的位址
2 我們告訴 Stub Runner 要下載哪些 Stub
3 我們為 Stub 提供外部位置(Git 儲存庫)
4 透過 ADDITIONAL_OPTS=--thin.properties.dependencies.XXX=GROUP:ARTIFACT:VERSION 屬性,我們告訴 Stub Runner 在運行時要額外獲取哪個依賴項。在本例中,我們想要獲取 camel-rabbitmq-starter,因此 XXX 是一個隨機字串,而我們想要獲取版本為 3.4.0org.apache.camel.springboot:camel-rabbitmq-starter 構件。
5 由於我們正在使用 Git,因此需要設定獲取 Stub 的遠端選項
6 為了加速 Stub Runner 的啟動,我們將本機 Maven 儲存庫 .m2 掛載為 volume。如果您沒有預先填充它,您可以考慮透過 :rw 設定寫入權限,而不是唯讀 :ro
7 我們公開 Stub Runner 運行的埠 8750
8 Stub Runner Docker 映像檔的座標。

稍後您會在控制台中注意到以下文字,這表示 Stub Runner 已準備好接受請求。

o.a.c.impl.engine.AbstractCamelContext   : Apache Camel 3.4.3 (camel-1) started in 0.007 seconds
o.s.c.c.s.server.StubRunnerBoot          : Started StubRunnerBoot in 14.483 seconds (JVM running for 18.666)
o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
o.s.web.servlet.DispatcherServlet        : Completed initialization in 2 ms

若要取得觸發器的清單,您可以發送 HTTP GET 請求至 localhost:8750/triggers 端點。若要觸發 Stub 訊息,您可以發送 HTTP POST 請求至 localhost:8750/triggers/standalone_ping_pong。在控制台中您會看到

o.s.c.c.v.m.camel.CamelStubMessages      : Will send a message to URI [rabbitmq:output?routingKey=#&queue=output]

如果您檢查 RabbitMQ 管理控制台,您會看到 output 佇列中有 1 則訊息。