查看原文
其他

某小公司自动化智能监控平台的实践

作者:邵磊

来源:https://urlify.cn/7FVfMr


# 前言


首先介绍下背景:我们公司的产品,会直接部署在甲方,因为甲方比较多,且他们包含个性化需求较多,所以,每个甲方可以理解为我们产品的一条git分支。由于甲方的机器环境、网络环境各不相同,时常出现一些运行时的问题,于是,我设计了这套简易的智能监控系统,用来实时监测各个甲方接口情况。


# 适用范围


该套方案衍生的适用范围如下:


  1. docker下多容器运行项目,且暂不具备接口健康检测,该套方案可实施检测多个运行点状态情况。


  2. 公司同一套代码有多个运行环境,需要监控各个环境状态。


  3. 项目分支化后,部署在各个甲方(含个性化需求)------我们是这个。


# 效果图


鼠标移到某个接口,可看某节点产生的时间和当时接口的出参。[这里只显示变动节点]


其中200为出参json的一个状态码,我们项目普遍采用。如出参无法解析出状态码(比如服务器500错误),则不显示。


单击节点可以再次模拟该次节点,在浏览器中显出出参。


双击某个节点可以复制该节点出参。


# 主体分析


因为我们产品已经完成了前后端分离,所以,我们重点监控接口。


我的方案是:


  1. 每隔一定时间,比如3分钟,主动get(或者post)一下http接口(含用户信息),获得接口出参。

  2. 比对此次出参和前一次该接口出参是否相同,如果不通则标记下。

  3. 前端展示,每个接口对应每个项目所有变动的节点,并排列一张节点差异图。

  4. 这里,我们监控并不关心接口出参的内容,不对节点出参进行校验是否合法,因为我们面向所有接口。


# 对某个接口分析


这里因为每个公司的各个接口,入参、出参加密方式不通,而且身份认证也是不相同,我们会在独立写一个方法来实现加解密,所以,我只介绍未加密、已解密的参数;关于出参下文以{"code":"200","data":null,"message"::"success"}为例,身份认证我以简单token进行讲解本文。


某个获取新闻列表接口:

http://*.com/v1/news?token=2c789e34dc81d79feba6a005ad63902b

解密后的出参:

{"code":"200","data":[{"id":"1","title":"这是一条假新闻","url":"http://"},{"id":"2","title":"这是一条假新闻","url":"http://"}],"message"::"success"}

# 由接口可知


  1. GET 方法

  2. 入参token=2c789e34dc81d79feba6a005ad63902b

  3. 出参为{"code":"200","data":[],"message"::"success"}

  4. 其中*.com对于各个环境可能各不相同,比如容器下为10.0.0.1、10.0.0.2多个地址

  5. 其中token在各个甲方不相同。


# 添加新闻获取接口时:


相对url:

v1/news

请求类型:

get

body主体:

token={token}

corn表达式:

0 0/3 * * * ?(每隔3分钟执行一次)

# 添加A环境时:


host(必填)

http://a.com

参数(list)

token 2c789e34dc81d79feba6a005ad63902b

# 添加B环境时:


host(必填)

http://b.com

参数(list)

token 4297f44b13955235245b2497399d7a93

# 绑定接口和环境


  1. 一个接口可绑定多个环境

  2. 一个环境可绑定多个接口

  3. 数据库中只记录单向绑定,逻辑上是双向的。


比如在新闻获取接口绑定A、B环境,则每隔3分钟请求一轮接口。


A:


A环境host+相对url=

http://a.com/v1/news

请求类型:

get

body主体:

token=2c789e34dc81d79feba6a005ad63902b

B:


B环境host+相对url=

http://b.com/v1/news

请求类型:

get

body主体:

token=4297f44b13955235245b2497399d7a93

我们框架本身并不会关心有几个参数,只会遍历接口中带占位符的,把占位符中字段替换为环境里的参数list中变量。比如一个body为token=2c789e34dc81d79feba6a005ad63902b&type=1&mobile=2


则添加接口时,body填入:

token={token}&type={type}&mobile={mobile} 其中占位符可以随便起名字

配置环境时参数(list)如下:


kv
token2c789e34dc81d79feba6a005ad63902b
type1
mobile2


参数和占位符里的对应,和url其他变量不相关。


# 前端展示


横轴代表时间,每个变动的节点都是一次出参变更,节点包含,当时的入参,出参,单击左键可以再次模拟请求得出解密后的出参;双击可以复制出参。


对于不同的状态码,我们有不同的颜色,对应自己产品中错误码选择合适颜色即可,错误等级越高,颜色越显眼。


某个节点右键可以查看历史解决方案,以及新增新的解决方案,比如解决了这个错误代码为999,是重启服务解决的,则,输入重启服务,再点添加。下次其他人就可以看到这个问题怎么解决的,优先参照做。


# 不同的展现


包含两种不同的纬度,可以直接点击进入,查看A环境所有接口的监控情况,或者查看接口1所有环境的监控情况。


或者:


环境编辑界面:



任务编辑界面:



# 核心技术


既然是周期性监控,自然少不了使用cron表达式,我们又是java项目,所以采用了quartz框架。


quartz核心代码


public class QuartzSchedule {
private static SchedulerFactory sf = new StdSchedulerFactory(); private static Scheduler sched; final static String groupName = "task";

public static void init() throws IOException, SchedulerException { //查询所有需要执行的任务和项目列表 // 使用类加载器,加载mybatis的配置文件 InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml"); // 构造sqlSession工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(); MprojectTaskDao mprojectTaskDao = sqlSession.getMapper(MprojectTaskDao.class); sched = sf.getScheduler(); //只查询所有在m_project_include表里面的任务 List<MprojectTask> mprojectTaskList = mprojectTaskDao.findList(); for (MprojectTask mprojectTask : mprojectTaskList) { startJob(mprojectTask); } }
public static void stopTask(MprojectTask mprojectTask) { TriggerKey triggerKey = TriggerKey.triggerKey(mprojectTask.getId(), groupName); try { sched.pauseTrigger(triggerKey);// 停止触发器 sched.unscheduleJob(triggerKey);// 移除触发器 sched.deleteJob(JobKey.jobKey(mprojectTask.getId(), groupName));// 删除任务 } catch (Exception e) { e.printStackTrace(); } }
/* 停止和暂停均可使用 */ public static void startTask(MprojectTask mprojectTask) { //无论是否关闭,先关闭任务再开启。 stopTask(mprojectTask); startJob(mprojectTask); }

private static void startJob(MprojectTask mprojectTask) { try { JobDetail jobDetail = JobBuilder.newJob(TaskQuzrtzJob.class).withIdentity(mprojectTask.getId(), groupName).build(); // 触发器 TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger(); // 触发器名,触发器组 triggerBuilder.withIdentity(mprojectTask.getId(), groupName); triggerBuilder.startNow(); // 触发器时间设定 triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(mprojectTask.getCron())); // 创建Trigger对象 CronTrigger trigger = (CronTrigger) triggerBuilder.build(); // 调度容器设置JobDetail和Trigger sched.scheduleJob(jobDetail, trigger); // 启动 if (!sched.isShutdown()) { sched.start(); } } catch (Exception e) { e.printStackTrace(); } }}

job核心代码


public class TaskQuzrtzJob implements Job {
public TaskQuzrtzJob() throws IOException { }
//JobExecutionContext context传递参数值 public void execute(JobExecutionContext context) { //通过context得到该任务的所有环境 //遍历所有环境 //拼凑出请求地址 //使用封装的框架加密发送请求 //得出出参解密后的结果 //与上次进行比对,不相同则标记,入节点表 //入记录表 }}

数据量


因为我们这个监控每个接口(任务)有多个环境,假设100个接口100个环境,每3分钟监控一次,则每天记录量为


100 * 100 * 20 * 24=4800 000条记录,这个记录比较惊人,所以我们分了两张表,一张表只记录记录,另外一张表记录变更,展示节点的时候只查询变更表,可解决性能上的问题。


前端


前端采用css来画圆和颜色

round { border-radius: 50%; text-align: center; width: 25px; height: 25px; line-height: 25px;}
.on { border: 1px solid #7CBA23;}

线条使用line画线

line { border-bottom: 1px solid gainsboro; height: 2px; width: 20px;}

整体采用angular js渲染列表


数据库


我们使用mysql数据库,mybatis框架连接。


异常提醒


我们会对每个接口和状态码进行关联,异常时,发送邮件,测试人员核实问题后反馈给开发,开发解决后,录入解决方案,以便下次查询。


# 总结


这个平台已经试行1个多月了,总体满足我们的需求,以往,我们跑测试脚本,往往不能实时监测服务器健康状态,现在,任何一个环境有问题,我们都实时,并且可以统计出某个环境从什么时间到什么时间接口处于异常。


这套平台,功能不多,但极大的简化了我们的对环境稳定性的评估,也收集了大量的数据信息便于后期统计分析。其次,该方案稍作改造,可运用于多种监测场景中。


# 展望


未来,我会融入一些智能化的分析,比如下文是某次邮件提醒的内容:


(智能监控) 发现《A客户私有环境》中 【获取新闻列表】 接口异常,异常代码为102;出参为{"data":null,"message":"成功","state":102}


历史解决方案有:


  1. 到管理端重启容器。

  2. xx表中有异常脏数据。根据历史统计,方案1可能性为80%,方案2为10%。


该环境接口在5分钟前是正常的,出参为{"data":null,"message":"暂无数据","state":200}


发现有另外2个其它环境异常,分别为XXX、YYY环境。但异常状态和该校不相似,排除接口war包bug的原因。


由于《XXX环境》其他接口在5分钟内某次监控均为异常,所以(智能监控) 已自主调用容器自愈模块,目前已恢复服务,本次收集数据耗时5分钟,分析问题耗时100毫秒,自愈耗时60秒,该环境中断服务时间为6分钟,本月中断服务总时长为15分钟,高可用性为98.33%。


热文推荐
老大问我,该如何提升springboot服务吞吐量?
真香:一款让你通过玩游戏来学习 Vim 命令的插件
某小型公司持续集成工具jenkins实践(JAVA WEB、Android、IOS、html)


点我,查看更多精彩文章。

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存