关于 SQS 的一些我不知道的事
发布于 作者: Chris Fidao
说到队列,AWS SQS 服务是一个 **绝佳** 的选择。它非常便宜,非常可靠,而且扩展性比我们大多数人需要的都要高。
总的来说,我喜欢任何我不必自己管理的服务。大多数 AWS 托管服务都很昂贵。SQS 是为数不多的几个非常实用的服务,而且价格非常实惠。
但是,有一些关键的区别你需要知道!以下每个细节都曾让我吃过亏。以下是一些关于 SQS 的细节,你应该知道!
可见性超时
SQS 队列的一个独特之处是可见性超时。
我们大多数人可能已经习惯于不用考虑这个问题——当我们在 Laravel 队列工作程序中获得一项作业时,其他队列工作程序不会接手该作业。
SQS 的工作方式略有不同。假设我们的可见性超时设置为 10 秒。如果我们的队列工作程序花费超过 10 秒来处理作业,SQS 将使该作业再次可见。这意味着另一个队列工作程序可能会接手该作业!
因此,我们需要确保我们的可见性超时设置得比完成作业所需的时间 **更长**。
可见性超时可以设置为每个 SQS 队列的默认值,也可以设置为每个作业的单独值。你甚至可以延长已存在作业的可见性超时。
Laravel 不会为你设置可见性超时,因此最好设置一个高于处理作业所需时间的默认值。
有一个 "hack" 可以用来延长作业的可见性超时!你可以调用 release()
方法,**确保设置了延迟**。
对于 SQS 队列驱动程序,这会 设置可见性超时。如果你有一个特定的作业,可能需要比默认可见性超时更长的时间,你可以在作业的早期调用此方法(并设置延迟!)以增加可见性超时。这将给你的作业更多时间来处理,然后再被设置为可见,以便其他队列工作程序接手。
public function handle() { // Increase visibility timeout for this job // to one minute $this->release(60); // Continue on with the job...}
如果你没有设置延迟,可见性超时将为零,这会立即使作业在 SQS 队列中可见——这可能不是你想要的!
长轮询和短轮询
SQS 的 API 都是基于 HTTP 的。这意味着当我们的队列工作程序轮询 SQS 以获取新作业时,它正在向 SQS API 发出 HTTP 请求。
默认情况下,这会进行 "短轮询"——如果在发出 HTTP 请求时没有可用的作业,SQS 会立即返回一个空响应。
长轮询允许你将一个 HTTP 请求保持打开一定时间。在 HTTP 请求保持打开状态时,SQS 可以随时向队列工作程序发送作业。
Laravel 不会进行任何长轮询,但这里有一点很重要。
如果你使用 SQS 队列驱动程序,你可能会发现某些作业需要一段时间才能处理完成——就好像队列工作程序找不到新的作业一样。这与 SQS 在 AWS 中的扩展方式有关。
以下是来自 SQS 文档 的相关内容。
使用短轮询,ReceiveMessage 请求仅查询服务器的一个子集(基于加权随机分布),以查找可用于包含在响应中的消息。即使查询未找到任何消息,Amazon SQS 也会立即发送响应。
使用长轮询,ReceiveMessage 请求会查询所有服务器以查找消息。Amazon SQS 在收集到至少一条可用消息后发送响应,直到达到请求中指定的消息最大数量。仅当轮询等待时间到期时,Amazon SQS 才会发送空响应。
事实证明,使用长轮询,我们更有可能更快地获得作业,因为它会轮询所有可能包含我们作业的 SQS 服务器!
但是,Laravel 默认情况下不支持长轮询。幸运的是,我们可以采取一些措施。在上面链接的文档的底部有一个小提示。
当 ReceiveMessage 请求的 WaitTimeSeconds 参数以以下两种方式之一设置为 0 时,会发生短轮询。
- ReceiveMessage 调用将 WaitTimeSeconds 设置为 0。
- ReceiveMessage 调用没有设置 WaitTimeSeconds,但队列属性 ReceiveMessageWaitTimeSeconds 设置为 0。
Laravel 不会执行上面的第一条——它不会在轮询新作业时通过设置 WaitTimeSeconds
来显式启用长轮询。
但是,如果我们将 SQS 队列的默认 ReceiveMessageWaitTimeSeconds
参数设置为大于 0,长轮询就会在 SQS 端启用!虽然 Laravel 队列工作程序不会等待 ReceiveMessageWaitTimeSeconds
的完整值(它会等待 AWS PHP SDK 为发出 HTTP 请求设置的默认超时时间),但这仍然会触发 SQS 检查所有服务器,就好像它正在进行长轮询一样,这意味着我们更有可能从我们的 SQS 队列中更快地获得作业。
这是一件小事,但确实帮助我解决了我偶尔对 SQS 队列的烦恼!
保证和作业顺序
SQS 有 2 种类型
- 标准
- FIFO(先进先出)
标准 SQS
标准 SQS 是我们大多数人使用的。它保证 "至少一次传递",除此之外别无其他。这意味着 2 件事
**没有去重**——如果你向 SQS 发送完全相同的作业多次,它将被处理多次。这可能并不令人惊讶。如果你向任何 Laravel 队列发送完全相同的作业,你将结束处理该作业多次!
**无法保证消息顺序**——你不会一定按照发送作业的顺序来处理作业,即使你只使用一个队列工作程序。
这与其他 Laravel 队列驱动程序(如数据库和 Redis 驱动程序)不同。
请注意,标准 SQS 会 *尝试* 按接收作业的顺序发送作业,但由于 SQS 的规模和架构,这并不总是可能的。这一点在 SQS 常见问题解答中说明。
FIFO SQS
FIFO 队列具有 2 种保证
- Exactly-Once 处理
- 消息顺序
FIFO 队列会删除重复作业。你可以允许 SQS 根据作业数据来确定作业是否重复,或者你可以将 Deduplication ID 设置为你选择的任何值。SQS 使用该值来与其他作业进行比较。如果它发现一个具有相同内容或 Deduplication ID 的作业,它将删除重复的作业。
因此,这有助于你确保不会处理同一个作业多次。
FIFO 还保证消息顺序。FIFO 队列按发送作业的顺序传递作业(先进先出——最旧的作业/消息最先被处理)。
在前面的作业完成(删除)之前,不会发送任何作业进行处理。然而,这意味着使用多个队列工作程序是无用的。那么,如何扩展我们的 FIFO 队列以一次处理多个作业呢?
你可以使用 消息组。顺序仅在消息组内保证,因此你可以通过为应按顺序处理的作业分配唯一的消息组来在 FIFO 队列中获得并发性。
例如,您可能希望在应用程序中为每个用户分配一个消息组,在这种情况下,您的消息组 ID 可能设置为类似于“user-x”的内容,其中x
是用户 ID。
FIFO 队列还有更多内容。要了解更多信息,请参阅我们关于使用 FIFO 队列的文章!