我的前端故事----来聊聊react-native应用的健康监控
监控什么
今天我们来聊聊如何监控你的应用程序,这里的监控说的不是让我们去监控用户,而是监控应用的健康状态,什么是健康状态呢?对于后端的同学来说,在微服务的架构下,每个子服务是否正常工作、返回的结果是否满足预期,这些就算是健康状态,再举个例子,你的台式机,对于操作系统来说,每个硬件是否能正常的工作、工作的稳定性,这些都是需要关注的健康状态。
既然我们关心健康状态,那么我们该如何衡量一个“设备”的健康状态呢?对于上面的例子,CPU运行的温度、硬盘读取的速度、子服务执行的效率,这些都可以作为健康状态的参考标准。而对于我们前端来说,一个服务的响应速度、某个页面渲染的时间、外接设备是否正常运行、以及正常运行的时间比,这些都可以作为我们衡量一个“设备”是否健康的标准。
上面说的了要监控什么指标,那这些指标具体的实例又是什么呢?由于我主要做react-native应用的开发,我今天就基于react-native来讨论一下这件事,而对于传统的web,相对来说就简单一些了,但具体的思路不会差太多。
在我遇到的实际场景中,我的应用程序常常需要链接多个外接设备,例如:键盘、扫码枪、各种个感应器,所以我需要时刻关注这些设备的健康状态,一旦发现某个设备不能正常工作或者在未来的某个时刻不能正常工作,就需要马上反馈出来,而这只是一部分,这些物理设备有着很明确的“指标”。
另一方面,诸如网络状态、电池电量,这些应用内的“指标”也需要我时刻的关注,什么时候处于弱网环境、什么时候出现低电量等各种各样的异常情况都会让我们的应用程序变得不健康。所以,我们的目标就是围绕着这两块展开监控,那么接下来我 们说说该从什么地方下手。
生命周期
将所有想要监控的服务收集到一起,作为一个总控制,然后在总控中对各个服务器的各个生命周期埋点。
1、主动式:手动的从各个生命周期中hook想要的数据,然后通过计算,收集上报。
2、被动式: 在各个生命周期中埋点,等待某一类事件的触发。
可是这么多设备,如果我们一个个的去监控、去适配,那就和给windows系统的硬件写驱动一样繁杂了,这对于我们前端开发来说工作量实在是太大了,所以为了方便我们进行统一的管理,和复用统一的代码,我们需要一个“模式”去规范和统一我们的设备。
现在我们用一个统一的class去集中监控我们的设备,另外一个问题就是众多的设备,无论是“物理”的设备还是“虚拟”的设备,如果我们专门为每一种去写监控代码,工作量实在是太大了,所以我们可以让这些设备在上层表现的“一致”,为此,我们引入“生命周期”这个概念,在一个设备启动、运行、暂停、卸载的各个阶段,我们都可以进行监控,无论是扫码器,还是网络请求的发起,各种各样的形态都逃不过这个步骤,所以,只要能在这个上面做好文章,那么监控各种数据就易如反掌了。
以下我会从两个方面来介绍一下我总结的“基础”的监控项,个人认为,无论你的项目为了应对什么样的场景,下面的这些例子基本都会是“必备”的选项了,即便你的项目比我的项目更加精简,但是下面介绍的思路也会是不错的参考。
主动式监控
上面说了这么多,那么我们来具体的看看需要监控些什么呢?
在一个项目刚上线的时候,存在着很多隐藏的问题,所以我们需要更多的日志去监控应用程序是否正常的运行,以及是否按照我们自身设定的路线去执行,一旦项目稳定,一些模块或逻辑被证实是没有问题的了,那么相应的我们也要去移除一些埋点日志,另外一方面,在开始上线的时候由于用户量少,一旦出现线上bug,由于缺少案例,导致定位和分析的难度都会增大,所以,这个时候我们就需要主动的埋一些监控点,来应对这种情况的发生,在出现紧急bug的时候我们可以通过日志点去推测和演算用户的行为,以及当时的情况。
但是,说到底这些监控都是临时的,不一个长期的监控,所以我的原则也是尽可能的不去侵入到业务代码中,无论是通过切面编程还是高阶组件封装,都要保证这些监控代码可以通过一个或一组规则快速的关闭统计,解放算力。
因此,凡是主动监控都要具备自动判断和动态策略这两个特点,可以动态的感知到当前的状态,从而做出对主业务影响最小的行为,即便监控再重要,也不能因为监控的行为而影响业务进程的工作,这只能是一个锦上添花行为。
被动式监控
为什么要有被动式的监控呢?首先,有些监控的相对“独立”的,每一次的监控点并不会100%的触发,每次的触发并不存在上下文或者前后的必然联系。也就是说,这些点的触发都是单独存在的,所以我们不需要对其进行诸如计算、格式化等分析操作,也不需要保存它的上下文,比如开始、结束时间。
相对于上面介绍的主动式监控,被动式监控的代码和逻辑都会长期的运行,因为在项目的中后期,在移除了大多数异常、性能监控后,这些仅存的代码就成了我们排查问题的关键所在了。因此,这些监控要保证自身的稳定,以及所积累的信息准确、及时,谁都不希望看到奔溃或者逻辑错误的警报在发生之后很久才上报出来。
那么有哪些需要我们做被动式的监控呢?在我的项目中,一个外接设备是否在线,用户点击键盘上某个按钮等行为就是一个典型的被动式监控,我不知道用户什么时候去点键盘,我也不知道我的外接设备什么时候断开,我只是需要捕获到这个行为的发生就可以了。
而且对于这些监控点,我们实际上是不需要开发自己去写代码的,它应该存在于依赖的包中,这就避免了我多次写重复代码的问题,举个例子,我在A项目中有个对扫码器扫描到内容的监控,而在B、C项目中我仍旧需要这个功能,那么我就可以不再反复的去写这块的日志代码了,因为它应该存在于这个扫码器的包中,我要做的,只是提供一个logger方法而已,返回的格式、内容都不需要我去关心。
而这些通过埋点、用户输入、事件回调等方式收集上来的日志,我们都要如实的上报到远程服务器(这里和主动式监控有所区别,主动监控的内容可以允许我们自己计算、统计),因为这些都是真正的异常,以后会影响我们debug的日志要最大程度的保留现场。
客户端流程图
服务端
上面说了那么多,都是基于客户端去做的,现在我们在客户端已经准备好了想要的数据,那么我们该如何去使用他们呢?
对于发送上来的日志,我可以做如下三件事:
- 监控24小时在线状态
- 异常指标的快速报警
- 可视化的展示监控信息
下面我们就围绕这三点来设计我们的服务端系统。
对于第一点,我们可以通过与客户端的心跳包来检查客户端是否存活,因为在业务场景中,我们的应用为react-native的,所以在检测心跳的时候就要区分是native模块还是js的业务模块了,同时很多场景下存在无人使用时js业务不会上传日志以及在移动网络下业务精简日志的情况,因此我们的24小时监控服务就要足够灵活、多变。所以在报警的时候可以通过读取配置的方式在服务器不重启的情况下动态的变换监控规则。
例如什么项目需要监控native存活,什么项目需要监控js环境存活,什么时候将报警通知给何人,并且可以通知到是什么地方的什么设备出了什么故障,这些都是基础功能。
而第二点,需要我们做的除了包含第一点之外的功能外,还有一个异常记录的功能,因为第二点的异常具有偶发性,并且相对于心跳包来说,日志内容更加丰富,因此我们可以针对这些日志做更多的事情,但首先就是将这些日志分类的保存起来。同时,在第二点中存在不同的应用对不同的异常有不同的定义的情况,比如说A应用认为数据初始化的接口超时就属于异常,而B应用则认为即便超时也不影响,那么就需要为每个应用单独配置异常指标,从而做到分项目、分阈值的功能。
还有一些额外的拓展,由于上面做的分项目、分阈值处理,就势必存在一些通用异常,那么每个项目就应该具有继承公共异常的功能,以及一些模板异常的设定。这些都是这个服务所需要的功能。
最后一点,在搜集到异常以及通知到相关负责人之后,这次异常报警就算结束了嘛?当然没有这么简单了,我们可以基于web服务来查看一些日志的基本情况,例如什么项目报警最多,什么地方报警最多,什么时间报警最多等等图表提供我们查看和分析。更有甚者我们可以根据不同的报警级别给不同的人发送不同的报警内容。
最后是这个服务端自身的健壮性,由于我们的服务是基于nodejs来做的,因此通过pm2,我可以很方便的在进程挂掉之后马上恢复服务,同时为了服务相互解耦和最大化cpu效率,通过pm2启动脚本来将24小时离线服务、异常指标报警、日志分析展示服务相互独立开,并为可以启动多线程的服务提供cluster模式的支持。并且将共享的数据通过redis缓存,配置通过数据库持久化等方式来备份和保证服务的健壮与高效。
服务端流程图
总结
至此,一个由客户端+服务端的健康监控系统初步完成。它们这些服务看似相互依赖,但又相互解耦,不会造成一个环节失效导致整个系统奔溃,同时又可以做到对正常业务最小化的侵入。
当然,这些都只是一个雏形,一个全部由js去完成的项目,相对于大公司那些完整而又系统的监控来说,仅仅只能作为开发业务的我们自查的一个工具。虽然有这些系统来保证我们的项目正常、健康的运行,但是更重要的是我们开发者自己代码的健壮和稳定。工具做的再好,也不能替代我们自己写的优异的代码。
我的前端故事----来聊聊react-native应用的健康监控的更多相关文章
- 我的前端故事----来聊聊怎么写react-native上的样式吧
我遇到了什么问题? 不久之前我重构了一个古老的项目,总结了一些js方面的想法,不过对于一个前端项目而言不仅仅只由js组成的嘛,上学的时候老师和我说HTML+CSS+JS对应的是页面的骨架.皮肤和肌肉. ...
- 移动端跨平台方案对比:React Native、weex、Flutter
跨平台一直是老生常谈的话题,cordova.ionic.react-native.weex.kotlin-native.flutter等跨平台框架百花齐放,颇有一股推倒原生开发者的势头. 为什么我们需 ...
- 最火移动端跨平台方案盘点:React Native、weex、Flutter
1.前言 跨平台一直是老生常谈的话题,cordova.ionic.react-native.weex.kotlin-native.flutter等跨平台框架的百花齐放,颇有一股推倒原生开发者的势头. ...
- 小谈React、React Native、React Web
React有三个东西,React JS 前端Web框架,React Native 移动终端Hybrid框架,React Web是一个源码转换工具(React Native 转 Web,并之所以特别提出 ...
- 腾讯优测优分享 | 探索react native首屏渲染最佳实践
腾讯优测是专业的移动云测试平台,旗下的优分享不定时提供大量移动研发及测试相关的干货~ 此文主要与以下内容相关,希望对大家有帮助. react native给了我们使用javascript开发原生app ...
- 探索react native首屏渲染最佳实践
文 / 腾讯 龚麒 0.前言 react native给了我们使用javascript开发原生app的能力,在使用react native完成兴趣部落安卓端发现tab改造后,我们开始对由react n ...
- H5、React Native、Native应用对比分析
每日更新关注:http://weibo.com/hanjunqiang 新浪微博!iOS开发者交流QQ群: 446310206 "存在即合理".凡是存在的,都是合乎规律的.任何新 ...
- React Native踩坑日记 —— tailwind-rn
项目背景 在项目的初始阶段,我们需要建立自己的design system,我们spike了一些方案,tailwind-rn就是其中一种,如果有用到或者即将用到tailwind-rn的,可以进来看一看, ...
- React Native纯干货总结
随着项目也渐渐到了尾声,之前的项目是mobile开发,采用的是React Native.为即将要开始做RN项目或者已经做过的小伙伴可以参考借鉴,也顺便自己做一下之前项目的总结. 文章比较长,可以选择自 ...
随机推荐
- ES2015 类 class 语法
在ES2015之前,定义类的方法只能通过原型链来模拟 function Animal(family,species) { this.family = family; this.species = sp ...
- Java 多线程之哪个对象才是锁?
问题背景 在感觉正常的使用ArrayList的迭代删除的操作的时候,发现了如下的崩溃日志: Caused by: java.util.ConcurrentModificationException a ...
- Handsontable添加超链接
本文在上文的基础上,返回的数据中多了一个link超链接跳转的字段,,需要在Handsontable中显示超链接. <!DOCTYPE html> <html> <head ...
- CentOS配置本地yum源
如果CentOS服务器处在内网环境中时,如果缺少依赖手动安装那么会非常麻烦,要花费很多时间来寻找rpm包,现在如果搭建本地的yum源,就非常方便了,使用yum源首先需要一个CentOS安装镜像,去官网 ...
- 2-SAT 问题与解法小结
2-SAT 问题与解法小结 这个算法十分的奇妙qwq... 将一类判定问题转换为图论问题,然后就很容易解决了. 本文有一些地方摘录了一下赵爽<2-SAT解法浅析> (侵删) 一些概念: \ ...
- UWP 创建动画的极简方式 — LottieUWP
提到 UWP 中创建动画,第一个想到的大多都是 StoryBoard.因为 UWP 和 WPF 的界面都是基于 XAML 语言的,所以实现 StoryBoard 会非常方便. 来看一个简单的 Stor ...
- 记录解决python在spark运行加载第三方库的问题
一般写python的我们经常会import一些常用的库,然后有时集群环境上的python没有这些库,怎么办呢? 通过一段时间的摸索发现有二种方式可以解决这个问题: 第一种方法: 下载对应python的 ...
- 常见的if语句shell脚本
常见的if语句shell脚本 author :headsen chen 2017-10-17 15:00:07 1,cat if.sh 2, cat if2.sh
- Maven-05:插件目标
在学习插件和生命周期的绑定关系之前,必须先了解插件目标(plugin goal). 我们知道,Maven的核心仅仅定义了抽象的生命周期,具体的任务是交由插件完成的,插件以独立的构件形式存在,因此Mav ...
- http.request的请求
var http=require('http'); var request=require('request'); var body = { "data":{ "id&q ...