Docker 项目

在本节中,我们将发布一个 Docker 镜像 ,其中包含一个项目,该项目生成测试并在 针对正在运行的应用程序。springcloud/spring-cloud-contractEXPLICITspring-doc.cn

该模式意味着从 Contract 生成的测试发送 真实的请求,而不是嘲笑的请求。EXPLICIT

我们还发布了一个 Docker 镜像 ,这将启动 Stub Runner 的独立版本。spring-cloud/spring-cloud-contract-stub-runnerspring-doc.cn

Maven、JAR 和二进制存储简介

由于非 JVM 项目可以使用 Docker 镜像,因此最好 解释 Spring Cloud Contract 打包默认值背后的基本术语。spring-doc.cn

以下定义的一部分摘自 Maven 词汇表spring-doc.cn

  • Project:Maven 从项目的角度思考。项目 都是你构建的全部。这些项目遵循定义明确的 “项目对象模型”。项目可以依赖于其他项目 — 在这种情况下,后者称为 “依赖项”。一个项目可以 多个子项目的一致性。但是,这些子项目仍然是 平等地视为项目。spring-doc.cn

  • Artifact:工件是生产或使用的东西 按项目。Maven 为项目生成的工件示例 包括 JAR 文件以及源代码和二进制分发。每个工件 由组 ID 和工件 ID 唯一标识,该 ID 为 在组中唯一。spring-doc.cn

  • JAR:JAR 代表 Java ARchive。其格式基于 ZIP 文件格式。Spring Cloud Contract 将 Contract 打包并生成 stub 中的 stub 文件。spring-doc.cn

  • GroupId:组 ID 是项目的通用唯一标识符。 虽然这通常只是项目名称(例如,), 使用完全限定的包名称来区分它很有帮助 从具有相似名称的其他项目(例如 )中获取。 通常,当发布到 Artifact Manager 时,获取 斜杠分隔,并构成 URL 的一部分。例如,对于组 ID 和工件 ID ,结果将为 。commons-collectionsorg.apache.mavenGroupIdcom.exampleapplication/com/example/application/spring-doc.cn

  • Classifier:Maven 依赖关系表示法如下所示:.分类器是一个附加后缀 传递给依赖项 — 例如,或 .相同的依赖项 (例如,) 可以生成多个工件,这些工件 分类器彼此不同。groupId:artifactId:version:classifierstubssourcescom.example:applicationspring-doc.cn

  • Artifact manager:生成二进制文件、源或包时,您将 喜欢它们以供其他人下载、引用或重复使用。在这种情况下 在 JVM 世界中,这些工件通常是 JAR。对于 Ruby 来说,这些工件就是宝石。 对于 Docker,这些工件是 Docker 镜像。您可以存储这些工件 在经理中。此类管理人的示例包括 ArtifactoryNexusspring-doc.cn

在生产者端生成测试

该图像在文件夹下搜索合同。 运行测试的输出在文件夹(对调试很有用 目的)。/contracts/spring-cloud-contract/buildspring-doc.cn

您可以挂载 Contract 并传递环境变量。 然后,图像:spring-doc.cn

环境变量

Docker 镜像需要一些环境变量来指向 您正在运行的应用程序,到 Artifact Manager 实例,依此类推。 以下列表描述了环境变量:spring-doc.cn

表 1.Docker 环境变量

名字spring-doc.cn

描述spring-doc.cn

违约spring-doc.cn

ADDITIONAL_FLAGSspring-doc.cn

(仅限 Docker 映像)要传递给 Gradle 构建的其他标志spring-doc.cn

调试spring-doc.cn

(仅限 Docker 映像)适用于 Docker 映像 - 打开 Gradle 构建的调试模式spring-doc.cn

spring-doc.cn

EXTERNAL_CONTRACTS_ARTIFACT_IDspring-doc.cn

包含合同的项目的工件 IDspring-doc.cn

EXTERNAL_CONTRACTS_CLASSIFIERspring-doc.cn

具有 Contract 的项目的分类器spring-doc.cn

EXTERNAL_CONTRACTS_GROUP_IDspring-doc.cn

包含合同的项目的组 IDspring-doc.cn

com.示例spring-doc.cn

EXTERNAL_CONTRACTS_PATHspring-doc.cn

给定项目的合同路径,在具有 Contract 的项目内。默认为以斜杠分隔的 and 和 artifact ID 连接,将导致 contracts 路径。EXTERNAL_CONTRACTS_GROUP_ID/EXTERNAL_CONTRACTS_ARTIFACT_ID. For example, for group id `cat-server-side.dogfishcat/dog/fishspring-doc.cn

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_PASSWORDspring-doc.cn

(可选)Password (如果需要身份验证)。它默认为 'REPO_WITH_BINARIES_PASSWORD,如果未设置,则默认为 'passwordEXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URLspring-doc.cn

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URLspring-doc.cn

Artifact Manager 的 URL。它默认为 environment variable 的值,如果未设置,则默认为REPO_WITH_BINARIES_URLlocalhost:8081/artifactory/libs-release-localspring-doc.cn

EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_USERNAMEspring-doc.cn

(可选)用户名(如果需要身份验证)。它默认为 .如果未设置,则默认为 'adminEXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URLREPO_WITH_BINARIES_USERNAMEspring-doc.cn

EXTERNAL_CONTRACTS_VERSIONspring-doc.cn

包含合同的项目版本。Defautls 相当于选择最新的spring-doc.cn

+spring-doc.cn

EXTERNAL_CONTRACTS_WORK_OFFLINEspring-doc.cn

如果设置为 ,则从容器的 .将本地挂载为容器路径中可用的卷true.m2.m2/root/.m2spring-doc.cn

spring-doc.cn

FAIL_ON_NO_CONTRACTSspring-doc.cn

如果没有 Contract 存在,构建是否应该失败?spring-doc.cn

spring-doc.cn

MESSAGING_TYPEspring-doc.cn

消息类型。可以是 [rabbit] 或 [kafka]。spring-doc.cn

PRODUCER_STUBS_CLASSIFIERspring-doc.cn

用于生成的生产者存根的存档分类器spring-doc.cn

存根spring-doc.cn

PROJECT_GROUPspring-doc.cn

项目的群组 IDspring-doc.cn

com.示例spring-doc.cn

PROJECT_NAMEspring-doc.cn

项目的工件 IDspring-doc.cn

spring-doc.cn

PROJECT_VERSIONspring-doc.cn

您的项目版本spring-doc.cn

0.0.1-快照spring-doc.cn

PUBLISH_ARTIFACTSspring-doc.cn

如果设置为 ,则将对象发布到二进制存储truespring-doc.cn

spring-doc.cn

PUBLISH_ARTIFACTS_OFFLINEspring-doc.cn

如果设置为 ,则将对象发布到本地 m2truespring-doc.cn

spring-doc.cn

PUBLISH_STUBS_TO_SCMspring-doc.cn

如果设置为 ,将运行任务以将存根发布到 scmtruespring-doc.cn

spring-doc.cn

REPO_ALLOW_INSECURE_PROTOCOLspring-doc.cn

(可选)如果 <true>则允许通过不安全的 HTTP 将工件发布到 Artifact Managerspring-doc.cn

spring-doc.cn

REPO_WITH_BINARIES_PASSWORDspring-doc.cn

(可选)保护 Artifact Manager 时的密码spring-doc.cn

密码spring-doc.cn

REPO_WITH_BINARIES_URLspring-doc.cn

Artifact Manager 的 URL(在本地运行时,默认为 Artifactory 的默认 URL)spring-doc.cn

本地主机:8081/artifactory/libs-release-localspring-doc.cn

REPO_WITH_BINARIES_USERNAMEspring-doc.cn

(可选)Artifact Manager 受到保护时的用户名spring-doc.cn

管理spring-doc.cn

STANDALONE_PROTOCOLspring-doc.cn

对于独立版本,应添加哪些附加协议spring-doc.cn

运行测试时使用以下环境变量:spring-doc.cn

表 2.Docker 环境变量 - 在运行时读取

名字spring-doc.cn

描述spring-doc.cn

违约spring-doc.cn

APPLICATION_BASE_URLspring-doc.cn

运行应用程序的 URL。spring-doc.cn

APPLICATION_PASSWORDspring-doc.cn

用于访问应用程序的可选密码。spring-doc.cn

APPLICATION_USERNAMEspring-doc.cn

用于访问应用程序的可选用户名。spring-doc.cn

MESSAGING_TRIGGER_CONNECT_TIMEOUTspring-doc.cn

连接到应用程序以触发消息的超时。spring-doc.cn

5000spring-doc.cn

MESSAGING_TRIGGER_READ_TIMEOUTspring-doc.cn

Timeout 从应用程序读取响应以触发消息。spring-doc.cn

5000spring-doc.cn

MESSAGING_TYPEspring-doc.cn

消息类型。可以是 [rabbit] 或 [kafka]。spring-doc.cn

MESSAGING_TYPEspring-doc.cn

定义处理基于消息的 Contract 时的消息类型。spring-doc.cn

SPRING_KAFKA_BOOTSTRAP_SERVERSspring-doc.cn

对于 Kafka - 代理地址。spring-doc.cn

SPRING_RABBITMQ_ADDRESSESspring-doc.cn

对于 RabbitMQ - 代理地址。spring-doc.cn

自定义 gradle 构建

您可以通过在运行容器时将自定义构建文件挂载为卷来提供要在容器中运行的自定义文件:gradle.buildspring-doc.cn

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

通过 HTTP 的使用示例

在本节中,我们将探讨一个简单的 MVC 应用程序。要开始使用,请克隆以下内容 git 存储库并 cd 到结果目录,方法是运行以下命令:spring-doc.cn

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

合同位于文件夹中。/contractsspring-doc.cn

由于我们想要运行测试,因此可以运行以下命令:spring-doc.cn

$ npm test

但是,出于学习目的,我们将其拆分为多个部分,如下所示:spring-doc.cn

# 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 脚本,将发生以下情况:spring-doc.cn

通过消息收发使用示例

如果您想通过 Docker 镜像(例如 如果是多语言应用程序),则必须满足以下先决条件:spring-doc.cn

  • 中间件(例如 RabbitMQ 或 Kafka)必须在生成测试之前运行spring-doc.cn

  • 您的合约需要调用一个参数等于合约的 .triggerMessage(…​)Stringlabelspring-doc.cn

  • 您的应用程序需要有一个 HTTP 终端节点,我们可以通过该终端节点触发消息spring-doc.cn

    • 该端点在生产中不应可用(可以通过环境变量启用)spring-doc.cn

消息传送协定示例

合约需要调用一个方法。该方法已在 docker 映像中所有测试的基类中提供,并将向生产者端的 HTTP 端点发送请求。您可以在下面找到此类合同的示例。triggerMessage(…​)spring-doc.cn

槽的
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: '#'
                ]
           ]
         ]
        ])
}
YAML
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 合约 必须生成各种语言的代码(就像在 Java 中一样)才能触发生产 向 broker 发送消息的代码。如果未生成此类代码,那么我们无论如何都需要能够触发消息,而执行此操作的方法是提供一个 HTTP 端点,用户将以他们选择的语言准备该端点。spring-doc.cn

终端节点必须具有以下配置:spring-doc.cn

  • URL:其中可以是任何文本/springcloudcontract/{label}labelspring-doc.cn

  • 方法:POSTspring-doc.cn

  • 基于 将生成一条消息,该消息将根据合同定义发送到给定的目的地labelspring-doc.cn

下面是此类终端节点的示例。如果您对 以您的语言提供示例,请随时在 Github 上的 Spring Cloud Contract 存储库spring-doc.cn

#!/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.')

在创建者端运行消息测试

现在,让我们从 Contract 生成测试以测试生产者端。 我们将运行 bash 代码来启动 Docker 镜像 使用附加的合约,但是我们还将为消息传递添加变量 代码才能工作。在这种情况下,我们假设合约存储在 Git 存储库。spring-doc.cn

#!/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

将发生的事情是:spring-doc.cn

  • 测试将从 Git 获取的合约中生成spring-doc.cn

  • 在合约中,我们在 metadata 中提供了一个名为 的条目,这将导致在发送触发消息的请求之前在 RabbitMQ 中创建一个具有给定名称的队列declareQueueWithNamespring-doc.cn

  • 通过方法调用,将向 Python 应用程序发出 POST 请求,以到达端点triggerMessage("ping_pong")/springcloudcontract/ping_pongspring-doc.cn

  • Python 应用程序将通过 RabbitMQ 生成 JSON 并将其发送到名为'{"message":"pong"}'outputspring-doc.cn

  • 生成的测试将轮询发送到 Exchange 的消息outputspring-doc.cn

  • 收到消息后,将断言其内容spring-doc.cn

测试通过后,我们知道消息已从 Python 应用程序正确发送到 RabbitMQ。spring-doc.cn

在使用者端运行 Stub

本节介绍如何在 Consumer 端使用 Docker 来获取和运行 stub。spring-doc.cn

我们发布一个 Docker 镜像 ,这将启动 Stub Runner 的独立版本。spring-cloud/spring-cloud-contract-stub-runnerspring-doc.cn

安全

由于 Spring Cloud Contract Stub Runner Docker Image 使用独立版本的 Stub Runner,因此需要采取相同的安全考虑。您可以在文档的此部分阅读有关这些内容的更多信息。spring-doc.cn

环境变量

您可以运行 docker 镜像并将 JUnit 和 Spring 的任何通用属性作为环境变量传递。惯例是,所有 字母应为大写。 点 () 应替换为下划线 () 字符。例如 属性应表示 作为环境变量。._stubrunner.repositoryRootSTUBRUNNER_REPOSITORY_ROOTspring-doc.cn

除了这些变量之外,您还可以设置以下变量:spring-doc.cn

  • MESSAGING_TYPE- 您正在使用哪种类型的消息传递系统(当前支持的是 、rabbitkafka)spring-doc.cn

  • ADDITIONAL_OPTS- 要传递给应用程序的任何其他属性spring-doc.cn

使用示例

我们想使用在此 [docker-server-side] 步骤中创建的存根。 假设我们想在 port 上运行存根。你可以看到 NodeJS 代码 通过克隆存储库并更改为以下命令中指示的目录:9876spring-doc.cn

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

现在,我们可以通过运行以下命令来运行带有存根的 Stub Runner Boot 应用程序 命令:spring-doc.cn

# 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}"

当上述命令运行时,spring-doc.cn

在服务器端,我们构建了一个有状态的存根。我们可以使用 curl 来断言 存根设置正确。为此,请运行以下命令:spring-doc.cn

# 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
如果您想使用在本地构建的存根,请在主机上使用 您应该设置环境变量并挂载 本地 M2 () 的音量。-e STUBRUNNER_STUBS_MODE=LOCAL-v "${HOME}/.m2/:/home/scc/.m2:rw"

消息收发的用法示例

为了使消息传递正常工作,只需将环境变量与 or 值一起传递就足够了。这将导致设置 Stub Runner Boot Docker 镜像,其中包含连接到代理所需的依赖项。MESSAGING_TYPEkafkarabbitspring-doc.cn

为了设置连接属性,您可以查看 Spring Cloud Stream 属性页面以设置适当的环境变量。spring-doc.cn

您将设置的最常见属性是正在运行的 middlewara 的位置。 如果调用了要设置它的属性,则应分别命名环境变量 和 。spring.rabbitmq.addressesspring.kafka.bootstrap-serversSPRING_RABBITMQ_ADDRESSESSPRING_KAFKA_BOOTSTRAP_SERVERSspring-doc.cn

针对现有中间件运行 Contract 测试

有正当理由对现有中间件运行合约测试。一些 测试框架可能会给您带来误报结果 - 构建中的测试 通过,而在生产环境中,通信失败。spring-doc.cn

在 Spring Cloud Contract docker 镜像中,我们提供了一个连接到现有中间件的选项。 如前面的小节所述,我们确实支持开箱即用的 Kafka 和 RabbitMQ。然而 通过 Apache Camel 组件,我们可以支持 其他中间件也是如此。让我们看一下以下用法示例。spring-doc.cn

Spring Cloud Contract Docker 和正在运行的中间件

为了连接到任意中间件,我们将利用元数据条目 在 Contract (合同) 部分。standalonespring-doc.cn

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 protocol:destination根据 Apache Camel 的要求
4 输出消息正文
5 独立元数据条目
6 Setup 部分将包含有关如何在实际调用正在运行的应用程序的 HTTP 端点之前准备运行 Contract 测试的信息
7 Apache Camel URI 在设置阶段调用。在这种情况下,我们将尝试在交换处轮询消息,并且由于具有 和 ,将设置具有 name 的队列并将其绑定到具有路由键的交换outputqueue=outputroutingKey=outputoutput
8 要附加到起点 (3) 的其他选项(更具技术性的选项)将一起以以下格式组合。protocol:destinationrabbitmq:output?routingKey=#&queue=output

为了使合约测试通过,我们将像往常一样需要在多语言环境中发送消息 一个正在运行的应用程序和正在运行的中间件。这一次,我们将为 Spring Cloud Contract Docker 镜像设置不同的环境变量。spring-doc.cn

#!/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 通过环境变量,我们将获取一个 Apache Camel 组件。我们将获取的工件是 。换句话说,匹配 Camel 的组件。STANDALONE_PROTOCOLorg.apache.camel.springboot:camel-${STANDALONE_PROTOCOL}-starterSTANDALONE_PROTOCOL
4 我们正在通过 Camel 的 Spring Boot Starter 机制设置地址(我们可以设置凭据)。Apache Camel 的 RabbitMQ Spring Boot 自动配置的示例

Stub Runner Docker 和正在运行的中间件

为了针对正在运行的中间件触发 stub 消息,我们可以按以下方式运行 Stub Runner Docker image。spring-doc.cn

使用示例spring-doc.cn

$ 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 的 Spring Boot 自动配置注入 RabbitMQ 的地址
2 我们告诉 Stub Runner 要下载哪些存根
3 我们为存根提供了一个外部位置(Git 存储库)
4 通过该属性,我们告诉 Stub Runner 在运行时要获取哪个额外的依赖项。在这种情况下,我们想要获取 so 是一个随机字符串,并且我们想获取版本 .ADDITIONAL_OPTS=--thin.properties.dependencies.XXX=GROUP:ARTIFACT:VERSIONcamel-rabbitmq-starterXXXorg.apache.camel.springboot:camel-rabbitmq-starter3.4.0
5 由于我们使用的是 Git,因此需要设置获取存根的 remote 选项
6 为了加快 Stub Runner 的启动速度,我们将本地 Maven 存储库作为卷附加。如果您没有填充它,则可以考虑通过 instead read only 设置写入权限。.m2:rw:ro
7 我们公开了 Stub Runner 正在运行的端口。8750
8 Stub Runner Docker 镜像的坐标。

一段时间后,您会在控制台中注意到以下文本,这意味着 Stub Runner 已准备好接受请求。spring-doc.cn

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

要获取触发器列表,您可以向 endpoint 发送 HTTP GET 请求。要触发存根消息,您可以向 .在控制台中,您将看到:localhost:8750/triggerslocalhost:8750/triggers/standalone_ping_pongspring-doc.cn

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

如果您检查 RabbitMQ 管理控制台,您将看到队列中有 1 条消息可用。outputspring-doc.cn