EC2上Java应用偶发SQS连接丢失问题咨询
平均每月一次,运行在AWS EC2实例上的Java应用会丢失与AWS SQS的连接,报错信息如下:
尝试与服务交互时收到UnknownHostException。查看原因了解无法解析的具体端点。如果此前该端点可正常工作,可能存在网络连接问题或DNS缓存存储端点时间过长。
根本错误:java.net.UnknownHostException: sqs.eu-west-3.amazonaws.com
已确认Java DNS缓存配置(基于amazoncorretto:17-alpine镜像默认值):
networkaddress.cache.ttl=30networkaddress.cache.negative.ttl=10
SQS客户端配置(AWS SDKv2):
SqsClient sqsClient = SqsClient.builder() .region(Region.EU_WEST_3) .credentialsProvider(InstanceProfileCredentialsProvider.create()) .build();
消息消费代码:
ReceiveMessageRequest receiveMessageRequest = ReceiveMessageRequest.builder() .queueUrl(queueUrl) .maxNumberOfMessages(1) .visibilityTimeout(30) .build(); sqsClient.receiveMessage(receiveMessageRequest) .messages() .forEach(message -> /*some processing*/);
当前重试策略:使用默认的STANDARD重试模式,重试2次,指数退避从100ms开始,总重试时长不足1秒,小于networkaddress.cache.negative.ttl(10秒)。
1. 是否只需增加重试次数即可解决问题?
不一定只靠增加重试次数,但调整重试策略是核心解决方向之一。
当前总重试时长不足1秒,而networkaddress.cache.negative.ttl为10秒——第一次DNS解析失败后,Java会缓存这个失败结果10秒,期间不会尝试重新解析,现有重试全部发生在这个缓存窗口内,必然都会失败。
要解决这个问题,有两个关键调整方向:
- 延长重试总时长:调整重试间隔或增加重试次数,让最后一次重试发生在10秒的负缓存过期之后,确保能触发新的DNS解析。
- 缩短DNS负缓存时间:将
networkaddress.cache.negative.ttl调小(比如设为1秒),让失败的DNS记录更快过期,能更早触发重新解析。但需注意过短的负缓存可能增加DNS服务器负载,需权衡利弊。
另外,也可以在AWS SDKv2中配置自定义重试策略,针对UnknownHostException这类DNS相关错误设置专门的重试规则,比如更长的退避时间,确保跨过负缓存窗口。
2. 多个配置相似的应用是否应该同时抛出UnknownHostException?
不一定同时触发,原因如下:
- DNS缓存独立性:每个Java进程(或Docker容器)都有独立的DNS缓存,同一EC2上的应用,各自的缓存过期时间、解析时机可能不同,只有刚好在DNS故障窗口发起请求的应用才会报错。
- 请求触发时机差异:不同应用的消息消费频率不同,有的可能在DNS故障窗口外发起请求,不会触发错误,报错时间点会分散。
- EC2网络路径差异:如果应用运行在不同EC2实例上,每个实例的DNS解析路径(如VPC DNS服务器、本地缓存)可能存在差异,故障可能只影响部分实例上的应用。
因此,多个配置相似的应用不一定同时抛出该错误,零散、非同步的报错是正常现象。
内容的提问来源于stack exchange,提问作者Raphallal

