Spring Boot连接MongoDB时出现DNS解析异常求助
问题:Spring Boot应用连接MongoDB时持续抛出DNS解析异常
异常信息
Spring Boot应用连接MongoDB时抛出核心异常:
Caused by: com.mongodb.spi.dns.DnsWithResponseCodeException: DNS name not found [response code 3] Caused by: javax.naming.NameNotFoundException: DNS name not found [response code 3]
完整堆栈信息:
[INFO ] 2023-01-28 10:11:19 o.m.d.cluster [cluster-ClusterId{value='63d53b1769d90d388d824a76', description='null'}-srv-<domain>]: Exception while resolving SRV records com.mongodb.MongoConfigurationException: Failed looking up SRV record for '_mongodb._tcp.<domain>'. at com.mongodb.internal.dns.DefaultDnsResolver.resolveHostFromSrvRecords(DefaultDnsResolver.java:92) ~[mongodb-driver-core-4.8.2.jar:?] at com.mongodb.internal.connection.DefaultDnsSrvRecordMonitor$DnsSrvRecordMonitorRunnable.run(DefaultDnsSrvRecordMonitor.java:80) [mongodb-driver-core-4.8.2.jar:?] at java.lang.Thread.run(Thread.java:829) [?:?] Caused by: com.mongodb.spi.dns.DnsWithResponseCodeException: DNS name not found [response code 3] at com.mongodb.internal.dns.JndiDnsClient.getResourceRecordData(JndiDnsClient.java:52) ~[mongodb-driver-core-4.8.2.jar:?] at com.mongodb.internal.dns.DefaultDnsResolver.resolveHostFromSrvRecords(DefaultDnsResolver.java:74) ~[mongodb-driver-core-4.8.2.jar:?] ... 2 more Caused by: javax.naming.NameNotFoundException: DNS name not found [response code 3] at com.sun.jndi.dns.DnsClient.checkResponseCode(DnsClient.java:661) ~[jdk.naming.dns:?] at com.sun.jndi.dns.DnsClient.isMatchResponse(DnsClient.java:579) ~[jdk.naming.dns:?] at com.sun.jndi.dns.DnsClient.doUdpQuery(DnsClient.java:427) ~[jdk.naming.dns:?] at com.sun.jndi.dns.DnsClient.query(DnsClient.java:212) ~[jdk.naming.dns:?] at com.sun.jndi.dns.Resolver.query(Resolver.java:81) ~[jdk.naming.dns:?] at com.sun.jndi.dns.DnsContext.c_getAttributes(DnsContext.java:434) ~[jdk.naming.dns:?] at com.sun.jndi.toolkit.ctx.ComponentDirContext.p_getAttributes(ComponentDirContext.java:235) ~[?:?] Caused by: com.mongodb.spi.dns.DnsWithResponseCodeException: DNS name not found [response code 3] at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:141) ~[?:?] at com.sun.jndi.toolkit.ctx.PartialCompositeDirContext.getAttributes(PartialCompositeDirContext.java:129) ~[?:?] at javax.naming.directory.InitialDirContext.getAttributes(InitialDirContext.java:142) ~[?:?] at com.mongodb.internal.dns.JndiDnsClient.getResourceRecordData(JndiDnsClient.java:41) ~[mongodb-driver-core-4.8.2.jar:?] at com.mongodb.internal.dns.DefaultDnsResolver.resolveHostFromSrvRecords(DefaultDnsResolver.java:74) ~[mongodb-driver-core-4.8.2.jar:?] ... 2 more [WARN ] 2023-01-28 10:11:19 o.s.d.c.CustomConversions [main]: Registering converter from class java.time.LocalDateTime to class org.joda.time.LocalDateTime as reading converter although it doesn't convert from a store-supported type; You might want to check your annotation setup at the converter implementation [WARN ] 2023-01-28 10:11:19 o.s.d.c.CustomConversions [main]: Registering converter from class java.time.LocalDateTime to class org.joda.time.LocalDateTime as reading converter although it doesn't convert from a store-supported type; You might want to check your annotation setup at the converter implementation Caused by: javax.naming.NameNotFoundException: DNS name not found [response code 3]
背景信息
- 使用Gradle的
bootRun任务运行Spring Boot应用 - 已引入依赖:
org.mongodb:mongodb-driver-core、org.mongodb:mongodb-driver-sync、org.mongodb:bson、org.springframework.data:spring-data-mongodb - 连接字符串配置在yml文件:
mongodb+srv://<username>:<password>@<domain>/<dbname>?ssl=false&authMechanism=PLAIN&connectTimeoutMS=30000&maxIdleTimeMS=600000 - Mongo配置类:
@Configuration public class MongoConfiguration { @Bean public MongoClient mongoClient(@Value("${spring.data.mongodb.uri}") String url) { return MongoClients.create(url); } @Bean public MongoTemplate mongoTemplate(MongoClient mongo, @Value("${mongo.database}") String databaseName) { MongoTemplate mongoTemplate = new MongoTemplate(mongo, databaseName); mongoTemplate.createCollection("Collection_Name"); return mongoTemplate; } } - 异常现象:MongoDB Compass和IntelliJ可正常连接,队友使用相同代码也能正常访问,仅自身Spring Boot应用连接失败
已尝试的无效方案
- Windows系统刷新DNS缓存
- 防火墙添加30000端口的入站/出站规则
- 移除连接URI中的
srv前缀 - 修改DNS地址的InterfaceMetric值
- 更换两台工作笔记本,当前为第三台
调试发现
应用通过ResolverConfigurationImpl.java的loadConfig()方法中的loadDNSconfig0()本地方法加载系统DNS服务器地址,调试显示os_nameservers值为:192.168.1.1 10.2.217.17,10.2.225.17 192.168.1.1。临时将公司VPN的10段DNS移至列表顶部后,应用可正常执行Mongo CRUD操作,但该方案无法永久生效。
解决方案
方案1:强制MongoDB驱动使用指定DNS服务器
自定义DNS解析器,让MongoDB驱动优先使用能解析SRV记录的公司DNS服务器:
import com.mongodb.ConnectionString; import com.mongodb.MongoClientSettings; import com.mongodb.client.MongoClients; import com.mongodb.spi.dns.DnsResolver; import com.mongodb.spi.dns.DnsRecord; import com.mongodb.spi.dns.RecordType; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.naming.directory.Attributes; import javax.naming.directory.InitialDirContext; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; @Configuration public class MongoConfiguration { @Bean public MongoClient mongoClient(@Value("${spring.data.mongodb.uri}") String url) { MongoClientSettings settings = MongoClientSettings.builder() .applyConnectionString(new ConnectionString(url)) .dnsResolver(new CustomDnsResolver()) .build(); return MongoClients.create(settings); } @Bean public MongoTemplate mongoTemplate(MongoClient mongo, @Value("${mongo.database}") String databaseName) { MongoTemplate mongoTemplate = new MongoTemplate(mongo, databaseName); mongoTemplate.createCollection("Collection_Name"); return mongoTemplate; } static class CustomDnsResolver implements DnsResolver { @Override public List<DnsRecord> resolveRecords(String domain, RecordType recordType) { List<DnsRecord> records = new ArrayList<>(); try { // 指定公司DNS服务器地址 Hashtable<String, String> env = new Hashtable<>(); env.put("java.naming.factory.initial", "com.sun.jndi.dns.DnsContextFactory"); env.put("java.naming.provider.url", "dns://10.2.217.17,dns://10.2.225.17"); InitialDirContext ctx = new InitialDirContext(env); Attributes attrs = ctx.getAttributes(domain, new String[]{recordType.name()}); if (attrs != null && attrs.get(recordType.name()) != null) { for (Object attr : attrs.get(recordType.name()).getAll()) { records.add(new DnsRecord(attr.toString(), 300)); } } ctx.close(); } catch (Exception e) { throw new RuntimeException("DNS解析失败", e); } return records; } } }
方案2:通过JVM启动参数指定DNS服务器
在Gradle的bootRun任务中添加JVM参数,强制JVM优先使用公司DNS:
bootRun { jvmArgs += "-Dsun.net.spi.nameservice.provider.1=dns,sun" jvmArgs += "-Dsun.net.spi.nameservice.nameservers=10.2.217.17,10.2.225.17" }
该方式无需修改代码,直接让JVM使用指定DNS解析域名。
方案3:永久调整系统DNS优先级
若修改InterfaceMetric无效,可通过以下步骤调整Windows系统DNS顺序:
- 打开网络连接,右键当前VPN连接,选择属性
- 双击Internet协议版本4 (TCP/IPv4)
- 选择使用下面的DNS服务器地址,输入公司10段DNS作为首选,本地DNS作为备用
- 点击高级,在DNS标签页中,将公司DNS移至列表顶部,删除重复的DNS条目
- 勾选在DNS中注册此连接的地址,保存配置
方案4:使用非SRV格式连接字符串
如果能获取MongoDB节点的具体IP和端口,可替换为标准连接字符串,绕过SRV解析:
mongodb://<username>:<password>@<node1-ip>:<port>,<node2-ip>:<port>/<dbname>?ssl=false&authMechanism=PLAIN&connectTimeoutMS=30000&maxIdleTimeMS=600000&replicaSet=<replica-set-name>
内容的提问来源于stack exchange,提问作者Ketan Deshpande
相关产品推荐
相关产品推荐

