简体   繁体   English

测试容器中的 Kafka 侦听器,避开端口 9092

[英]Kafka listener in test container, avoiding port 9092

I've written an Integration Test to show that a message sent to Kafka will arrive with a listener.我编写了一个集成测试来显示发送到 Kafka 的消息将与侦听器一起到达。 It passes if and only if I use KAFKA_PORT=9092 .当且仅当我使用KAFKA_PORT=9092时,它才会通过。 That constant is the port used on the developer machine (or CI machine).该常量是开发人员机器(或 CI 机器)上使用的端口。

Ultimately I want to do this on a dynamically allocated port (ie using a GenericContainer and not a FixedHostPortGenericContainer ) but for the moment I would simply like to be able to use a different port.最终,我想在动态分配的端口上执行此操作(即使用GenericContainer而不是FixedHostPortGenericContainer ),但目前我只想能够使用不同的端口。

If I set KAFKA_PORT=59092 in the below code then the test fails and I see console output such as Connection to node -1 (localhost/127.0.0.1:9092) could not be established eg:如果我在下面的代码中设置 KAFKA_PORT=59092 则测试失败并且我看到控制台 output 例如Connection to node -1 (localhost/127.0.0.1:9092) could not be established的连接,例如:

2020-06-08 12:16:22.374  WARN 1371 --- [ntainer#0-0-C-1] org.apache.kafka.clients.NetworkClient   : [Consumer clientId=consumer-1, groupId=test-consumer-group] Connection to node -1 (localhost/127.0.0.1:9092) could not be established. Broker may not be available.

I assume there is some additional configuration I need to do so that no attempt is made to use port 9092, but this is eluding me.我假设我需要做一些额外的配置,这样就不会尝试使用端口 9092,但这让我望而却步。

A recreated, stripped back test is below, as is the associated gradle.build .下面是重新创建的剥离测试,关联的gradle.build

KafaSpikeFixedPort.java KafaSpikeFixedPort.java

package com.example.kafkaspike;

import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.config.KafkaListenerEndpointRegistry;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.listener.MessageListenerContainer;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.FixedHostPortGenericContainer;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@Testcontainers
public class KafaSpikeFixedPort {

    // Test works only if this port is 9092 (matching the Docker container port)
    final static int KAFKA_PORT = 9092;

    @DynamicPropertySource
    static void kafkaProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.kafka.producer.bootstrap-servers",
                     () -> "kubernetes.docker.internal:"+ KAFKA_PORT);
        registry.add("spring.kafka.consumer.group-id",
                     () -> "test-consumer-group");
    }

    @Container
    private GenericContainer kafkaContainer =
            new FixedHostPortGenericContainer("obsidiandynamics/kafka:2.3.0-11")
                    .withFixedExposedPort(KAFKA_PORT, 9092)
                    .withExtraHost("kubernetes.docker.internal", "127.0.0.1")
                    .withEnv("KAFKA_LISTENERS",
                             "INTERNAL://:29092," +
                             "EXTERNAL://:"+KAFKA_PORT)
                    .withEnv("KAFKA_ADVERTISED_LISTENERS",
                             "INTERNAL://kubernetes.docker.internal:29092," +
                             "EXTERNAL://kubernetes.docker.internal:"+KAFKA_PORT)
                    .withEnv("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP",
                             "INTERNAL:PLAINTEXT," +
                             "EXTERNAL:PLAINTEXT")
                    .withEnv("KAFKA_INTER_BROKER_LISTENER_NAME",
                             "INTERNAL")
                    .waitingFor(Wait.forLogMessage(
                            ".*INFO\\s+\\[KafkaServer\\s+id=\\d+\\]" +
                            "\\s+started\\s+\\(kafka.server.KafkaServer\\).*",
                            1));

    @Autowired
    KafkaTemplate<String, String> kafkaTemplate;

    @Autowired
    KafkaListenerEndpointRegistry kafkaListenerEndpointRegistry;

    private List<String> payloadsReceived = new ArrayList<>();

    @KafkaListener(autoStartup = "false", topics = "topic1")
    public void onMessage(@Payload String payload) {
        payloadsReceived.add(payload);
    }

    @BeforeEach
    public void beforeEach() {
        payloadsReceived.clear();
        for(MessageListenerContainer listenerContainer : kafkaListenerEndpointRegistry.getListenerContainers()) {
            listenerContainer.start();
        }
        sleep(2_000); // Just for the spike. (Eliminates code checking the listener container states.)
    }

    @AfterEach
    public void afterEach() {
        for(MessageListenerContainer listenerContainer : kafkaListenerEndpointRegistry.getListenerContainers()) {
            listenerContainer.stop();
        }
        sleep(2_000); // Just for the spike. (Eliminates code checking the listener container states.)
    }

    @Test
    @Timeout(value = 3, unit = TimeUnit.SECONDS)
    public void test() {
        kafkaTemplate.send("topic1", "Hello World!");
        sleep(1_000); // Just for the spike. (Wait for message in production test, with test timeout.)
        assertAll(
                () -> assertEquals(1, payloadsReceived.size()),
                () -> assertEquals("Hello World!", payloadsReceived.get(0))
                 );
    }

    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch(InterruptedException e) {
        }
    }
}

gradle.build gradle.build

plugins {
    id 'org.springframework.boot' version '2.2.7.RELEASE'
    id 'io.spring.dependency-management' version '1.0.9.RELEASE'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

configurations {
    compileOnly {
        extendsFrom annotationProcessor
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'org.springframework.kafka:spring-kafka'
    compileOnly 'org.projectlombok:lombok'
    annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
    annotationProcessor 'org.projectlombok:lombok'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.springframework.kafka:spring-kafka-test'
    // for TestContainers
    testCompile group: 'org.testcontainers', name: 'testcontainers', version: "1.14.1"
    testCompile group: 'org.testcontainers', name: 'junit-jupiter', version: "1.14.1"
}

test {
    useJUnitPlatform()
}

There are reasons why Testcontainers' Kafka module exists:) Testcontainers 的 Kafka 模块存在是有原因的:)

It takes care of the ports setup by deferring running Kafka's process, so that it can provide the actual assigned random port as ADVERTISED_HOST env variable.它通过推迟运行 Kafka 的进程来处理端口设置,以便它可以提供实际分配的随机端口作为ADVERTISED_HOST环境变量。

Try it, or have a look at the sources for some inspiration:试试看,或者看看一些灵感的来源:
https://github.com/testcontainers/testcontainers-java/tree/master/modules/kafka https://github.com/testcontainers/testcontainers-java/tree/master/modules/kafka

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 spring kafka @KafkaListener 监听器容器? - spring kafka @KafkaListener listener container? 嵌入式 Kafka 测试 - 未调用 Kafka 侦听器 - Embedded Kafka Test - The Kafka listener is not invoked Spring Kafka集成测试侦听器不起作用 - spring Kafka integration test listener not working Spring kafka 单元测试侦听器未订阅主题 - Spring kafka unit test listener not subscribing to topic spring Kafka 集成测试侦听器不工作(KAFKA JsonDeserializer) - spring Kafka integration test listener not working (KAFKA JsonDeserializer) 如何销毁kafka消息侦听器容器而不是停止它 - How to destroy the kafka message listener container rather than stopping it 带有注解的春季启动中的kafka侦听器容器不占用消息 - kafka listener container in spring boot with annotations does not consume message Spring 启动 Kafka Consumer Bootstrap 服务器总是选择 Localhost 9092 - Spring boot Kafka Consumer Bootstrap Servers always picking Localhost 9092 KafkaContainer - 如何在 Spring 中读取 kafka 容器端口作为属性 start() / 如何在启动前配置 Kafka 端口 - KafkaContainer - how to read kafka container port as property in Spring Boot after start() / how to configure Kafka port before starting 在向 Kafka 测试容器发出请求后验证存储库 state - Validate repository state after producing request to Kafka test container
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM