Java Spring Boot连接Windows11 Docker部署的Cosmos DB Emulator超时问题
我按照微软官方指南在Windows 11机器上使用Docker部署了基于NoSQL API的Cosmos DB模拟器,容器已成功启动运行。目前在IntelliJ中运行Java Spring Boot项目,使用依赖implementation 'com.azure:azure-spring-data-cosmos:5.17.1'连接本地模拟器,该库可正常连接云端Azure Cosmos DB,但本地连接时出现Socket超时错误,错误日志如下:
2024-10-11T16:30:32.352-04:00 WARN 23028 --- [tor-http-nio-17] c.a.c.implementation.ClientRetryPolicy : marking the endpoint https://172.17.0.2:8081/ as unavailable for read 2024-10-11T16:30:32.352-04:00 INFO 23028 --- [tor-http-nio-17] c.a.c.i.RxDocumentClientImpl : Getting database account endpoint from https://localhost:8081 2024-10-11T16:30:54.432-04:00 WARN 23028 --- [tor-http-nio-18] c.a.c.i.d.GatewayAddressCache : Network failure io.netty.channel.AbstractChannel$AnnotatedConnectException: Connection timed out: getsockopt: /172.17.0.2:8081 Caused by: java.net.ConnectException: Connection timed out: getsockopt at java.base/sun.nio.ch.Net.pollConnect(Native Method) ~[na:na] at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:682) ~[na:na] at java.base/sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:973) ~[na:na] at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:336) ~[netty-transport-4.1.110.Final.jar:4.1.110.Final] at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:339) ~[netty-transport-4.1.110.Final.jar:4.1.110.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:776) ~[netty-transport-4.1.110.Final.jar:4.1.110.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:724) ~[netty-transport-4.1.110.Final.jar:4.1.110.Final] at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:650) ~[netty-transport-4.1.110.Final.jar:4.1.110.Final] at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:562) ~[netty-transport-4.1.110.Final.jar:4.1.110.Final] at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:994) ~[netty-common-4.1.110.Final.jar:4.1.110.Final] at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.110.Final.jar:4.1.110.Final] at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.110.Final.jar:4.1.110.Final] at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]
我的AbstractCosmosConfiguration配置如下:
@Bean public CosmosClientBuilder getCosmosClientBuilder() { final DirectConnectionConfig directConnectionConfig = new DirectConnectionConfig(); final GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); return new CosmosClientBuilder() .endpoint("https://localhost:8081") .credential(new ManagedIdentityCredentialBuilder() .build()) .key("my primary key") .directMode(directConnectionConfig, gatewayConnectionConfig) .clientTelemetryConfig( new CosmosClientTelemetryConfig() .diagnosticsThresholds( new CosmosDiagnosticsThresholds() ) .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER)); } @Override public CosmosConfig cosmosConfig() { return CosmosConfig.builder() .enableQueryMetrics(cosmosProperties.isQueryMetricsEnabled()) .enableIndexMetrics(cosmosProperties.isIndexMetricsEnabled()) .responseDiagnosticsProcessor(new ResponseDiagnosticsProcessorImplementation()) .build(); } @Override protected String getDatabaseName() { return "Database Name"; } private static class ResponseDiagnosticsProcessorImplementation implements ResponseDiagnosticsProcessor { @Override public void processResponseDiagnostics(@Nullable final ResponseDiagnostics responseDiagnostics) { log.info("Response Diagnostics {}", responseDiagnostics); } }
请问我遗漏了什么配置或步骤来解决这个连接超时问题?
1. 切换连接模式为Gateway
错误日志中出现容器内部IP172.17.0.2,是因为你使用了Direct Mode,客户端会尝试连接模拟器返回的容器内部端点,但主机无法直接访问该IP。本地Docker模拟器推荐使用Gateway模式:
- 移除配置中的
.directMode(directConnectionConfig, gatewayConnectionConfig),替换为.gatewayMode(gatewayConnectionConfig) - 添加
.endpointDiscoveryEnabled(false),禁止客户端自动发现内部端点,强制使用配置的localhost:8081
2. 移除不必要的Managed Identity认证
本地模拟器不需要Managed Identity,同时配置密钥和Managed Identity会导致认证冲突,直接删除.credential(new ManagedIdentityCredentialBuilder().build()),只保留.key("my primary key")
3. 处理SSL证书信任问题
Cosmos DB模拟器使用自签名证书,Java默认不信任,需导入证书到Java信任库:
- 从Docker容器导出证书:
docker exec <你的容器名称> cat /cosmosdb/cert/cosmos_emulator.crt > cosmos_emulator.crt - 导入到Java cacerts(默认密码为
changeit):keytool -importcert -alias cosmosdbemulator -file cosmos_emulator.crt -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit
开发环境临时方案:可以禁用SSL验证,但生产环境禁止使用,添加以下配置到CosmosClientBuilder:
.sslContext(SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build())
4. 确认Docker端口映射正确
启动容器时必须映射必要端口,Gateway模式至少映射8081,Direct Mode需要额外映射10250-10255:
docker run -p 8081:8081 -p 10250-10255:10250-10255 -m 3g --cpus 2 mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator
用docker ps检查端口映射是否生效。
修正后的配置示例
@Bean public CosmosClientBuilder getCosmosClientBuilder() { final GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); return new CosmosClientBuilder() .endpoint("https://localhost:8081") .key("my primary key") .gatewayMode(gatewayConnectionConfig) .endpointDiscoveryEnabled(false) .clientTelemetryConfig( new CosmosClientTelemetryConfig() .diagnosticsThresholds(new CosmosDiagnosticsThresholds()) .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER)); } // 其余配置保持不变
内容的提问来源于stack exchange,提问作者KevinG

