问题排查常见问题 - Amazon SDK for Java 2.x
Amazon Web Services 文档中描述的 Amazon Web Services 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 中国的 Amazon Web Services 服务入门 (PDF)

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

问题排查常见问题

在应用程序 Amazon SDK for Java 2.x 中使用时,可能会遇到本主题中列出的运行时错误。使用此处的建议来帮助您找出根本原因并解决错误。

如何修复 “java.net.SocketException:连接重置” 或 “服务器无法完成响应” 错误?

连接重置错误表示您的主机 Amazon Web Services 服务、或任何中间方(例如,NAT 网关、代理、负载均衡器)在请求完成之前关闭了连接。由于原因有很多,因此要找到解决方案,就必须知道连接关闭的原因。以下各项通常会导致连接关闭。

  • 连接处于非活动状态。这在流媒体操作中很常见,在这种操作中,数据在一段时间内没有写入或写出电线,因此中间方检测到连接已失效并关闭了连接。为防止出现这种情况,请确保您的应用程序主动下载或上传数据。

  • 你已经关闭了 HTTP 或 SDK 客户端。确保不要在资源使用期间将其关闭。

  • 代理配置错误。尝试绕过您配置的任何代理,以查看它是否可以解决问题。如果这样可以解决问题,则代理出于某种原因正在关闭您的连接。研究您的特定代理以确定它关闭连接的原因。

如果您无法识别问题,请尝试在网络的客户端边缘为受影响的连接运行 TCP 转储(例如,在您控制的任何代理之后)。

如果您看到 Amazon 端点正在发送TCP RST(重置),请联系受影响的服务,看看他们能否确定重置的原因。请准备好提供请求编号和问题发生时间的时间戳。 Amazon 支持团队还可能受益于电汇日志,这些日志可以准确显示您的应用程序发送和接收的字节以及何时发送。

如何修复 “连接超时”?

连接超时错误表示您的主机 Amazon Web Services 服务、或任何中间方(例如,NAT 网关、代理、负载均衡器)未能在配置的连接超时内与服务器建立新连接。以下内容描述了此问题的常见原因。

  • 配置的连接超时过低。默认情况下,连接超时为 2 秒 Amazon SDK for Java 2.x。如果将连接超时设置得太低,则可能会出现此错误。如果您只进行区域内呼叫,则建议的连接超时为 1 秒;如果您发出跨区域请求,则建议的连接超时时间为 3 秒。

  • 代理配置错误。尝试绕过您配置的任何代理,以查看它是否可以解决问题。如果这样可以解决问题,则代理是连接超时的原因。研究您的特定代理以确定发生这种情况的原因

如果您无法识别问题,请尝试在网络的客户端边缘为受影响的连接运行 TCP 转储(例如,在您控制的任何代理之后),以调查任何网络问题。

如何修复 “java.net.SocketTimeoutException:读取超时”?

读取超时错误表示 JVM 尝试从底层操作系统读取数据,但未在通过 SDK 配置的时间内返回数据。如果操作系统、或任何中间方(例如 Amazon Web Services 服务,NAT 网关、代理、负载均衡器)未能在 JVM 预期的时间内发送数据,则可能会发生此错误。由于原因有很多,因此要找到解决方案,就必须知道未返回数据的原因。

尝试在网络的客户端边缘为受影响的连接运行 TCP 转储(例如,在您控制的任何代理之后)。

如果您看到 Amazon 终端节点正在发送TCP RST(重置),请联系受影响的服务。请准备好提供请求编号和问题发生时间的时间戳。 Amazon 支持团队还可能受益于电汇日志,这些日志可以准确显示您的应用程序发送和接收的字节以及何时发送。

如何修复 “无法执行HTTP请求:等待池连接超时” 错误?

此错误表示请求无法在指定的最长时间内从池中获得连接。要解决问题,我们建议您启用 SDK 客户端指标,以便向 Amazon CloudWatch 发布指标。HTTP 指标可以帮助缩小根本原因范围。以下项目描述了此错误的常见原因。

  • 连接泄漏。您可以通过检查LeasedConcurrencyAvailableConcurrency、和MaxConcurrency指标来对此进行调查。如果在到达之前一直LeasedConcurrency增加MaxConcurrency但从未降低,则可能存在连接泄漏。泄漏的常见原因是流媒体操作(例如 S3 getObject 方法)未关闭。我们建议您的应用程序尽快从输入流中读取所有数据,然后关闭输入流。下图显示了连接泄漏的 SDK 指标可能是什么样子。

    显示可能存在连接泄漏的 CloudWatch 指标的屏幕截图。
  • 连接池不足。如果您的请求速率过高,并且已配置的连接池大小无法满足请求需求,则可能会发生这种情况。默认连接池大小为 50,当池中的连接达到最大值时,HTTP 客户端会将传入的请求排队,直到连接可用为止。下图显示了连接池不足的 SDK 指标可能是什么样子。

    显示连接池饥饿情况的 CloudWatch 指标屏幕截图。

    要缓解此问题,请考虑采取以下任何措施。

    • 增加连接池的大小,

    • 增加采集超时时间。

    • 降低请求速率。

    通过增加最大连接数,可以增加客户端吞吐量(除非网络接口已被充分利用)。但是,您最终可能会遇到操作系统对进程使用的文件描述符数量的限制。如果您已经完全使用网络接口或无法进一步增加连接数,请尝试延长获取超时时间。随着时间的增加,在超时之前,您可以获得更多时间来请求获取连接。如果连接没有释放,则后续请求仍将超时。

    如果您无法通过使用前两种机制来解决问题,请尝试以下选项来降低请求速率。

    • 平滑您的请求,以免大量流量爆发使客户端超负荷。

    • 通过呼叫提高效率 Amazon Web Services 服务。

    • 增加发送请求的主机数量。

  • I/O 线程太忙了。这仅适用于您使用带的异步 SDK 客户端NettyNioAsyncHttpClient。如果该AvailableConcurrency指标不低(表示池中存在连接),而是很高,ConcurrencyAcquireDuration则可能是因为 I/O 线程无法处理请求。确保你不是Runnable:run作为未来完成执行器传递的,也不要在响应的未来完成链中执行耗时的任务,因为这可能会阻塞 I/O 线程。如果不是这样,请考虑使用该eventLoopGroupBuilder方法增加 I/O 线程的数量。作为参考,NettyNioAsyncHttpClient实例的默认 I/O 线程数是主机 CPU 核心数的两倍。

  • TLS 握手延迟高。如果您的AvailableConcurrency指标接近 0 且小LeasedConcurrencyMaxConcurrency,则可能是因为 TLS 握手延迟很高。下图显示了高 TLS 握手延迟的 SDK 指标可能是什么样子。

    可能表明 TLS 握手延迟较高的 CloudWatch 指标的屏幕截图。

    对于 Java SDK 提供的不基于 CRT 的 HTTP 客户端,请尝试启用 TLS 日志来解决 TLS 问题。对于 Amazon 基于 CRT 的 HTTP 客户端,请尝试启用 Amazon CRT 日志。如果您发现 Amazon 端点似乎需要很长时间才能执行 TLS 握手,则应联系受影响的服务

我该如何修复NoClassDefFoundErrorNoSuchMethodErrorNoSuchFieldError

A NoClassDefFoundError 表示无法在运行时加载类。导致此错误的两个最常见原因是:

  • 该类在类路径中不存在,因为 JAR 丢失或类路径上的 JAR 版本不正确。

  • 该类无法加载,因为其静态初始化器引发了异常。

同样,NoSuchMethodErrors 和 NoSuchFieldError s 通常是由于 JAR 版本不匹配所致。我们建议您执行以下步骤。

  1. 检查你的依赖关系,确保你使用的所有 SDK jar 版本相同。找不到类、方法或字段的最常见原因是升级到新的客户端版本,但继续使用旧的 “共享” SDK 依赖版本。新的客户端版本可能会尝试使用仅存在于较新的 “共享” SDK 依赖项中的类。尝试运行mvn dependency:treegradle dependencies(对于 Gradle)来验证 SDK 库版本是否全部匹配。为了完全避免将来出现此问题,我们建议使用 BOM(物料清单)来管理 SDK 模块版本。

    以下示例向您展示了混合 SDK 版本的示例。

    [INFO] +- software.amazon.awssdk:dynamodb:jar:2.20.00:compile [INFO] | +- software.amazon.awssdk:aws-core:jar:2.13.19:compile [INFO] +- software.amazon.awssdk:netty-nio-client:jar:2.20.00:compile

    的版本dynamodb是 2.20.00,的版本aws-core是 2.13.19。aws-core工件版本也应为 2.20.00。

  2. 在日志的早期检查语句,以查看某个类是否由于静态初始化失败而无法加载。当类第一次加载失败时,它可能会抛出一个不同的、更有用的异常来指定无法加载该类的原因。这个可能有用的异常只发生一次,因此以后的日志语句只会报告未找到该类。

  3. 检查您的部署过程,确保它实际上与您的应用程序一起部署了所需的 JAR 文件。您可能使用正确的版本进行构建,但是为应用程序创建类路径的过程排除了必需的依赖项。

如何修复 “SignatureDoesNotMatch” 错误或 “我们计算的请求签名与您提供的签名不匹配” 错误?

SignatureDoesNotMatch错误表示生成的签名 Amazon SDK for Java 和生成的签名 Amazon Web Services 服务 不匹配。以下项目描述了潜在原因。

  • 代理方或中间方修改请求。例如,代理或负载均衡器可能会修改由 SDK 签名的标头、路径或查询字符串。

  • 服务和 SDK 的不同之处在于它们在生成待签字符串时对请求进行编码的方式。

要调试此问题,我们建议您为 SDK 启用调试日志记录。尝试重现错误并找到 SDK 生成的规范请求。在日志中,规范请求用AWS4 Canonical Request: ...标记,待签字符串标记。AWS4 String to sign: ...

如果您无法启用调试(例如,因为它只能在生产环境中重现),请向应用程序添加逻辑,以便在错误发生时记录有关请求的信息。然后,您可以使用该信息尝试在启用调试日志记录的集成测试中将错误复制到生产环境之外。

收集规范请求和待签字符串后,将其与 Signature 版本 4 规范进行比较,以确定 SDK 生成待Amazon 签字符串的方式是否存在问题。如果出现问题,可以创建GitHub 错误报告给 Amazon SDK for Java。

如果没有出现任何问题,您可以将 SDK 的待签字符串与失败响应中 Amazon Web Services 服务 返回的待签字符串(例如 Amazon S3)进行比较。如果这不可用,则应联系受影响的服务,以查看它们生成了哪些规范请求和待签字符串,以便进行比较。这些比较可以帮助识别可能修改了请求或服务与客户端之间的编码差异的中间方。

有关签署请求的更多背景信息,请参阅 Amazon Identity and Access Management 用户指南中的签署 Amazon API 请求

例 规范请求的
PUT /Example-Bucket/Example-Object partNumber=19&uploadId=string amz-sdk-invocation-id:f8c2799d-367c-f024-e8fa-6ad6d0a1afb9 amz-sdk-request:attempt=1; max=4 content-encoding:aws-chunked content-length:51 content-type:application/octet-stream host:xxxxx x-amz-content-sha256:STREAMING-UNSIGNED-PAYLOAD-TRAILER x-amz-date:20240308T034733Z x-amz-decoded-content-length:10 x-amz-sdk-checksum-algorithm:CRC32 x-amz-trailer:x-amz-checksum-crc32
例 待签字符串
AWS4-HMAC-SHA256 20240308T034435Z 20240308/us-east-1/s3/aws4_request 5f20a7604b1ef65dd89c333fd66736fdef9578d11a4f5d22d289597c387dc713

如何修复 “java.lang.IllegalStateException:连接池关闭” 错误?

此错误表示底层 Apache HTTP 连接池已关闭。以下项目描述了潜在原因。

  • SDK 客户端过早关闭。只有在关联的客户端关闭时,SDK 才会关闭连接池。确保不要在资源使用期间将其关闭。

  • A java.lang.Error 被扔了。诸如此类的错误OutOfMemoryError会导致 Apache HTTP 连接池关闭。检查您的日志中是否有错误堆栈痕迹。还要检查你的代码,看看它捕获 Throwable s 或 Error s 但会吞下输出以防止错误浮出水面的地方。如果您的代码未报告错误,请重写代码以记录信息。记录的信息有助于确定错误的根本原因。

  • 您尝试使用在证书提供程序关闭DefaultCredentialsProvider#create()后返回的凭证提供程序。 DefaultCredentialsProvider#create返回一个单例实例,因此,如果它已关闭并且您的代码调用了该resolveCredentials方法,则会在缓存的凭证(或令牌)过期后引发异常。

    检查您的代码中DefaultCredentialsProvider是否有关闭的地方,如以下示例所示。

    • 通过调用关闭单例实例 DefaultCredentialsProvider#close().

      DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create(); // Singleton instance returned. AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials(); // Make calls to Amazon Web Services 服务. defaultCredentialsProvider.close(); // Explicit close. // Make calls to Amazon Web Services 服务. // After the credentials expire, either of the following calls eventually results in a "Connection pool shut down" exception. credentials = defaultCredentialsProvider.resolveCredentials(); // Or credentials = DefaultCredentialsProvider.create().resolveCredentials();
    • 在 try-with-resources方块DefaultCredentialsProvider#create()中调用。

      try (DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create()) { AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials(); // Make calls to Amazon Web Services 服务. } // After the try-with-resources block exits, the singleton DefaultCredentialsProvider is closed. // Make calls to Amazon Web Services 服务. DefaultCredentialsProvider defaultCredentialsProvider = DefaultCredentialsProvider.create(); // The closed singleton instance is returned. // If the credentials (or token) has expired, the following call results in the error. AwsCredentials credentials = defaultCredentialsProvider.resolveCredentials();

    DefaultCredentialsProvider.builder().build()如果您的代码已关闭单例实例,并且您需要使用解析证书,则通过调用来创建一个新的非单例实例。DefaultCredentialsProvider