【问题标题】:S3 Java client fails a lot with "Premature end of Content-Length delimited message body" or "java.net.SocketException Socket closed"S3 Java 客户端因“内容长度分隔的消息正文过早结束”或“java.net.SocketException 套接字已关闭”而失败很多
【发布时间】:2012-04-14 17:16:04
【问题描述】:

我有一个在 S3 上做了很多工作的应用程序,主要是从它下载文件。我看到很多此类错误,我想知道这是否是我的代码中的问题,或者该服务是否真的像这样不可靠。

我用来从 S3 对象流中读取的代码如下:

public static final void write(InputStream stream, OutputStream output) {

  byte[] buffer = new byte[1024];

  int read = -1;

  try {

    while ((read = stream.read(buffer)) != -1) {
      output.write(buffer, 0, read);
    }

    stream.close();
    output.flush();
    output.close();
  } catch (IOException e) {
    throw new RuntimeException(e);
  }

}

这个OutputStream是一个new BufferedOutputStream(new FileOutputStream(file))。我正在使用最新版本的 Amazon S3 Java 客户端,此调用在放弃前被重试 四次。所以,在尝试了 4 次之后,它仍然失败。

感谢任何关于我如何改进这一点的提示或技巧。

【问题讨论】:

  • 是所有(或大部分)文件、随机文件还是有限且可重现的文件集都会发生这种情况?您是否在第一次上传之前设置了任何元数据?我见过某些文件上的元数据(或缺少元数据)可能会导致一些奇怪问题的情况。如果您还没有尝试过,可能值得一试。
  • 大部分是随机文件,我们不使用任何元数据:(
  • 只是猜测。您确定这些随机文件在 s3 正确上传了吗?尝试通过 GET 请求或其他工具下载这些文件。
  • 最终它可以工作,这是我的主要问题,我必须重试多次才能做到。

标签: java sockets amazon-s3 connection amazon-web-services


【解决方案1】:

我刚刚设法克服了一个非常相似的问题。就我而言,我得到的例外是相同的;它发生在较大的文件中,但不适用于小文件,并且在单步调试器中从未发生过。

问题的根本原因是 AmazonS3Client 对象在下载过程中被垃圾收集,导致网络连接中断。发生这种情况是因为我在每次调用加载文件时都构建了一个新的 AmazonS3Client 对象,而首选用例是创建一个在调用中仍然存在的持久客户端对象 - 或者至少保证在整个调用过程中都存在下载。因此,简单的补救措施是确保保留对 AmazonS3Client 的引用,以免它被 GC。

AWS 论坛上对我有帮助的链接在这里:https://forums.aws.amazon.com/thread.jspa?threadID=83326

【讨论】:

  • 将客户端对象保存在方法中起到了作用,哎呀。谢谢史蒂夫!
【解决方案2】:

由于某种原因,在客户端获取所有数据之前,网络正在关闭连接。

任何 HTTP 请求的一部分是内容长度,您的代码正在获取标头,说嘿,伙计,这是数据,它就是这么多......然后在客户端读取所有数据之前连接就断开了.. 所以它的轰炸除外。

我会查看您的操作系统/网络/JVM 连接超时设置(尽管在这种情况下 JVM 通常从操作系统继承)。关键是要弄清楚网络的哪一部分导致了问题。是您的计算机级别设置说,不打算再等待数据包.. 是不是您正在使用非阻塞读取,它在您的代码中有超时设置,它说,嘿,还没有得到来自服务器的任何数据都比我应该等待的时间长,所以我将放弃连接和异常。等等等等等等。

最好的办法是低级别监听数据包流量并向后跟踪,以查看发生连接中断的位置,或者查看您是否可以在您可以控制的事情(例如您的软件和操作系统/JVM)中设置超时。

【讨论】:

  • 太好了,我们遇到这个问题已经有一段时间了,而且我们的负载均衡器也有超时问题。出于某种原因,我没有想到这两者可能是一样的。
【解决方案3】:

首先,当(且仅当)您自己与 Amazon S3 之间的连接出现问题时,您的代码才能完全正常运行。正如 Michael Slade points out,标准连接级调试建议适用。

至于您的实际源代码,我注意到您应该注意一些代码异味。直接在源代码中注释它们:

public static final void write(InputStream stream, OutputStream output) {

  byte[] buffer = new byte[1024]; // !! Abstract 1024 into a constant to make 
                                  //  this easier to configure and understand.

  int read = -1;

  try {

    while ((read = stream.read(buffer)) != -1) {
      output.write(buffer, 0, read);
    }

    stream.close(); // !! Unexpected side effects: closing of your passed in 
                    //  InputStream. This may have unexpected results if your
                    //  stream type supports reset, and currently carries no 
                    //  visible documentation.

    output.flush(); // !! Violation of RAII. Refactor this into a finally block, 
    output.close(); //  a la Reference 1 (below).

  } catch (IOException e) {
    throw new RuntimeException(e); // !! Possibly indicative of an outer 
                                   //   try-catch block for RuntimeException. 
                                   //   Consider keeping this as IOException.
  }
}

(Reference 1)

否则,代码本身看起来还不错。 IO 异常应该在您连接到变化无常的远程主机的情况下发生,并且您最好的行动方案是起草一个合理的策略来缓存和重新连接在这些情况下。

【讨论】:

    【解决方案4】:
    1. 尝试使用wireshark 查看发生这种情况时线路上发生的情况。

    2. 尝试暂时用您自己的 Web 服务器替换 S3,看看问题是否仍然存在。如果是,那是您的代码,而不是 S3。

    它是随机的,这表明您的主机和某些 S3 主机之间存在网络问题。

    【讨论】:

      【解决方案5】:

      根据我的经验,S3 也可以关闭慢速连接。

      【讨论】:

      • 如果可能,尝试将您的输入流存储到文件中,并使用 POST 通过浏览器将其上传到 S3(将您的 java 服务器代码替换到同一台机器上的浏览器上,以确定问题是否出在您的代码)
      【解决方案6】:

      我会非常仔细地查看离您的客户端应用最近的网络设备。此问题与某些网络设备在您和服务之间丢弃数据包有关。查看问题首次出现时是否有起点。那时是否有任何变化,例如路由器的固件更新或交换机的更换?

      根据从 ISP 购买的数量验证您的带宽使用情况。一天中是否有时间接近该限制?你能得到你的带宽使用图表吗?查看提前终止是否可以与高带宽使用相关联,特别是在它接近某个已知限制时。问题似乎只在小文件和大文件快要完成下载时才出现?从您的 ISP 购买更多带宽可能会解决此问题。

      【讨论】:

      • 这些是连接到 S3 机器的 EC2 机器,没有任何限制。不过还是谢谢:)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-19
      • 1970-01-01
      • 1970-01-01
      • 2017-03-25
      相关资源
      最近更新 更多