当前位置: 首页 > 后端技术 > Node.js

说说react-native应用的健康监控

时间:2023-04-03 17:01:31 Node.js

监控什么今天就来说说如何监控你的应用。这里的监控不是让我们监控用户,而是监控应用的健康状态。什么是健康?状态呢?对于后端同学来说,在微服务架构下,各个子服务是否正常工作,返回结果是否符合预期,才算健康状态。再比如你的桌面,对于操作系统来说,各个硬件是否能正常工作,工作的稳定性,这些都是需要关注的健康状态。既然关心健康状况,那么如何衡量一个“设备”的健康状况呢?对于上面的例子,CPU的运行温度、硬盘的读取速度、子服务的执行效率都可以作为健康状态的参考标准。对于我们的前端来说,一个服务的响应速度,某个页面的渲染时间,外部设备是否正常运行,以及正常运行时间的比例,都可以作为我们衡量一个“设备”健康度的标准”。上面说了应该监控哪些指标,这些指标的具体例子有哪些?由于本人主要开发react-native应用,所以今天就基于react-native来讨论这件事。对于传统的web,还是比较简单的,但是具体的思路不会有太大区别。在我遇到的实际场景中,我的应用经常需要链接多个外部设备,比如键盘、条码扫描仪、各种传感器,所以我需要时刻关注这些设备的健康状态。一旦某个设备不能正常工作或者在未来的某个时间不能正常工作,需要立即上报,这只是其中的一部分。这些物理设备都有非常明确的“指标”。另一方面,网络状态、电池电量等应用中的“指标”也需要我时刻关注。弱网环境、低电量等各种异常情况都会使我们的应用程序变得不健康。所以,我们的目标就是监控这两个区域,那我们就来说说从哪里入手吧。生命周期将所有想要监控的服务收集起来作为一个总控,然后将每个服务器的每个生命周期都嵌入到总控中。1.主动式:从每个生命周期手动hook需要的数据,然后通过计算收集上报。2.被动:埋在每个生命周期中,等待某类事件触发。但是这么多的设备,如果我们对它们进行监控和适配,就如同为windows系统的硬件编写驱动程序一样复杂。这对于我们的前端开发来说工作量太大了,所以为了方便我们统一管理和统一代码的复用,我们需要一个“模式”来规范和统一我们的设备。现在我们使用一个统一的类来集中监控我们的设备。另一个问题是设备有很多,无论是“物理”设备还是“虚拟”设备。如果我们专门为每种类型编写监控代码,工作量太大,所以我们可以在上层让这些设备“一致”。为此,我们引入了“生命周期”的概念。在一个设备的启动、运行、挂起、卸载的每个阶段,我们都可以进行监控,无论是扫码,还是网络请求的发起,所有的形式都逃不过这一步。所以,只要你能做好这一点,那么监控各种数据就轻而易举了。下面,我将从两个方面介绍一下我总结出来的“基本”监控项。我个人认为无论你的项目是设计来应对什么样的场景,下面的例子基本上都会是“必备”选项,即使你的项目比我的更精简,但下面的思路也会是一个不错的选择参考。主动监控上面说了这么多,那我们再仔细看看需要监控什么?一个项目刚上线的时候,隐藏的问题很多,所以我们需要更多的日志来监控应用是否正常运行,是否按照我们设置的路由执行。一旦项目稳定下来,一些模块或者逻辑已经被证明是没有问题的,所以我们不得不相应地移除一些埋藏的日志。另一方面,由于上线初期用户量少,一旦出现线上BUG,由于案例不足,会导致定位和分析的难度增加。所以这个时候,我们就需要主动埋点一些监控点来应对这种情况。当出现紧急bug时,我们可以通过日志点、Case来推测和计算用户行为。不过归根结底,这些监控都是暂时的,不是长期的监控,所以我的原则是尽量不去侵入业务代码。无论是通过切面编程还是高层组件封装,都必须保证这些监控代码能够通过一个或一组规则快速关闭统计,从而释放算力。因此,所有的主动监控都必须具备自动判断和动态策略的特点,能够动态感知当前状态,从而采取对主业务影响最小的行动。即使监控很重要,也不能被监控行为所影响。影响业务流程的工作只能是锦上添花的行为。被动监控为什么要有被动监控?首先,有些监控是比较“独立”的,每个监控点都不是100%触发的,每个触发之间没有上下文或必然联系。也就是说,这些点的触发器是独立存在的,所以我们不需要对它们进行计算、格式化等分析操作,也不需要保存它的上下文,比如开始时间、结束时间等。相对于上面介绍的主动监控,被动监控的代码和逻辑会运行很长时间,因为在项目的中后期,去掉大部分异常和性能监控后,剩下的这些代码就成了我们排查问题的关键就是现在。因此,这些监控必须保证自身的稳定性,积累的信息准确及时。没有人希望看到崩溃或逻辑错误警报发生很久之后才报告。那么被动监控需要做什么呢?在我的项目中,外部设备是否在线,用户是否点击了键盘上的某个按钮等都是典型的被动监控。我不知道用户何时点击键盘,我也不知道我的外部设备何时断开连接,我只需要捕获这种行为的发生。而对于这些监控点,我们其实不需要自己去开发写代码。它应该存在于依赖包中,这避免了我多次编写重复代码的问题。比如我在项目A中有一个扫描器扫描的内容的监控,而我在项目B和C中仍然需要这个功能,所以我不能再重复写这段日志代码,因为它应该存在于这个扫描中code在logger的包中,我只需要提供一个logger方法即可,不需要关心返回的格式和内容。而这些通过埋点、用户输入、事件回调等方式采集的日志,我们必须如实上报给远程服务器(这里不同于主动监控,主动监控的内容可以让我们自己去计算统计),因为这些是真正的异常,影响我们以后调试的日志尽量保留在现场。客户端流程图服务端上面说了这么多,都是基于客户端的。既然我们已经在客户端准备好了想要的数据,那我们应该如何使用呢?对于上来的日志,我可以做以下三件事:监控24小时在线状态异常指标,快速报警可视化展示监控信息接下来,我们将围绕这三点来设计我们的服务器系统。对于第一点,我们可以通过与客户端的心跳包来判断客户端是否存活,因为在业务场景中,我们的应用是react-native的,所以在检测心跳的时候,一定要区分是nativemodule还是js同时在很多场景下,js业务在没有人使用的情况下不会上传日志,在移动网络下会简化业务日志。因此,我们的24小时监控服务必须足够灵活多变。因此,当告警发生时,可以通过读取配置动态改变监控规则,无需重启服务器。比如什么项目需要监控native的存活,什么项目需要监控js环境的存活,什么时候通知谁报警,可以通知哪里什么设备出现故障。这些是基本功能。至于第二点,除了第一点以外的功能外,还有一个异常记录功能,因为第二点中的异常是零星的,而且相比心跳包,日志内容更加丰富,所以我们可以对这些日志做更多的事情,但首先是要将这些日志分类保存。同时在第二点,不同的应用对于不同的异常有不同的定义。比如应用A认为数据初始化接口超时是异常,而应用B认为即使超时也不影响,那么就需要为每个应用单独配置异常指标,从而实现子项目和子阈值的功能。还有一些额外的扩展。由于上面的子项目和子阈值处理,必然会存在一些共性异常,所以每个项目都应该有继承共性异常的功能,以及一些模板异常设置。这些是该服务所需的功能。最后,收集到异常,通知相关负责人后,异常告警结束了吗?当然,没那么简单。我们可以基于web服务查看一些日志的基本情况,比如哪些条目告警最多,哪里告警最多,什么时候告警最多。图表为我们提供了查看和分析。更重要的是,我们可以根据不同的警报级别向不同的人发送不同的警报内容。最后是服务器本身的健壮性。由于我们的服务是基于nodejs的,通过pm2,我可以很方便的在进程挂掉后立即恢复服务,同时实现服务之间的解耦,最大化cpu效率,通过pm2启动脚本,将24小时离线服务、异常指标告警、日志分析展示服务相互独立,并为可启动多线程的服务提供集群模式支持。并且通过redis对共享数据进行缓存,通过数据库持久化等方式对配置进行备份,保证服务的健壮性和高效性。至此总结了服务端的流程图,初步完成了一个客户端+服务端组成的健康监控系统。这些服务看似相互依赖,其实是相互解耦的,这样一个环节的故障不会导致整个系统崩溃,同时可以将对正常业务的干扰降到最低。当然,这些只是雏形,一个全部由js完成的项目,相比于大公司完备、系统的监控,只能作为我们业务发展自查的工具。虽然有这些制度保证了我们项目的正常健康运行,但更重要的是我们开发者自己代码的健壮性和稳定性。再好的工具也无法替代我们自己写的优秀代码。