前些日子,我所在的Team接到了一个“大活儿”,为我们公司某个服务(出于保密的原因,这里不能直说)做数据可视化及数据分析平台。
与数据生产方(服务维护方)沟通几轮后,我了解到了本次的后端服务有几大挑战:
我们对接的服务维护方可以为我们提供的是 将所有实时数据落到clickhouse
(一种适合大数据存储分析的数据库)中,除实时数据外,还有小时级别的聚合数据(维度是各业务单元)。
也就是说,每个业务线在该服务上产生的数据会每小时做一个聚合并落到另一张表(包括:小时内的总计、平均值、uv均值、pv总值等等)。
剩下的事情就要靠我们几位前端同学搞定了。
后端当然是用NodeJS写,毕竟我们是前端开发,选择NodeJS是理所当然的。
但是基于NodeJS的http server框架该选什么?目前市面上三个比较被大家熟知的有3个。
Express
本人用了多年,做过两个运行多年的大型项目,但事实上用的很痛苦。每次的请求实例都是通过参数一层层传递的,所有错误也要一层层传递出来。用起来很不爽,急需寻找改变。Koa
是Express
原班人马打造的, 从根源上做解决了Express的很多痛点,但是我需要一个更适合企业级应用的框架EggJS
成为了最终的选择,我觉得Egg有如下的优势:
本次开发的系统结构图大致如下:
系统中关键的几点:
介于数据的安全性,对于每个请求都会做登录状态(sso
)和资源权限的校验,所以使用中间件是非常合适的。
因为Egg中间件机制是遵循”洋葱圈”模式,每个请求都会经过各个中间件的一层层处理。
我们公司基建中是有登录校验
和资源权限校验
的http API
,可是在我们着手开发此项目时,并没有针对NodeJS
的sdk封装,于是我将这两个校验过程封装成了两个Plugin
。
有了这两个Plugin后,今后只需要在不同环境的config文件中配置好appKey
、appSecret
和其他相关信息就好。(这些是我司对于http API请求的安全校验机制,类似于微信公众平台)。
事实证明,有了这两个
Plugin
后,今后Egg项目的开发效率得到了大幅度地提升。
项目中的所有配置都写在了配置中心(本次项目选择用etcd
),这样做有几个好处:
etcd
自带权限管理,所以只有当前项目权限的开发者才会看到关键信息每次接口请求的数据都会缓存到Redis
中,下次请求如果发现Redis中已经存在,则从Redis中取。
为了保证数据的实时性,Redis缓存时间可以设置的短一些
上图和我监控架构很相似,不过有一些不同点:
AlertManager
通知的手段会更多。sentry
系统稳定性需要关注的几点:
存活检查
和就绪检查
都通过后,再删除一个……这样循环,从而实现平滑重启
自动扩容
是当你的应用突然迎来一波流量高峰,云平台可以自动为你复制出多个pod,你只需要设置满足扩容的条件即可(如CPU使用率超过85%并持续1分钟)抛出异常
,应该第一时间得知,化被动为主动。(代码抛异常,有时并不会让系统崩溃,所以很容易被人忽视)。
本次项目使用了Sentry
,如果发生错误,开发者会第一时间收到邮件通知,并且可以在平台中看到各个异常相关的数据和统计。
通过上面的总结,我们的系统已经可以稳定运行了,并且形成了像下面一样的完整闭环。下一节的”性能优化”就是基于这个闭环来进行优化的。
在上面的指标监控的加持下,在运行了一段时间后,通过Grafana的指标监控显示,发现了我们的数据可视化系统中,某个业务线的接口返回时间很长,大约要22s以上,这个时长是无法容忍的。
根据日志排查发现,原因是某个业务线的数据量很大,针对于该业务线的所有数据都很慢。
通过日志排查,日常消耗在egg-sequelize
进行数据查询上,通过其他SQL平台进行查询依然很慢,说明这个查询本身就是慢查询。
如果是MySQL的话,如果出现慢查询,我这里会收到报警,但是clickhouse目前没有这种处理。
首页展示内容是当前业务线的实时内容,包括:
上面三个接口返回时长都在22s以上。
数据均已做过处理
解决方案:
介于用户对于这些数据的实时性要求不是那么那么高,偏差几分钟是在接受范围内的,所以我利用定时任务每5分钟计算一次首页数据,并将这些计算结果缓存到redis中,所以这些接口实际是从Redis读数据,速度大幅提升,最多5分钟的延迟也是用户可以接受的。
接口返回时长均在100ms之下
数据均已做处理
用户行为分析页面的特点是用户可能会对长时间范围内的数据进行聚合和分析,所以用户选择的时间跨度越大返回时长越长,clickhouse
的查询就越慢(单个业务线的每日数据量在千万以上)。
这个页面查询数据的特点:
所以最后的解决方案是,对各个业务线在小时
和天
两个级别将上面的所有指标数据提前计算出来,并落到clickhouse
上,所以此页面的计算会是在中间表上进行查询,效率非常高。定时任务中没有计算到的时间点(如当前小时和上个小时还没有计算和落库),可以再单独去clickhouse
中查询,最终将两份数据合并。
接口返回时长也在100ms左右
该项目已经在线上正常运转3个月以上了,在性能上还没有收到用户的负面反馈,总体来说还是符合预期的。
但是我系统中还很多不完美的地方:
distinct
处理是十分耗时的。接下来要做的努力:
数据切片
并落库。Flink
+ Kafka
,进行实时运算再塞到消息队列Kafka
中,最后再数据落地,这样数据量会减少很多。经过这次项目过后,我们在Node应用开发上积累了很多经验,并且沉淀出了多个egg插件,为今后的开发打下了良好基础、效率大大的提升。事实上,在此之后的多个Node项目的开发周期可以控制在一周内,开发人员可以将精力集中在业务逻辑上。
或许以下文章您也会感兴趣