为什么我的CountDownLatch在通过docker构建的测试运行中表现不同?



我创建了2个独立的微服务,它们使用Apache Kafka进行通信。一个被称为生产者,另一个被称为消费者。两个微服务都有自己的测试,无论我如何运行它们(run Test, mvn clean install…),我也使用嵌入式Kafka测试Kafka,所以我的测试不依赖于任何其他系统的存在,比如单独的Kafka代理或在线的DB。我想创建两个微服务的docker映像,但是当在Producer的DOCKERFILE上运行docker build时,mvn clean package期间的一个测试抛出错误并且进程失败。在这个测试中,一个消息被发送到EmbeddedKafka,另一个类(TestConsumer)与KafkaListener正在消费这个消息。这个类包含CountDownLatch,当任何消息被消费时,都会触发countDown。

public class TestConsumer {
private CountDownLatch latch = new CountDownLatch(1);
@KafkaListener(topics = "${test.kafka.topic.name}",
containerFactory = "embeddedKafkaListenerContainerFactory",
groupId = "Test_Group")
public void receive(ConsumerRecord<?, Payload> consumerRecord) {
// Receiving consumerRecord.value();
log.info("DEBUG CONSUMER getLatch before countdown" + getLatch());
latch.countDown();
log.info("DEBUG CONSUMER getLatch after countdown" + getLatch());
}
public void resetLatch() {
latch = new CountDownLatch(1);
}
public CountDownLatch getLatch() {
return latch;
}

测试类等待此消息,并根据此闩锁断言消息被接收,这就是测试失败的地方。

@Slf4j
@SpringBootTest
@ActiveProfiles({ "test" })
@EnableKafka
@EmbeddedKafka(partitions = 1, brokerProperties = { "listeners=PLAINTEXT://localhost:9092", "port=9092" })
@TestPropertySource(locations="classpath:test.properties")
class EmbeddedKafkaTest {
@Autowired
private TestConsumer consumer;
@Autowired
private TestProducer producer;
Payload testPayload = TestPayload.getTestPayload();
@BeforeEach
void setup() {
consumer.resetLatch();
}
@Test
void whenSendingWithCustomSerDe_thenMessagePojoReceived() throws Exception {
producer.send(topic, testPayload);
boolean messageConsumed = consumer.getLatch().await(10, TimeUnit.SECONDS);
log.info("DEBUG TEST consumer.getLatch after await " + consumer.getLatch());
assertTrue(messageConsumed);  // <- THIS ASSERTION FAILS
assertEquals(testPayload.getName(), consumer.getPayload().getName());
}

当我设置额外的日志打印门闩信息在不同的阶段,我惊讶地发现,当我从测试类日志门闩,TestConsumer类,我得到2不同IdentityHashCodes和不同的价值观在执行时的码头工人建造(TestConsumer日志显示门闩下降为0,但门闩测试类日志访问通过consumer.getLatch()没有改变),并与相应的期望值,如果我只有一个IdentityHashCode手动运行测试。有人能给我简单解释一下这种行为吗?我更希望测试在所有情况下都能以相同的方式运行,最好在所有类中只使用一个IdentityHashCode和相同的值。我还在DOCKERFILE中尝试了不同版本的JDK,因为这是我能想到的我的系统和docker构建环境之间唯一明显的区别,但这没有帮助。

以下是我自己手动执行测试时的日志,甚至通过mvn clean package:

DEBUG CONSUMER getLatch before countdownjava.util.concurrent.CountDownLatch@2932e15f[Count = 1]
DEBUG CONSUMER getLatch after countdownjava.util.concurrent.CountDownLatch@2932e15f[Count = 0]
DEBUG TEST consumer.getLatch after await java.util.concurrent.CountDownLatch@2932e15f[Count = 0]

这些日志包含docker build - mvn clean package

DEBUG CONSUMER getLatch before countdownjava.util.concurrent.CountDownLatch@48d71ccf[Count = 1]
DEBUG CONSUMER getLatch after countdownjava.util.concurrent.CountDownLatch@48d71ccf[Count = 0]
DEBUG TEST consumer.getLatch after await java.util.concurrent.CountDownLatch@7546033d[Count = 1]

这是我当前的DOCKERFILE:

FROM maven:3.9.0-eclipse-temurin-17-alpine AS MAVEN_BUILD
COPY ./ ./
RUN mvn clean package
FROM openjdk:21-ea-jdk-slim
COPY --from=MAVEN_BUILD /target/kafka-producer-0.0.1-SNAPSHOT.jar /kafka-producer.jar
CMD ["java", "-jar", "/kafka-producer.jar"]

帮助我从Test类访问这些值的方法是使闩锁变量static。有趣的是,之前的测试是有可能通过的,但当作为docker build的一部分运行时,只有大约1/10的情况下(10/10当运行任何其他方式时,这仍然让我感到困惑)。添加此关键字后,10次测试中有10次通过。

10%成功率:

private CountDownLatch latch = new CountDownLatch(1); 

100%成功率:

private static CountDownLatch latch = new CountDownLatch(1);

如果有人能给我指出正确的方向,找出为什么这种行为在docker build中不同,我将不胜感激,我只能想到DOCKERFILE中提到的jdk,也许我会尝试更多不同的