本文翻译自, 侵删
Elasticsearch 在演进过程中, 考虑了集群及节点维度的稳定性. 例如, 向节点发送了太多请求或者请求体太大, 那么这些请求会被拒绝. 这个拒绝的过程是靠 Elastics 的各种熔断器实现的. 熔断器被放置在读写请求处理的关键路径中, 如当网络请求进入节点, 或执行聚合之前. 熔断器的核心思想, 是通过估算请求使用的内存是否会超过熔断器的限制而避免 OOM.Elasticsearch 设置有各种类型的熔断器, 如 in-flight request 熔断器, field ddata 熔断器等. 在这些子熔断器之上, Elasticsearch 还有一个父熔断器, 提供所有子熔断器的全局视图. 某些场景下, 请求没有超过任何子熔断器的限制, 但是预估的 jvm 使用量总和会超过父熔断器, 此时父就会生效.
跟踪每个对象的分配申请是不切实际的, 所以熔断器只能跟踪那些经常会出问题的内存使用. 在某些情况下, 可能无法精切的预估内存的使用情况. 这意味着熔断器只是一种尽力而为的机制, 由于为跟踪的内存申请相对的占比较大, 因此节点设置的堆越小, 越容易因未追踪的内存申请造成 OOM
创建一个更好的熔断器
如果我们想在熔断器中准确的知道节点正在使用多少内存, 应该怎么办? 如果解决了这个问题, 我们就可以根据系统在当前时刻的实际状态拒绝请求而不是基于熔断器对部分跟踪的内存分配值来预估. 我们在 Elastics7.0 版本中, 开发了新的实际内存熔断器完成这项工作. 实际内存熔断器是老版本父熔断器的替代实现, 它使用 JVM 中的接口来获取当前内存的使用量, 而不是仅考虑当前所有子熔断器所跟踪的内存. 虽然这比之前增加 JVM 的接口调用, 但是这个操作的耗时仍然是非常低的: 在微基准测试中, 我们观察到 400ns~900ns 的额外开销.
我们进行了各种实验来测试实际内存使用熔断器在不同条件下的有效性. 在一个场景中, 我们针对仅有 256MB 的 JVM 的节点运行了全文索引的基准测试. 早期版本的 Elasticsearch 无法维持次工作负载且熔断器未生效, 几乎立即 OOM. 但实际内存熔断器会拒绝请求, 保证 Elasticsearch 的正常使用. 这里需要注意, 熔断器起作用的时候, Elasticsearch 将返回错误响应, 业务需要保证客户端有适当的退避和重试机制. 当然, 只要您已经使用我们提供的官方 SDK, 如. NET,Ruby,Python 和 Java 客户端都已经实现了这些重试策略, 并提供了扩展来处理批量索引.
在另一个实验中, 我们执行了一个聚合, 该聚合在一个有 16GBJVM 的节点上故意产生了大量无用的桶. 同样, 早起版本的 Elasticsearch 会因为内存不足, 导致聚合执行了将近半小时直到错误的发生. 而在 7.0 版本的集群上, 节点提供了响应, 这取决于我们是否允许在稍微超过一分钟或大约二十分钟之后的部分结果. 通过多次实验, 我们将新的父熔断器的默认值设置为 JVM 的 95%. 这意味着 Elasticsearch 将允许使用高达 95%的 JVM, 直到实际内存熔断器熔断为止.
让我们看一个实际的例子, 在这个例子中批量发送的请求所使用的内存足够小, 可以通过所有的子熔断器, 但是会使实际内存熔断器熔断. 测试节点配置 128MB 的 JVM 运行, 父熔断器设置为 95%, 即 JVM 使用达到 117.5MB 会触发熔断. 如果此时继续发送请求, 则节点将返回 code 429, 如下:
- {
- 'error': {
- 'type': 'circuit_breaking_exception',
- 'reason': '[parent] Data too large, data for [<http_request>] would be [123848638/118.1mb], which is larger than the limit of [123273216/117.5mb], real usage: [120182112/114.6mb], new bytes reserved: [3666526/3.4mb]',
- 'bytes_wanted': 123848638,
- 'bytes_limit': 123273216,
- 'durability': 'TRANSIENT'
- },
- 'status': 429
- }
上面的错误提示表明熔断器熔断是一个瞬态的故障, 客户端可以在一段时间后重试. 然而, 在某些情况下, 如果熔断器设置的预留内存太小, 熔断可能是一个长时间的故障.
结语
虽然在某些情况下, 依然会存在 Elasticsearch 节点内存不足的情况, 但是新的实际内存熔断器使用基于实际测量的 JVM 使用量来执行熔断而不是像早期版本的只考虑跟踪的内存, 从而极大地提高了集群的稳定性. 在我们的实验中, Elasticsearch 现在可以维持相对于早期版本中远远无法实现的负载和更大的峰值. 要试用新的实际内存熔断器, 请下载最新的 7.0 版本, 并欢迎提出反馈.
来源: https://www.qcloud.com/developer/article/1416502