前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >WebMonitor采集端优化之路

WebMonitor采集端优化之路

作者头像
WecTeam
发布2020-07-27 09:36:40
6260
发布2020-07-27 09:36:40
举报
文章被收录于专栏:WecTeamWecTeam

一、前言

WebMonitor 作为一个前端监控系统,服务于众多业务的上报需求,包括:微信小程序H5京喜 App部分 PC 页。作为京喜业务流量最大的服务,WebMonitor 过去在超大流量下的表现并不理想,例如 2020 年年初疫情抢口罩时,几乎是每天的抢购高峰就会有分钟级的服务不可用,后果就是红绿灯面板的一半红灯都亮起来了?。因此 2020 年彻底优化了 WebMonitor 的采集端性能,此文借以回顾过去两年内对于 WebMonitor 采集端的优化升级之路

二、WebMonitor基本架构

WebMonitor基本架构

上图是目前的 WebMonitor 整体架构,称之为WebMonitor Stack。整体架构是三层,分别是采集端管理端数据端。三层结构通过 WebMonitor 的数据流结合在一起,下面简单介绍以下各层的主要职责:

采集端

采集端面向的是 Nginx,即直面用户设备上的前端上报请求,采集端的整体架构是两级 Flume Agent 组成的。

  • 第一级 Flume Agent,用于解析前端上报的请求,根据 biz 类型的上报和 badJs 类型上报的接口协议,将请求解析。根据解析后的数据上报 Athena 和 UMP。然后将数据传递至下游 Flume Agent。
  • 第二级 Flume Agent,用于将数据持久化,目前数据持久化的通道有两个,HDFS 和 Kafka(MQ),一方面可以通过 Impala+HDFS 的形式查询持久化的数据。另一方面也可以通过 Kafka 落 ElasticSearch(后简称 ES ),通过搜索引擎查询数据。

管理端

相信很多前端同学都体验过 WebMonitor 的 Old School 式的管理端。管理端主要的功能包括以下内容:

  1. 维护 biz 类型上报和 badJs 类型上报的埋点信息
  2. 提供查询业务错误和 badJs 错误的界面
  3. 提供配置内容接口,方便采集端定时更新上报配置

数据端

数据端的数据承载形式在过去的若干年进行了多次优化升级,毕竟在现有的上报量级下(日均4TB+),当传统的DB已经不能承载这种数据量级时,需要提供一些高效的查询手段。

  • 黑铁时代。上报数据在本地磁盘进行 休克式 采样,仅保留不足1%的原始日志,通过 rsync 汇总至一台机器进行查询。无索引、无并行。
  • 白银时代。上报数据通过采集端落入分布式文件系统 HDFS ,使用大数据查询引擎 Impala 进行并行查找。无索引、并行。
  • 黄金时代。上报数据通过采集端落入 Kafka,通过一个 Agent 将数据写入ES,利用搜索引擎进行查找。有索引、并行。

数据端的优化往往最能改变用户的体验感受,查询的性能终于有着火箭般的提升:

  • 黑铁时代:1-2 min
  • 白银时代:30s
  • 黄金时代:不足5s

三、WebMonitor采集端架构的整体优化

WebMonitor采集端旧流程

上图是 WebMonitor 采集端旧流程的示意图。采集端的核心流程包括:

  • 接收数据
  • 解析数据(计算)
  • 上报数据( IO )
  • 存储数据(高 IO)

这四个流程依次顺序的同步执行,完成数据存储后将 Response 返回给前端。但是由于上述流程中涉及到 IO 操作,在海量请求下,哪怕是仅仅保留较小比例的数据,IO压力也会非常大,会直接拖累采集端的性能。因此针对上述情况,WebMonitor 采集端做了一些调整,结果如下图所示:

WebMonitor采集端新流程

新的流程通过以下手段规避了旧流程的问题:

  • 在同步处理流程中丢弃高 IO 负载的操作,例如数据落地。保证主流程中尽可能少的IO操作,避免IO操作降低接口响应的整体性能。
  • 通过内存Event Bus Channel解耦监控上报和数据落地,使接受请求、上报监控和数据落地三个操作完全异步化
  • 数据落地使用分布式文件系统HDFS,解决了单一机器存储不足需要定时 rsync 到管理端机器的问题。

通过上述的优化过程,大量减轻了采集端接口的 IO 压力,使得接口性能有了显著的提升,如下表所示。

流程类型

Avg

TP99

MAX

旧流程

2.1ms

300ms

2000ms

新流程

0.7ms

200ms

2000ms

四、监控上报的优化

量变引发质变,谁能想到简单的监控上报也会成为业务的性能瓶颈?

在海量上报请求(日常 300W QPM)的前提下,UMP上报存在一些问题

  • UMP 对于标记为失败的方法监控,不做聚合操作。
  • UMP 对于相同 key 的业务上报,不做聚合操作。

由于存在上述的问题,那么过去直接将上报能力放入采集端接口处可能引发性能问题。因此我们曾经进行了一次失败的尝试,其修改如下图所示:

WebMonitor上报异步化流程

我将监控上报的能力从采集端的两级 Flume Agent 中完全拆解,通过消费下游的 Kafka 消息,解析后进行上报,这个其实是很“很常规”的异步操作,看似完美的解决了上报的性能问题,但是完全异步化又会引入新的问题?

  • 中间链条过长,可用率受到中间节点的影响
  • 由于 WebMonitor 的上报请求过大,需要单独维护一组机器进行上报,资源利用率较低

曾经出现过一次故障,因为 Kafka 的部分节点故障,导致 Kafka 短时间内不可用,然后红绿灯面板的一半红灯就亮起来了......

痛定思痛,我将采集端+上报的结构再次进行了调整,结构如下图所示:

WebMonitor上报优化流程

同样是异步上报,和之前的架构相比,我做了以下调整:

  • 将监控上报通过 Sink 的方式加在采集端的 Channel 后面,在采集端的进程内完成上报。
  • 业务监控、方法监控,成功的、失败的上报通通聚合,避免单条上报引发的IO性能瓶颈。

前面介绍WebMonitor采集端的架构调整,接下来我们讲讲枯燥的性能优化,虽然枯燥,但是优化完毕的效果着实喜人

五、Jetty Server 的线程优化

Jetty 是一个轻量的 Java Servlet 容器,当然在 WebMoniter 中更多的是作为一个普通的 Http Server。但是 Jetty 本身是非常“可配置的”,所以 Jetty 的各种默认配置并不适合所有的情况,特别是 WebMonitor 这样一个非典型的Http服务

Jetty结构简图

上图是 Jetty 的一个结构简图,也是比较典型的 Reactor 模式,较少的链接线程处理链接,大量的工作线程执行业务逻辑,例如调用服务、读写 DB 、存取缓存等等。其实大部分的 Http 服务的业务逻辑也是如此,但是 WebMonitor 作为一个非典型的Http服务,是一个零外部服务调用零外部 IO 调用的 Http 服务,所以默认的 Http 服务器的设置并不能完美的适配 WebMonitor 场景。

默认的 Jetty 线程配置:

代码语言:javascript
复制
public QueuedThreadPool () {
    this(200);
}

public QueuedThreadPool (@Name("maxThread") int maxThreads) {
    this(maxThreads, Math.min(8, maxThreads));
}

简单解读一下上述代码:

  • 默认的线程数是200
  • 最低线程数不能少于8

其实对于大部分“典型”的 Http 服务而言,确实是比较合理的,毕竟动辄调用一个外部服务10+ms,写一次 DB 10+ms,那么大量的线程能够明显改善性能问题。但是 WebMonitor 它不是个“典型的” Http 服务呀?

默认线程模型的不足:

  • 更适合数据密集型的应用。
  • 过多的 Worker 线程在高负载的情况下,大幅度抵消多线程的性能“红利”,甚至会影响 Connect 线程。

针对 WebMonitor 的业务特点,我做了一些关于线程的调整:

  • Worker 线程数调整至 32 甚至更低(8 Core的机器),这是避免在超高的请求下(例如大促零点),即使所有 Worker 线程都满负荷,也不能影响前端的 Nginx 。做一个好服务的必备条件是:既要保护好自己的服务不被打垮,也要保护好别人的服务不受影响
  • connect 线程可以通过线程池的方式进行连接,避免单一连接线程有问题引发的 HttpServer 整体的低响应。

六、Jetty Server 的 Timeout 优化

Jetty请求响应示意图

Timeout 是一个非常关键设置,还是那句话“量变引发质变”,当请求量过大时,连 TCP 的连接可能都值得优化。设置 Timeout 是一个妥协的过程,没有完全合适的设置,只有妥协上下游的设置。我在设置 Timeout 的过程中走了很多弯路:

No Timeout

  • ?:不需要和前端 Nginx 维持长连接,内存负载压力小。
  • ?:需要额外的资源不断的创建连接、关闭连接。(TCP的三次握手和四次挥手,一个都不能少!)

Short Timeout(100ms)

  • ?:自身服务异常时不会影响前端 Nginx。(就算自己爆炸也要保护兄弟)
  • ?:Nginx 容易使用已经被服务端关闭的连接,造成出错。
  • ?:连接较少可以复用,增加创建连接的成本。

Long Timeout(8000ms = Nginx KeepLive Timeout)

  • ?:连接基本可以复用,不论是 Nginx 还是服务端都能节省创建连接的开销。
  • ?:服务异常时 Nginx 性能可能受到影响。因为这条连接在 Timeout 时间内都是可用的,但是服务异常时,Nginx 就不得不创建更多的连接,而创建的连接可能响应依然很慢,所以整体降低 Nginx 的性能。
  • ?:连接过大时,由于 TCP 的 TIME_WAIT 阶段需要一定时间,可能引发 connect timeout 的问题,因为没有 socket 可以使用了。

前面提到,Timeout 的设置是一个妥协的过程,上面三种 Timeout 的设置都是有优势、有劣势,我也进行了多次的尝试,最终,选择了 Long Timeout 的方案,原因是当自身服务的可用性极高时,Long Timeout 更具有性能的优势。

七、意料之外的优化!

意料之外的优化源自于下面一行代码:

代码语言:javascript
复制
int cores = Runtime.getRuntime().availableProcessors();

上面这行代码是 Java 多线程应用中最普遍使用的一句,其作用是获取系统可用的处理器核心数,因为通常来说不论是数据密集型任务还是计算密集型任务,其线程数的设置都需要充分考虑主机的理论最佳并行度,但是成也萧何、败也萧何,这条关键的语句居然在 Docker 容器使用中翻车了?。

JDK1.7

JDK1.8.0_91

JDK1.8.0_201

上面三幅图分别代表了在 Docker 容器中,使用 JDK1.7,JDK1.8.0_91 和 JDK1.8.0_201 三个 JDK 的版本,执行上述语句的结果。该容器是 8 核心 16G 内存,可以看到 JDK1.7 和 JDK1.8.0_91居然识别的是容器所在宿主机的核心数,这就会对各种框架对于最佳并行度的设置带来非常大的影响。

针对计算密集型应用:

  • 最佳线程数应等于可用 CPU 的核数。
  • 过多的线程会引发 CPU 的上下文切换,导致性能下降。
  • JDK1.8.0_190 版本优化了 Docker 容器中 CPU 可用资源的识别。
  • 不论是 JDK 内部,还是应用的第三方库,都会大量使用上述语句得到预期的最佳并发度,因此如果该语句不能返回真实的数据,影响是非常巨大的!

至此,WebMonitor 的各项优化,从架构到性能的各项优化措施已经介绍完了,最后看看优化完成以后的性能指标

流程类型

单机QPM

Avg

TP99

MAX

旧流程

30k

2.1ms

300ms

2000ms

新流程

41k

0.7ms

200ms

2000ms

性能优化后

81k

0.8ms

3ms

25ms

八、一个 Java 后端程序员的经验之谈~

  • 80%的性能问题与 GC 有关。但是 GC 往往只是表象,产生不合理 GC 的原因才是问题的根本原因。
  • 线程资源要合理利用,过多的线程非但不能带来预期的性能提升,反而会拖你的后腿。
  • 永远要根据自己应用的实际情况来分析,Google 出来的答案未必适合你。
  • 定位问题善用 Arthas (一个开源的性能分析工具),各种硬件资源监控,找到不合理的地方,也许就能找到问题的突破口。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 WecTeam 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、前言
  • 二、WebMonitor基本架构
    • 采集端
      • 管理端
        • 数据端
        • 三、WebMonitor采集端架构的整体优化
        • 四、监控上报的优化
          • 前面介绍WebMonitor采集端的架构调整,接下来我们讲讲枯燥的性能优化,虽然枯燥,但是优化完毕的效果着实喜人
          • 五、Jetty Server 的线程优化
          • 六、Jetty Server 的 Timeout 优化
            • No Timeout
              • Short Timeout(100ms)
                • Long Timeout(8000ms = Nginx KeepLive Timeout)
                • 七、意料之外的优化!
                  • 至此,WebMonitor 的各项优化,从架构到性能的各项优化措施已经介绍完了,最后看看优化完成以后的性能指标
                  • 八、一个 Java 后端程序员的经验之谈~
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档