java - Amazon Web Services S3 中的线程安全文件重命名

标签 java scala amazon-web-services amazon-s3 rename

我需要移动重命名 AWS S3 存储中的对象。

我发现的所有解决方案都需要复制然后删除。但是,这会在两个文件都存在的时间内留下很短的时间,我认为这不是线程安全的。

有没有办法以线程安全的方式做到这一点?

该代码使用 Java AWS SDK 在 Scala 中编写。

编辑: 罗布,感谢您的回复,我相信我理解代码的作用,但让我觉得我问了错误的问题。

让我根据我想要实现的目标来描述它,而不是具体的 AWS 功能。

我有一个 S3 目录,它定期从外部源接收文件。我有多个进程需要“处理”这些文件,并且每个文件只能处理一次。

过去,作为处理此问题的一种廉价方法,我使用重命名来移动文件或将其标记为正在处理。如果重命名成功,则进程知道它“拥有”该文件并将继续处理。如果由于源文件不存在而失败,则会尝试目录中的下一个文件。

我需要的是一种方法,最好只是 S3,它允许多个进程处理文件,同时确保每个文件只处理一次。

在下面的解决方案中,由于“查找”和“删除”是单独的方法,并且如果文件不存在,删除不会失败,所以我不确定我是否看到这两个进程不能简单地(在最坏的情况下)案例场景)都与另一个同步完成。

文件移动可能是错误的解决方案,而且我对 AWS 的经验不足,导致我无法找到更好的方法来完成此任务。

最佳答案

In the past as a cheap way of handling this, I've used a rename to either move the file or mark it as processing. If the rename succeeded, then the process knew it 'owned' the file and would continue processing. If it failed because the source file did not exist, then it would try the next file in the directory.

首先让我指出,这种使用线程原子重命名来获取处理文件的独占访问权限的技术是有效的,但它确实存在使文件未处理的风险。想象一下如果线程(或整个服务器)在重命名后立即死亡会发生什么。如果没有强大的方法来跟踪哪些文件尚未完成以及重试它们的方法,您的系统将不会具有很强的弹性。

正如您所注意到的,S3 没有原子重命名操作,因此您常用的技术无法按您的意愿工作。

S3 有一个很好的可以配置的“通知”功能。就您而言,您可能希望在创建文件时收到通知。通知可以传送到 SNS、SQS 或 Lambda。您可能需要 SQS 或 Lambda。使用 SQS,消息会添加到队列中,您可以让线程抓取并处理文件。 SQS 模型保证“至少一次”传递,并将重试传递,直到消息被删除(或从队列中老化)。如果未删除则重新传送时间是可配置的。请注意,SQS 可能会多次传递相同的消息 - 它们宁愿过度传递而不是不传递消息。如果可以很少地双重处理文件,那么这可能适合您。我们广泛使用 SQS 队列,并且很高兴。

我不熟悉 Lambda 消息处理的详细语义。

我建议您通过 Google 搜索“S3 事件通知”以了解更多详细信息。

原始问题的原始答案:

我不确定问题是“线程安全” - 也许更多的是“事务完整性”?

无论如何,您认为进行 S3“原子”重命名并不明显,这是正确的。我认为你必须“选择你的毒药” - 要么你必须面对这样一个事实:1)你同时拥有旧的和新的副本,或者2)你有一段时间既没有旧的也没有新的副本新副本。

无论哪种情况,您需要处理的一个关键问题是坚持您正在执行重命名的事实(直到确认重命名完成)。如果某个数据库中有一行代表该文件,那么您可以在那里保留状态。以下假设您不想使用 S3 以外的任何内容来持久保存状态。

您实际上将复制该文件两次,并使用临时文件夹作为中间副本。您可以使用单独的线程执行每个步骤(查找要处理的文件),也可以使用单个线程检查各种条件并执行其余步骤。换句话说,您需要查找已部分完成的重命名(但该线程未能完成)并从中断处继续。

在此示例中,我们将从 A 重命名为 B 并使用名为 tmp 的临时文件夹。

如果您希望短暂保留两份副本:

1. Copy A to tmp/A-B (the file name has before and after names in it).
2. Finding tmp/A-B: copy it to B.
3. Finding tmp/A-B, A and B: delete A.
4. Finding tmp/A-B, A is missing and B exists: delete tmp/A-B.

如果您不想暂时不保留任何副本:

1. Copy A to tmp/A-B.
2. Finding tmp/A-B and A: delete A.
3. Finding tmp/A-B and A is missing and B is missing: copy tmp/A-B to B.
4. Finding tmp/A-B and A is missing and B exists: delete tmp/A-B.

关于java - Amazon Web Services S3 中的线程安全文件重命名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35921890/

相关文章:

java - 在 Spring Boot/FreeMarker 中加载图像时出现问题

java - 比较字符串,就好像它们是数字一样

java -/struts/webconsole.html 在设置 Struts2 devMode=false 后仍然有效

java - java 8 中的并行流与 Completablefuture

scala - Scala 3 中使用 Varargs 的模糊重载

Scala None 实例不是 == None

amazon-web-services - 将 AWS Certificate Manager(ACM 证书)与 Elastic Beanstalk 结合使用

java - Apache Spark : issue with Scala example

node.js - Cloudfront 分发不适用于 EC2 实例

amazon-web-services - 如何使用 golang 检查 s3 对象大小