如何解决 OpenSearch Service 中的搜索或写入拒绝问题?

3 分钟阅读
0

当我向我的 Amazon OpenSearch Service 集群提交搜索或写入请求时,这些请求被拒绝。

简短描述

当您在 OpenSearch Service 集群中写入或搜索数据时,可能会收到以下 HTTP 429 错误或 es_rejected_execution_exception

error":"elastic: Error 429 (Too Many Requests): rejected execution of org.elasticsearch.transport.TransportService$7@b25fff4 on
EsThreadPoolExecutor[bulk, queue capacity = 200, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@768d4a66[Running,
pool size = 2, active threads = 2, queued tasks = 200, completed tasks = 820898]] [type=es_rejected_execution_exception]"

Reason={"type":"es_rejected_execution_exception","reason":"rejected execution of org.elasticsearch.transport.TcpTransport$RequestHandler@3ad6b683 on EsThreadPoolExecutor[search, queue capacity = 1000, org.elasticsearch.common.util.concurrent.EsThreadPoolExecutor@bef81a5[Running, pool size = 25, active threads = 23, queued tasks = 1000, completed tasks = 440066695]]"

以下变量可能导致 HTTP 429 错误或 es_rejected_execution_exception

  • 数据节点实例类型和搜索或写入限制
  • 实例指标的值较高
  • 活动线程和队列线程
  • CPU 利用率高、JVM 内存压力大

发生 HTTP 429 错误的原因可能在于对向集群发出的搜索写入请求。拒绝还可能来自集群的单个节点或多个节点。

**注意:**不同版本的 Elasticsearch 使用不同的线程池来处理对 _index API 的调用。Elasticsearch 版本 1.5 和 2.3 使用索引线程池。Elasticsearch 版本 5.x、6.0 和 6.2 使用批量线程池。Elasticsearch 版本 6.3 及更高版本使用写入线程池。有关详细信息,请参阅 Elasticsearch 网站上的线程池

解决方法

数据节点实例类型和搜索或写入限制

数据节点实例类型具有固定的虚拟 CPU(vCPU)。将 vCPU 计数插入公式中,检索节点在进入队列之前可以执行的并发搜索写入操作。如果活动线程已满,则该线程会溢出到队列并最终被拒绝。有关 vCPU 与节点类型之间关系的详细信息,请参阅 OpenSearch Service 定价

此外,每个节点可以执行的搜索次数或每个节点可以执行的写入次数有限制。此限制基于线程池定义和 Elasticsearch 版本号。有关详细信息,请参阅 Elasticsearch 网站上的线程池

例如,如果您为 Elasticsearch 集群(版本 7.4)中的五个节点选择 R5.2xlarge 节点类型,则该节点将有 8 个 vCPU。

使用以下公式计算搜索请求的最大活动线程数:

int ((# of available_processors * 3) / 2) + 1

使用以下公式计算写入请求的最大活动线程数:

int (# of available_processors)

对于 R5.2xlarge 节点,您最多可以执行 13 次搜索操作:

(8 VCPUs * 3) / 2 + 1 = 13 operations

对于 R5.2xlarge 节点,您最多可以执行 8 次写入操作:

8 VCPUs = 8 operations

对于具有五个节点的 OpenSearch Service 集群,您最多可以执行 65 次搜索操作:

5 nodes * 13 = 65 operations

对于具有五个节点的 OpenSearch Service 集群,您最多可以执行 40 次写入操作:

5 nodes * 8 = 40 operations

实例指标的值较高

要解决 429 异常,请检查您的集群的以下 Amazon CloudWatch 指标:

  • **IndexingRate:**每分钟索引操作的次数。对 _bulk API 的单次调用(添加两个文档并更新两个文档)算作四次操作,这些操作可能分布在各节点上。如果该索引有一个或多个副本,则集群中的其他节点也会记录总共四次索引操作。文档删除不计入 IndexingRate 指标。
  • **SearchRate:**一个数据节点上所有分片每分钟的搜索请求总数。对 _search API 的单次调用可能会返回来自许多不同分片的结果。如果一个节点上有五个不同的分片,则即使客户端仅发出了一个请求,该节点也会对此指标报告“5”次操作。
  • **CoordinatingWriteRejected:**协调节点上发生的拒绝总数。这些拒绝是由自 OpenSearch Service 启动以来积累的索引压力造成的。
  • **PrimaryWriteRejected:**主分片上发生的拒绝总数。这些拒绝是由自上次 OpenSearch Service 启动以来积累的索引压力造成的。
  • **ReplicaWriteRejected:**由于索引压力而在副本分片上发生的拒绝总数。这些拒绝是由自上次 OpenSearch Service 启动以来积累的索引压力造成的。
  • **ThreadpoolWriteQueue:**写入线程池中排队任务的数量。此指标会指示您请求是因高 CPU 使用量还是高索引并发性被拒绝。
  • **ThreadpoolWriteRejected:**写入线程池中被拒绝任务的数量。
    **注意:**在 OpenSearch Service 版本 7.9 中,默认写入队列大小从 200 增加到 10,000。因此,此指标不再是衡量 OpenSearch Service 拒绝的唯一指标。使用 CoordinatingWriteRejectedPrimaryWriteRejectedReplicaWriteRejected 指标监控版本 7.9 及更高版本中的拒绝。
  • **ThreadpoolSearchQueue:**搜索线程池中排队任务的数量。如果队列大小一直很高,则可以考虑扩展您的集群。最大搜索队列大小为 1,000。
  • **ThreadpoolSearchRejected:**搜索线程池中被拒绝任务的数量。如果这个数字持续增长,那么可以考虑扩展您的集群。
  • **JVMMemoryPressure:**JVM 内存压力指定了集群节点中 Java 堆的百分比。如果 JVM 内存压力达到 75%,则 OpenSearch Service 会启动并发标记清除(CMS)垃圾收集器。垃圾回收是一个 CPU 密集型过程。如果 JVM 内存压力以这个百分比保持几分钟,就可能会遇到集群性能问题。有关详细信息,请参阅如何解决 Amazon OpenSearch Service 集群上的高 JVM 内存压力问题?

**注意:**列出的线程池指标可帮助您了解 IndexingRateSearchRate

有关使用 CloudWatch 监控您的 OpenSearch Service 集群的详细信息,请参阅实例指标

活动线程和队列线程

如果缺少 CPU 或请求并发性过高,则队列可能会很快填满,从而导致 HTTP 429 错误。要监控队列线程,请查看 CloudWatch 中的 ThreadpoolSearchQueueThreadpoolWriteQueue

要检查活动线程和队列线程是否存在任何搜索被拒绝情况,请使用以下命令:

GET /_cat/thread_pool/search?v&h=id,name,active,queue,rejected,completed

要检查活动线程和队列线程是否存在写入被拒绝情况,请将“搜索”替代为“写入”。输出中的 rejectedcompleted 值是累积节点计数器,启动新节点时会重置这两个计数器。有关详细信息,请参阅 Elasticsearch 网站上的 cat 线程池 API带有显式列的示例部分。

**注意:**每个节点上的批量队列可以容纳 50 到 200 个请求,具体取决于您使用的 Elasticsearch 版本。队列已满时,新请求将被拒绝。

搜索和写入被拒绝时出现的错误

搜索被拒绝

搜索被拒绝错误表示活动线程忙碌,并且队列任务已达到数量上限。因此,您的搜索请求可能会被拒绝。您可以配置 OpenSearch Service 日志,以便这些错误消息出现在您的搜索慢日志中。

**注意:**为避免额外开销,请将慢日志阈值设置为较大值。例如,如果您的大多数查询需要 11 秒,而您的阈值为“10”,则 OpenSearch Service 需要更多时间来写日志。您可以通过将慢日志阈值设置为 20 秒来避免这种开销。然后,仅记录一小部分较慢的查询(耗时超过 11 秒)。

将您的集群配置为将搜索慢日志推送到 CloudWatch 后,为慢日志生成设置特定阈值。您可以通过以下 HTTP POST 调用为慢日志生成设置特定阈值:

curl -XPUT http://<your domain’s endpoint>/index/_settings -d '{"index.search.slowlog.threshold.query.<level>":"10s"}'

写入被拒绝

写入被拒绝 429 错误消息表示存在批量队列错误。es_rejected_execution_exception[bulk] 表示您的队列已满,任何新请求都会被拒绝。当对集群的请求数量超过批量队列大小(threadpool.bulk.queue_size)时,就会出现这种批量队列错误。每个节点上的批量队列可以容纳 50 到 200 个请求,具体取决于您使用的 Elasticsearch 版本。

您可以配置 OpenSearch Service 日志,以便这些错误消息出现在您的索引慢日志中。

**注意:**为避免额外开销,请将慢日志阈值设置为较大值。例如,如果您的大多数查询需要 11 秒,而您的阈值为“10”,则 OpenSearch Service 将需要更多时间来写日志。您可以通过将慢日志阈值设置为 20 秒来避免这种开销。然后,仅记录一小部分较慢的查询(耗时超过 11 秒)。

将您的集群配置为将搜索慢日志推送到 CloudWatch 后,为慢日志生成设置特定阈值。要为慢日志生成设置特定阈值,请使用以下 HTTP POST 调用:

curl -XPUT http://<your domain’s endpoint>/index/_settings -d '{"index.indexing.slowlog.threshold.query.<level>":"10s"}'

写入被拒绝最佳实践

以下是一些缓解写入被拒绝的最佳实践:

  • 当文档的索引速度更快时,写入队列就不太可能达到容量上限。
  • 根据您的工作负载和所需性能调整批量大小。有关详细信息,请参阅 Elasticsearch 网站上的调整索引速度
  • 在应用程序逻辑中添加指数重试逻辑。指数重试逻辑可确保自动重试失败的请求。
    **注意:**如果您的集群持续遇到大量并发请求,则指数重试逻辑将无助于解决 429 错误。当流量突然或偶尔出现高峰时,请使用此最佳实践。
  • 如果您要从 Logstash 提取数据,请调整 Worker 计数和批量大小。最佳做法是将批量大小设置在 3~5 MB 之间。

有关索引性能调整的详细信息,请参阅如何提高 OpenSearch Service 集群的索引性能?

搜索被拒绝最佳实践

以下是缓解搜索被拒绝的一些最佳实践:

  • 切换到更大的实例类型。OpenSearch Service 严重依赖文件系统缓存来提高搜索结果获取速度。线程池中每个搜索请求节点线程的数量的计算方法为:int((# of available_processors * 3) / 2) + 1。切换到具有更多 vCPU 的实例,以获得更多线程来处理搜索请求。
  • 为给定索引或所有具有合理阈值的索引启用搜索慢日志。查看哪些查询需要更长的时间才能运行,并为您的查询实施搜索性能策略。有关详细信息,请参阅 Elasticsearch 网站上的 Elasticsearch 搜索故障排除(适用于初学者)高级调整: 查找和修复慢 Elasticsearch 查询
AWS 官方
AWS 官方已更新 1 年前