You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring应用连接MongoDB TestContainer时Netty连接异常排查求助

排查Spring WebFlux应用连接TestContainers MongoDB的Netty异常

问题背景

将Udemy的Maven示例项目转为Gradle后,使用TestContainers启动MongoDB容器时出现Netty连接异常。使用docker-compose部署MongoDB服务时应用可正常连接,已确认配置了spring.data.mongodb.uri的端口映射。

相关配置代码

build.gradle配置

plugins {
    java
    id("org.springframework.boot") version "3.4.1"
    id("io.spring.dependency-management") version "1.1.7"
}

group = "com.omri"
version = "0.0.1-SNAPSHOT"

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

configurations {
    compileOnly {
        extendsFrom(configurations.annotationProcessor.get())
    }
}

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-data-mongodb-reactive")
    implementation("org.springframework.boot:spring-boot-starter-webflux")
    compileOnly("org.projectlombok:lombok")
    annotationProcessor("org.projectlombok:lombok")
    testImplementation("org.springframework.boot:spring-boot-starter-test")
    testImplementation("io.projectreactor:reactor-test")
    testRuntimeOnly("org.junit.platform:junit-platform-launcher")
    testImplementation("org.testcontainers:testcontainers:1.20.4")
    testImplementation("org.testcontainers:junit-jupiter")
}

dependencyManagement {
    imports {
        mavenBom("org.testcontainers:testcontainers-bom:1.20.4")
    }
}

tasks.withType<Test> {
    useJUnitPlatform()
}

测试基类BaseTest代码

public abstract class BaseTest {

private static final int MONGO_PORT = 27017;
private static final String INIT_JS = "/docker-entrypoint-initdb.d/init.js";
private static final String MONGO_URI_FORMAT = "mongodb://job_user:job_password@%s:%s/job";

@Container
protected static final GenericContainer<?> mongo = new GenericContainer<>(DockerImageName.parse("mongo"))
        .withExposedPorts(MONGO_PORT)
        .withClasspathResourceMapping("data/job-init.js", INIT_JS, BindMode.READ_ONLY)
        .waitingFor(Wait.forListeningPort());

@DynamicPropertySource
static void mongoProperties(DynamicPropertyRegistry registry) {
    mongo.start();
    registry.add("spring.data.mongodb.uri", () -> String.format(MONGO_URI_FORMAT, mongo.getHost(), mongo.getFirstMappedPort()));
}
}

测试类JobServiceIT代码

@SpringBootTest
@AutoConfigureWebTestClient
class JobServiceIT extends BaseTest {

    @Autowired
    private WebTestClient webClient;

    @Autowired
    private Environment env;

    @Test
    public void allJobsTest() {
        System.out.println(env.getProperty("spring.data.mongodb.uri"));
        /*webClient.get().uri("/jobs/all").exchange()
                .expectStatus()
                .is2xxSuccessful()
                .expectBody().jsonPath("$").isNotEmpty();*/
    }

}

异常信息

com.mongodb.MongoSocketWriteException: Exception sending message
    at com.mongodb.internal.connection.InternalStreamConnection.throwTranslatedWriteException(InternalStreamConnection.java:790) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnection.sendMessage(InternalStreamConnection.java:675) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnection.trySendMessage(InternalStreamConnection.java:507) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnection.sendCommandMessage(InternalStreamConnection.java:482) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceiveInternal(InternalStreamConnection.java:440) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnection.lambda$sendAndReceive$0(InternalStreamConnection.java:375) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:378) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.CommandHelper.sendAndReceive(CommandHelper.java:100) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.CommandHelper.executeCommand(CommandHelper.java:49) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnectionInitializer.initializeConnectionDescription(InternalStreamConnectionInitializer.java:144) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnectionInitializer.startHandshake(InternalStreamConnectionInitializer.java:79) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.InternalStreamConnection.open(InternalStreamConnection.java:235) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitor.lookupServerDescription(DefaultServerMonitor.java:219) ~[mongodb-driver-core-5.2.1.jar:na]
    at com.mongodb.internal.connection.DefaultServerMonitor$ServerMonitor.run(DefaultServerMonitor.java:176) ~[mongodb-driver-core-5.2.1.jar:na]
Caused by: io.netty.channel.StacklessClosedChannelException: null
    at io.netty.channel.AbstractChannel$AbstractUnsafe.write(Object, ChannelPromise)(Unknown Source) ~[netty-transport-4.1.116.Final.jar:4.1.116.Final]

排查修复步骤

  1. 移除手动启动容器的代码
    TestContainers的@Container注解会自动管理容器的启动、停止生命周期,手动调用mongo.start()会导致容器状态异常,直接删除该行代码。

  2. 替换为专用MongoDB容器类
    使用TestContainers提供的MongoDBContainer替代GenericContainer,它能自动处理MongoDB的端口映射、连接字符串生成等细节,避免手动配置错误:

    import org.testcontainers.containers.MongoDBContainer;
    
    public abstract class BaseTest {
    
    private static final String INIT_JS = "/docker-entrypoint-initdb.d/init.js";
    
    @Container
    protected static final MongoDBContainer mongo = new MongoDBContainer(DockerImageName.parse("mongo:latest"))
            .withClasspathResourceMapping("data/job-init.js", INIT_JS, BindMode.READ_ONLY)
            .withCommand("--auth"); // 启用认证,匹配初始化脚本中的用户配置
    
    @DynamicPropertySource
    static void mongoProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.data.mongodb.uri", mongo::getReplicaSetUrl);
    }
    }
    
  3. 添加MongoDB TestContainers依赖
    在build.gradle的dependencies块中加入专用依赖:

    testImplementation("org.testcontainers:mongodb")
    
  4. 验证初始化脚本路径
    确保job-init.js文件放在src/test/resources/data/目录下,保证TestContainers能正确映射到容器内的初始化脚本目录。

  5. 确认Mongo用户配置匹配
    初始化脚本中创建的用户名、密码、数据库名必须和连接字符串中的一致,例如脚本内容需包含:

    db.createUser({
      user: "job_user",
      pwd: "job_password",
      roles: [{ role: "readWrite", db: "job" }]
    });
    

内容的提问来源于stack exchange,提问作者omri

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 00:14:54