架构设计器_大厂案例:马蜂窝大交通业务监控报警系统架构设计与实现

部门的业务线越来越多,任何一个线上运行的应用,都可能因为各种各样的原因出现问题:比如业务层面,订单量比上周减少了,流量突然下降了;技术层面的问题,系统出现 ERROR ,接口响应变慢了。拿大交通业务来说,一个明显的特点是依赖很多供应商的服务,所以我们还需要关注调用供应商接口是否出现异常等等。

为了让大交通下的各业务线都能够通过报警尽早发现问题、解决问题,进而提升业务系统的服务质量,我们决定构建统一的监控报警系统。一方面在第一时间发现已经出现的系统异常,及时解决;另一方面尽早发现一些潜在的问题,比如某个系统目前来看没有影响业务逻辑的正常运转,但是一些操作耗时已经比较长等,这类问题如果不及时处理,将来就很可能影响业务的发展。

本文主要介绍马蜂窝大交通业务监控报警系统的定位、整体架构设计,以及我们在落地实践过程中的一些踩坑经验。

架构设计与实现

我们希望监控报警系统主要具备以下三个能力:

1. 常用组件自动报警:对于各业务系统常用的框架组件(如 RPC ,HTTP 等)创建默认报警规则,来方便框架层面的统一监控。

2. 业务自定义报警:业务指标由业务开发自定义埋点字段,来记录每个业务和系统模块的特殊运行状况。

3. 快速定位问题:发现问题并不是目的,解决才是关键。我们希望在完成报警消息发送后,可以让开发者一目了然地发现问题出现在什么地方,从而快速解决。

在这样的前提下,报警中心的整体架构图和关键流程如下图所示:

37066d1e892dd678b1e6d7bd84ad3e06.png

纵向来看,Kafka 左侧是报警中心,右侧是业务系统。

报警中心的架构共分为三层,最上层是 WEB 后台管理页面,主要完成报警规则的维护和报警记录的查询;中间层是报警中心的核心;最下面一层是数据层。业务系统通过一个叫做 mes-client-starter 的 jar 包完成报警中心的接入。

我们可以将报警中心的工作划分为五个模块:

05653f72bf15d299dce95c90ed2ebfc1.png

1. 数据收集

我们采用指标采集上报的方式来发现系统问题,就是将系统运行过程中我们关注的一些指标进行记录和上传。上传的方式可以是日志、 UDP 等等。

首先数据收集模块我们没有重复造轮子,可是直接基于 MES (马蜂窝内部的大数据分析工具)来实现,主要考虑下面几个方面的原因:一来数据分析和报警在数据来源上是相似的;二来可以节省很多开发成本;同时也方便报警的接入。

那具体应该采集哪些指标呢?以大交通业务场景下用户的一次下单请求为例,整个链路可能包括 HTTP 请求、Dubbo 调用、SQL 操作,中间可能还包括校验、转换、赋值等环节。一整套调用下来,会涉及到很多类和方法,我们不可能对每个类、每个方法调用都做采集,既耗时也没有意义。

为了以最小的成本来尽可能多地发现问题,我们选取了一些系统常用的框架组件自动打点,比如 HTTP、SQL、我们使用的 RPC 框架 Dubbo ,实现框架层面的统一监控。

而对于业务来说,每个业务系统关注的指标都不一样。对于不同业务开发人员需要关注的不同指标,比如支付成功订单数量等,开发人员可以通过系统提供的 API 进行手动埋点,自己定义不同业务和系统模块需要关注的指标。

2. 数据存储

对于采集上来的动态指标数据,我们选择使用 Elasticsearch 来存储,主要基于两点原因:

一是动态字段存储。每个业务系统关注的指标可能都不一样,每个中间件的关注点也不同,所以埋哪些字段、每个字段的类型都无法预知,这就需要一个可以动态添加字段的数据库来存储埋点。Elasticsearch 不需要预先定义字段和类型,埋点数据插入的时候可以自动添加。

二是能够经得起海量数据的考验。每个用户请求进过每个监控组件都会产生多条埋点,这个数据量是非常庞大的。Elasticsearch 可以支持大数据量的存储,具有良好的水平扩展性。

此外,Elasticsearch 还支持聚合计算,方便快速执行 count , sum , avg 等任务。

3. 报警规则

有了埋点数据,下一步就需要定义一套报警规则,把我们关注的问题量化为具体的数据来进行检查,验证是否超出了预设的阈值。这是整个报警中心最复杂的问题,也最为核心。

之前的整体架构图中,最核心的部分就是「规则执行引擎」,它通过执行定时任务来驱动系统的运行。首先,执行引擎会去查询所有生效的规则,然后根据规则的描述到 Elasticsearch 中进行过滤和聚合计算,最后将上一步聚合计算得结果跟规则中预先设定的阈值做比较,如果满足条件则发送报警消息。

这个过程涉及到了几个关键的技术点:

1). 定时任务

为了保证系统的可用性,避免由于单点故障导致整个监控报警系统失效,我们以「分钟」为周期,设置每一分钟执行一次报警规则。这里用的是 Elastic Job 来进行分布式任务调度,方便操控任务的启动和停止。

2). 「三段式」报警规则

我们将报警规则的实现定义为「过滤、聚合、比较」这三个阶段。举例来说,假设这是一个服务 A 的 ERROR 埋点日志:

app_name=B is_error=false warn_msg=aa datetime=2019-04-01 11:12:00app_name=A is_error=false datetime=2019-04-02 12:12:00app_name=A is_error=true error_msg=bb datetime=2019-04-02 15:12:00app_name=A is_error=true error_msg=bb datetime=2019-04-02 16:12:09

报警规则定义如下:

  • 过滤:通过若干个条件限制来圈定一个数据集。对于上面的问题,过滤条件可能是:app_name=A , is_error=true , datetime between '2019-14-02 16:12:00' and '2019-14-02 16:13:00'.
  • 聚合:通过 count,avg,sum,max 等预先定义的聚合类型对上一步的数据集进行计算,得到一个唯一的数值。对于上面的问题,我们选择 count 来计算出现 ERROR 的次数。
  • 比较:把上一步得到的结果与设定的阈值比较。

对于一些复杂条件的报警,比如我们上边提到的失败率和流量波动,应该如何实现呢?

假设有这样一个问题:如果调用的 A 服务失败率超过 80%,并且总请求量大于 100,发送报警通知。

我们知道,失败率其实就是失败的数量除以总数量,而失败的数量和总数量可以通过前面提到的「过滤+聚合」的方式得到,那么其实这个问题就可以通过如下的公式描述出来:

failedCount/totalCount>0.8&&totalCount>100

然后我们使用表达式引擎 fast-el 对上面的表达式进行计算,得到的结果与设定的阈值比较即可。

3) 自动创建默认报警规则

对于常用的 Dubbo, HTTP 等,由于涉及的类和方法比较多,开发人员可以通过后台管理界面维护报警规则,报警规则会存储到 MySQL 数据库中,同时在 Redis 中缓存。

以 Dubbo 为例,首先通过 Dubbo 的 ApplicationModel 获取所有的 provider 和 consumer,将这些类和方法的信息与规则模板结合(规则模板可以理解为剔除掉具体类和方法信息的规则),创建出针对某个类下某个方法的规则。

比如:A 服务对外提供的 dubbo 接口/ order / getOrderById 每分钟平均响应时间超过 1 秒则报警;B 服务调用的 dubbo 接口/ train / grabTicket /每分钟范围 false 状态个数超过 10 个则报警等等。

4. 报警行为

目前在报警规则触发后主要采用两种方式来发生报警行为:

  • 邮件报警:通过对每一类报警制定不同的负责人,使相关人员第一时间获悉系统异常。
  • 微信报警:作为邮件报警的补充。

之后我们会持续完善报警行为的策略,比如针对不同等级的问题采用不同的报警方式,使开发人员既可以迅速发现报警的问题,又不过多牵扯在新功能研发上的精力。

5. 辅助定位

为了能够快速帮助开发人员定位具问题,我们设计了命中抽样的功能:

首先,我把命中规则的 tracer_id 提取出来,提供一个链接可以直接跳转到 kibana 查看相关日志,实现链路的还原。

其次,开发人员也可以自己设置他要关注的字段,然后我会把这个字段对应的值也抽取出来,问题出在哪里就可以一目了然地看到。

技术实现上,定义一个命中抽样的字段,这个字段里面允许用户输入一个或者多个 dollar 大括号。比如我们可能关注某个供应商的接口运行情况,则命中抽样的字段可能为下图中上半部分。在需要发送报警消息的时候,提取出里面的字段,到 ES 中查询对应的值,用 freemarker 来完成替换,最终发送给开发人员的消息是如下所示,开发人员可以快速知道系统哪里出了问题。

eec207f5037c0c98fcf0624d97ee4e84.png

踩坑经验和演进方向

大交通业务监控报警系统的搭建是一个从 0 到 1 的过程,在整过开发过程中,我们遇到了很多问题,比如:内存瞬间被打满、ES 越来越慢、频繁 Full GC ,下面具体讲一下针对以上几点我们的优化经验。

踩过的坑

1. 内存瞬间被打满

任何一个系统,都有它能承受的极限,所以都需要这么一座大坝,在洪水来的时候能够拦截下来。

5d3773a8ed9251eba9b23b34b32b2660.png

报警中心也一样,报警中心对外面临最大的瓶颈点在接收 Kafka 中传过来的 MES 埋点日志。上线初期出现过一次由于业务系统异常导致瞬间大量埋点日志打到报警中心,导致系统内存打满的问题。

解决办法是评估每个节点的最大承受能力,做好系统保护。针对这个问题,我们采取的是限流的方式,由于 Kafka 消费消息使用的是拉取的模式,所以只需要控制好拉取的速率即可,比如使用 Guava 的 RateLimiter :

messageHandler = (message) -> { RateLimiter messageRateLimiter = RateLimiter.create(20000); final double acquireTime = messageRateLimiter.acquire(); /** save.. */}

2. ES 越来越慢

由于 MES 日志量比较大,也有冷热之分,为了在保证性能的同时方便数据迁移,我们按照应用 + 月份的粒度创建 ES 索引,如下所示:

eab9ffc4353953b1be4c2b0304fbf858.png

3. 频繁 Full GC

我们使用 Logback 作为日志框架,为了能够搜集到 ERROR 和 WARN 日志,自定义了一个 Appender。如果想搜集 Spring 容器启动之前(此时 TalarmLogbackAppender 还未初始化)的日志, Logback 的一个扩展 jar 包中的 DelegatingLogbackAppender 提供了一种缓存的方式,内存泄漏就出在这个缓存的地方。

正常情况系统启动起来之后,ApplicationContextHolder 中的 Spring 上下文不为空,会自动从缓存里面把日志取出来。但是如果因为种种原因没有初始化这个类 ApplicationContextHolder,日志会在缓存中越积越多,最终导致频繁的 Full GC。

解决办法:

1. 保证 ApplicationContextHolder 的初始化

2. DelegatingLogbackAppender 有三种模式:OFF SOFT ON ,如果需要打开,尽量使用 SOFT模式,这时候缓存被存储在一个由 SoftReference 包装的列表中,在系统内存不足的时候,可以被垃圾回收器回收掉。

3b3cbd014934f43f56424fe486225645.png

近期规划

目前这个系统还有一些不完善的地方,也是未来的一些规划:

  • 更易用:提供更多的使用帮助提示,帮助开发人员快速熟悉系统。
  • 更多报警维度:目前支持 HTTP,SQL, Dubbo 组件的自动报警,后续会陆续支持 MQ,Redis 定时任务等等。
  • 图形化展示:将埋点数据通过图形的方式展示出来,可以更直观地展示出系统的运行情况,同时也有助于开发人员对于系统阈值的设置。

小结

总结起来,大交通业务监控报警系统架构有以下几个特点:

  • 支持灵活的报警规则配置,丰富的筛选逻辑
  • 自动添加常用组件的报警,Dubbo、HTTP 自动接入报警
  • 接入简单,接入 MES 的系统都可以快速接入使用

线上生产运维主要做 3 件事:发现问题、定位问题、解决问题。发现问题,就是在系统出现异常的时候尽快通知系统负责人。定位问题和解决问题,就是能够为开发人员提供快速修复系统的必要信息,越精确越好。

报警系统的定位应该是线上问题解决链条中的第一步和入口手段。将其通过核心线索数据与数据回溯系统( tracer 链路等),部署发布系统等进行有机的串联,可以极大提升线上问题解决的效率,更好地为生产保驾护航。

不管做什么,我们最终的目标只有一个,就是提高服务的质量。

本文作者:宋考俊,马蜂窝大交通平台高级研发工程师。

本文转载于博客园,原文:https://www.cnblogs.com/mfwtech/p/10955872.html

(题图来源:网络)

8d5ed2b226695b349b4d830e73e55b2a.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/354331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用mpvue开发小程序

一、安装node.js 1、在官网中安装nodejs最新版本。地址:https://nodejs.org/en/download/,根据自己环境,进行下载安装。 2、安装完成后,进行nodejs版本及npm版本查看。 打开cmd命令行,输入 node -v 和 npm -v&#…

iOS----------UITextField实现过滤选中状态拼音

2018年上班的第二天,就这样背了一个大锅。我们项目中有一个搜索功能,在这一期的版本中,为了增强优化,去除了过滤空格的请求,这样或许能增加很好的用户体验,恰恰相反,偷鸡不成蚀把米。没想到苹果…

ai电磁组属于什么组_RPA+AI 创新案例挑战赛 2020 【专业组】amp;【校园组】优胜名单来也!...

大赛介绍本次大赛由 RPA 产业推进方阵为指导单位,来也科技为主办单位,面向所有来也科技合作伙伴及深圳地区大学生公开报名征集【专业组】&【校园组】参赛案例。RPA 产业推进方阵是在中国人工智能产业发展联盟指导下,由中国信息通信研究院…

具有Spring Boot和Yeoman的单页Angularjs应用程序

我非常感谢yeoman之类的工具,它们提供了一种非常快速的方法来将不同的javascript库组合在一起成为一个一致的应用程序。 Yeoman提供了UI层,如果您需要开发服务层和静态资产的Web层,则打包的一种好方法是使用Spring Boot 。 我知道有像JHipste…

vue项目封装axios请求

目录: 一,src/utils/request.js import axios from axios import { getToken } from /utils/auth import store from /storeconst service axios.create({baseURL: process.env.VUE_APP_BASE_API,withCredentials: true,timeout: 5000,// headers:{ …

自定义函数_python3基础07函数(自定义)

"pythonic生物人"的第43篇分享。详细介绍python中:自定义函数的构建;参数传递;模块中调用函数。目录0、楔子1、自定义函数格式2、编写函数说明文档3、函数参数函数形参和实参区别位置实参关键字实参默认实参让实参可选传递任意数量…

curl -windows下接口通讯

1,下载curl -----url命令传输工具2,配置curl环境变量3,在cmd环境中使用举例:curl -G http://xxxxxxx.com?参数使用:curl -G "www.baidu.com" 最原始的批量通讯返回可以将通讯命令保存为bat格式文…

sqlserver 存储过程 C#调用 实现从数据库Get数据

在最近的项目中我想建立一个EFDBfirst的模型但是失败了,生成的edmx中没有实体类和表结构,到处需求解决方案,未果。 问题请见:https://q.cnblogs.com/q/102743/ 后来使用本文写的这个方法 /// 1.在sqlserver中建立存储过程 在一个d…

修改jwt过期时间_PostgreSQL如何修改用户过期时间

生产环境中,有时候需要设置一个有时效的临时帐户,供一段时间内,某些需要的使用,过期帐号自己禁用,但有时候因为更多的需求,需要对这种有过期时间的帐号进行延长过期时间,这时候就需要使用命令行…

怎么自定义字体_自定义字体@fontface的常见应用

前言font-face允许网页自带字体,从而消除对用户电脑字体的依赖。基本用法如下:font-face { font-family: "family-name"; /* 字体名 */ src: url("family-name.eot"); /* IE9 */ src: url("family-name.eot?#iefix&q…

vue项目中使用mock(一)

一,安装 npm install mockjs --save-dev npm install json5 --save-dev npm install axios --save 二,每个文件内容 目录: 流程: Home.vue执行getUserInforList() 调用main.js中全局变量$api 调用/utils/api下的getUserInfo…

使用RxJava和Completable并行执行阻止任务

借助RxJava 1.1.1中引入的Completable抽象,如何并行执行阻止“仅副作用”(也称为void)任务的并行执行变得更加容易。 “ 正如您可能已经注意到,阅读我的博客时,我主要专注于软件Craft.io和自动代码测试。 但是&#x…

javafx 使用_使用JavaFX AnimationTimer

javafx 使用回想一下,给AnimationTimer起个名字可能不是一个好主意,因为它不仅可以用于动画,还可以用于测量fps速率,碰撞检测,模拟步骤,游戏主循环等。实际上,我大部分时间都在看AnimationTimer…

.Net Core 简洁架构事件(这个不完整,待仔细补充)

.Net Core的架构 - 根据微软官方文档 微软给出了.Net Core的架构方法,无论是在web,azure,uwp等等 微软的github地址:https://github.com/dotnet-architecture/eShopOnWeb 转载于:https://www.cnblogs.com/bijinshan/p/8250512.htm…

原理图中如何连线_Altium Designer10绘制原理图

在进行原理图绘制之前,应先将原理图库与PCB库相关联,在原理图绘制完成后,在生成PCB图,如何将原理图库与PCB库相关联?先进入原理图库,如下图所示。双击元器件文件,进行元器件配置。 元器件配置界面如下图所示。选择Add...进行添加对应的PCB库。 选择浏览进行查找相关PCB库…

NetBeans Java EE技巧3:数据库中的RESTful Web服务

许多现代的Web应用程序正朝着使用HTTP使用无状态通信的方向发展。 REST(代表性状态转移)体系结构样式通常用于设计网络应用程序,而使用Java EE 7,很容易开发用于数据库通信的RESTful后端。 使用简单的POJO(普通的Java旧…

参数php_PHP多参数方法的重构

php中文网最新课程每日17点准时技术干货分享假设我们要完成一个保存文章的功能&#xff0c;如果采用函数编程的方式&#xff0c;大概会是下面这个样子&#xff1a;<?php function saveArticle($title, $content, $categoryId){ // ...}?>每个参数代表一个属性&#…

k8s集群部署成功后某个节点突然出现notready状态的问题原因分析和解决办法

文章目录 1、问题描述2、查看node03的日志3、错误原因分析4、解决办法 1、问题描述 k8s集群配置为 一主三个节点&#xff1b;刚开始运行一直正常&#xff1b;某天突然node03主机状态变为notready&#xff0c;问题如下&#xff1a; 在master节点使用&#xff1a; #master节点…

kickstart_具有Java Kickstart的MongoDB

kickstartNoSQL数据库由于其可伸缩性而变得越来越流行。 适当使用时 NoSQL数据库可以提供真正的好处。 MongoDB是使用C 编写的高度可扩展的开源NoSQL数据库。 1.安装MongoDB 您可以按照所使用的操作系统&#xff0c;按照MongoDB官方网站上的说明安装MongoDB&#xff0c;而不会…

DataGuard之DG环境搭建

DG 环境搭建 1.设置归档模式 DG环境的搭建必须要把数据库启动到归档模式&#xff0c;并且为了避免开发人员使用nologging语句&#xff0c;我们还要把数据库设置为force logging。 查看数据库是否运行在归档模式&#xff1a; #su - oracle $sqlplus / as sysdba SQL>archive…