检查S3文件是否已被修改

loo*_*oop 8 bash shell amazon-s3

如何修改Amazon S3文件(小.xml文件),如何使用shell脚本检查.我目前正在使用curl每10秒检查一次,但它正在发出许多GET请求.

curl "s3.aws.amazon.com/bucket/file.xml"
if cmp "file.xml" "current.xml"
then
     echo "no change"
else
     echo "file changed"
     cp "file.xml" "current.xml"
fi 
sleep(10s)
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法来检查每10秒减少GET请求的数量?(这是建立在rails应用程序之上,所以我可以在rails中构建一个处理程序?)

Bru*_*eis 8

让我首先告诉你一些关于 S3 的事实。您可能知道这一点,但如果您不知道,您可能会发现您当前的代码可能有一些“意外”行为。

S3 和“最终一致性”

S3 为被覆盖的对象提供“最终一致性”。从S3 常见问题解答中,您有:

问:Amazon S3 采用什么数据一致性模型?

所有区域中的 Amazon S3 存储桶为新对象的 PUTS 提供先写后读一致性,并为覆盖 PUTS 和 DELETES 提供最终一致性

覆盖的最终一致性意味着,无论何时更新对象(即,每当您的小 XML 文件被覆盖时),检索文件的客户端可能会看到新版本,也可能会看到旧版本。多长时间?在一段不确定的时间内。它通常在不到 10 秒的时间内实现一致性,但您必须假设最终需要 10 秒以上才能实现一致性。更有趣的是(可悲的是?),即使成功检索新版本之后,客户端仍可能会在稍后收到旧版本。

您可以确定的一件事是:如果客户端开始下载文件的一个版本,它将下载整个版本(换句话说,您不可能收到例如 XML 文件的前半部分作为旧版,下半部为新版)。

考虑到这一点,请注意您的脚本可能无法在 10 秒的时间范围内识别更改:即使在更改之后,您也可以发出多个请求,直到您的脚本下载更改后的版本。即便如此,在您检测到更改之后,(不幸的是)下一个请求完全有可能下载前一个(!)版本,并在您的代码中触发另一个“更改”,然后下一个将提供当前版本,并在您的代码中触发另一个“更改”!


如果您对 S3 提供最终一致性这一事实感到满意,那么有一种方法可以改进您的系统。

思路一:S3事件通知+SNS

您提到您考虑过使用 SNS。这绝对是一种有趣的方法:您可以启用 S3 事件通知,然后在文件更新时通过 SNS 获取通知。

你是如何得到通知的?您需要创建订阅,这里有几个选项。

想法 1.1:S3 事件通知 + SNS + 一个“网络应用程序”

如果您有一个“Web 应用程序”,即在可公开访问的 HTTP 端点中运行的任何内容,您可以创建一个 HTTP 订阅者,这样无论何时发生,SNS 都会通过通知调用您的服务器。这在您的场景中可能或不可能或不可取

思路二:S3事件通知+SQS

您可以在 SQS 中创建一个消息队列,并让 S3 将通知直接传送到队列。这也可以作为S3 事件通知 + SNS + SQS,因为您可以添加一个队列作为 SNS 主题的订阅者(优点是,如果您以后需要添加功能,您可以添加更多队列并订阅它们到同一主题,因此获得通知的“多个副本”)。

要检索通知,您需要调用 SQS。您仍然需要轮询 - 即,有一个循环并在 SQS 上调用 GET(与 S3 GET 相比,其成本大致相同,或者根据地区的不同可能略高一些)。稍有不同的是,您可以稍微减少总请求的数量——SQS 支持长达 20 秒的长轮询请求:您对 SQS 进行 GET 调用,如果没有消息,SQS 会保留请求到 20 秒,如果消息到达则立即返回,或者如果在这 20 秒内没有消息可用则返回空响应。因此,您将每 20 秒仅发送 1 次 GET,以获得比当前更快的通知。您可能会将 GET 的数量减半(每 10 秒一次到 S3,而每 20 秒一次到 SQS)。

此外 - 您可以选择使用一个 SQS 队列来聚合所有 XML 文件的所有更改,或者选择多个 SQS 队列,每个 XML 文件一个。使用单个队列,您将大大减少 GET 请求的总数。如果每个 XML 文件有一个队列,那么与现在相比,您可以将 GET 请求的数量“减半”。

想法 3:S3 事件通知 + AWS Lambda

您也可以为此使用 Lambda 函数。这可能需要对您的环境进行更多更改 - 您不会使用 Shell 脚本进行轮询,但可以将 S3 配置为调用 Lambda 函数作为对事件的响应,例如 XML 文件的更新。您可以使用 Java、Javascript 或 Python 编写代码(有些人设计了一些“技巧”来使用其他语言,包括 Bash)。

这样做的好处是不再需要轮询,而且您不必维护 Web 服务器(如“idea 1.1”中所示)。只要有变化,您的代码就会“简单地运行”。

请注意,无论您使用这些想法中的哪一种,您仍然必须处理最终一致性。换句话说,你会知道PUT/POST 已经发生,但是一旦你的代码发送了一个 GET,你仍然可以收到旧版本......

想法 4:改用 DynamoDB

如果您有能力对系统进行更多结构性更改,则可以考虑使用 DynamoDB 来完成此任务。

我建议这样做的原因是因为 DynamoDB 支持强一致性,即使对于更新也是如此。请注意,这不是默认设置 - 默认情况下,DynamoDB 在最终一致性模式下运行,但“检索”操作(例如 GetItem)支持完全一致的读取。

此外,DynamoDB 具有我们所说的“DynamoDB Streams”,这是一种机制,允许您获取对表中任何(或所有)项目所做的更改流。可以轮询这些通知,或者甚至可以将它们与 Lambda 函数结合使用,该函数会在发生更改时自动调用!这一点,再加上 DynamoDB 可以以强一致性使用这一事实,可能会帮助您解决问题。

在 DynamoDB 中,保持较小的记录通常是一个好习惯。您在评论中提到您的 XML 文件大约为 2kB - 我想说这可以被认为“足够小”,因此它非常适合 DynamoDB!(推理:DynamoDB 读取通常计算为 4kB 的倍数;因此要完全读取 1 个 XML 文件,您只需要读取 1 次;此外,这取决于您的操作方式,例如使用查询操作而不是GetItem 操作,您可能能够从 DynamoDB 读取 2 个 XML 文件,仅消耗 1 个读取操作)。

一些参考:


Ras*_*ash 5

我可以想到另一种方法,即使用S3 版本控制;这将需要对代码进行最少的更改

版本控制是将对象的多个变体保留在同一存储桶中的一种方法。

这意味着每次file.xml上传新版本时,S3 都会创建一个新版本。

在脚本中,不要获取对象并进行比较,而是获取包含该字段的对象的 HEADVersionId。将此版本与以前的版本进行匹配以查明文件是否已更改。

如果文件确实发生了变化,则获取新文件,同时获取该文件的新版本并将其保存在本地,以便下次可以使用此版本来检查是否已上传更新的版本。

注意 1:您仍然会对 S3 进行大量调用,但不是每次都获取整个文件,而是仅获取文件的元数据,速度更快且尺寸更小。

注 2:但是,如果您的目标是减少调用次数,我能想到的最简单的解决方案是使用 lambda。您可以在每次上传文件时触发 lambda 函数,然后调用服务的 REST 端点来通知您文件更改。