写点什么

【架构设计】【问题分析】记一次调用内部 es 服务超时问题

作者:如果晴天
  • 2023-06-09
    江苏
  • 本文字数:2747 字

    阅读完需:约 9 分钟

业务背景

我们有一个日志服务需要调用数据中台的 es 服务去写入日志数据(这里的技术选型有待商榷,不过作者无力逆转)。当前情况是 mongo 和 es 服务调用的双写,通过开关配置


当前架构

当前是批量写,在内存队列中累积,每个日志类型一个对象,队列大小 1000。两秒一个调度任务去双写,批量调用 es 服务。mongo 则是单点写,与 http 请求绑定的。


问题分析

6.2 上班,同事发现日志服务卡顿,请求响应慢。就让运维重启了容器(没有保留现场,只保留了容器日志)。之后问题依然,于是同事开始看日志,发现 es 服务不可用,于是去找数据中台的人 battle,然后吵起来了。。作者这时候被拉来救火。

因为只有日志了,那就只能通过日志+apm 来切入。大概有如下几种

分词过大

  • 由于数据中台同事第一时间反馈的问题是我们的报文过大,把他们的服务打挂了(这里无力吐槽,做服务提供的,怪调用者把服务调用挂了,怎么说的出口的,脸不会红吗,怎么做的稳定性治理?)。然后甩给我们一个他们日志的报错。又说是我们的报文过大,最大一个报文接近两百兆,但是同事说之所以我们采用这个批量的架构方案,都是数据中台的建议(这里作者持中立意见,都有问题,应该做好自我保护)。

[Elasticsearch exception [type=illegal_argument_exception, reason=Document contains at least one immense term in field="content" (whose UTF8 encoding is longer than the max length 32766), all of which were skipped.  Please correct the analyzer to not produce such terms.  The prefix of the first immense term is: '[10, 91, 73, 78, 70, 79, 93, 100, 101, 116, 97, 105, 108, 75, 101, 121, 58, 32, 68, 49, 89, 55, 50, 51, 48, 52, 49, 53, 48, 49]...', original message: bytes can be at most 32766 in length; got 2338851]], data=null
复制代码
  • 对应的优化方案的话,应该就是对应配置这个 ignore_above 字段,不过会截断日志,当前未使用。

网络通信失败


  • 500 Internal Server Error


  • Read timed out

内存溢出

  • 写入内存队列的时候

  • 批量写的时候



架构设计


方案一:基于 http 做治理

限制单次请求日志数目

  • 由于数据中台侧服务,兼容大报文,所以路由并没有限制报文大小

  • 数据中台会做自我保护,所以需要尽量控制在数据中台允许范围内,否则会返回错误

  • 目前的方案是 2 秒一次批量插入,所以可能会导致大报文。而且周期长很容易 oom,目前观测日志是有发生的(建议修改)

  • 静态指标:

  • 数据中台接口服务限制的单次报文大小

  • 日志平均大小,建议取 80%

  • 动态指标:

  • 数据中台接口服务限制的单次报文大小

  • 取出队列中所有日志,判断是否超过数据中台单次报文限制,不超过直接发送,超过则二分。剩余的放下次调度或者 mq

  • 更好的方案是重写内存队列,入队总和超过单次报文限制,就串行发送,串行带来的并发压力,交给熔断去处理

  • 非批次:直接采用单条发送

  • 考虑直接自旋处理

  • 同时不同类型的日志也建议采用线程隔离

限制单条日志大小

  • 同样因为数据中台限制了报文大小,所以自然单条报文也受限于此

  • 如果单条超限:见失败策略-failfast

限制并发数目

  • 数据中台给出并发支持的区间

  • 这里的失败策略:

  • 时间窗口内超限则熔断,即引入限流。熔断后走失败策略

  • 超过并发的剩余消息,延迟到下次调度任务写。指标判断也需要依赖熔断器,可以熔断后延迟写

  • 问题在于,目前是批量写,用处有限

方案二:mq 自消费

方案三:兼并方案

  • 维持现有架构

  • 减少调度周期,目的是软性的减少单次报文大小

  • 不同类型的日志也建议采用线程隔离

  • 考虑更细粒度的自旋

  • 接收到错误码,或者请求失败。就拆分单条入 mq 重试。这里相当于被动处理错误

  • 主动避免错误,可以在方案一中折中选择

  • 单条限制

  • 动态指标

  • 熔断最后兜底,失败扔 mq

  • 这里相当于是

  • 队列控制报文大小限制,同时也解决了 oom 的问题

  • 熔断控制并发

  • mq 解决内存重试压力


失败策略

  • failfast,快速失败。直接丢弃,或者加上告警

  • failback,故障恢复。

  • 内存重试:

  • 当前线程重试

  • 异步重试

  • 异步排队重试

  • db 记录失败记录,分布式调度任务处理重试

  • mq 重试,

  • 幂等问题,仅针对失败的 mq 消息

  • 单条日志入 mq,异步遍历+重试

  • 消费的时候,可以批量请求 es 服务

  • 降级:写 mongo,写文件,但是如果后续不再是双写,读的时候不太好操作

  • 以上策略再失败,则告警转人工


参考资料


https://blog.rabbitmq.com/

https://www.elastic.co/guide/en/elasticsearch/reference/7.15/mapping.html

https://www.rabbitmq.com/admin-guide.html


用户头像

如果晴天

关注

非淡泊无以明志,非宁静无以致远 2021-04-24 加入

朴实无华的开发者,热爱思考,喜欢探究原理,学以致用,追求极致。

评论

发布
暂无评论
【架构设计】【问题分析】记一次调用内部es服务超时问题_架构设计_如果晴天_InfoQ写作社区