新功能部署记录

引言

大家好!最近花了整整两周时间,总算把那个磨人的新功能给上线了。说实话,这次部署过程属实有点折腾,踩了好几个坑,但也积累了不少经验。今天正好有空,就跟大伙儿好好聊聊这次部署的全过程,算是给自己做个记录,也希望能给有类似需求的朋友一点参考。

这次我们要上线的是一个用户行为分析模块,简单来说就是记录用户在产品里的各种操作,然后通过数据可视化的方式呈现出来,帮助产品经理和运营同学更好地了解用户习惯。听起来不难对吧?但实际做起来才发现涉及的知识点还挺多的。

功能概述

先说说这个功能具体是干啥的。我们团队这段时间一直在优化用户体验,但苦于没有具体的数据支撑,很多决策都是靠"感觉"来做的。于是产品同学就提出了这个需求——做一个用户行为分析系统。

具体功能包括:

- 事件埋点:记录用户的点击、浏览、停留等行为

- 用户路径分析:看看用户都是怎么使用产品的

- 漏斗分析:转化率是多少,哪一步流失最严重

- 实时数据看板:运营可以实时看到数据变化

技术层面的话,我们用 Node.js 写了一套数据采集服务,数据存到 MongoDB 里,然后用 Elasticsearch 做搜索和分析,最后用 Vue 搭了个可视化后台。整体架构不算复杂,但对于我们这个小团队来说也算是折腾了一把。

技术选型那些事儿

选型阶段真的纠结了很久。数据存储这块,最开始考虑的是直接用 MySQL,毕竟团队大家都很熟。但后来一想,这种日志类的数据量可能会很大,写入频率又高,MySQL 估计扛不住。

于是就调研了一下 MongoDB。怎么说呢,MongoDB 确实很适合这种场景,文档型的结构对于这种不固定格式的埋点数据特别友好。而且支持水平扩展,后期数据量大了加机器就行。不过 MongoDB 有个问题就是事务能力不如 MySQL,但我们这个场景对一致性要求没那么高,所以问题不大。

然后是 Elasticsearch。最开始数据量小的时候,我们直接用 MongoDB 的查询也能满足需求。但产品经理说了,后期要做复杂的统计分析,什么按时间段筛选、按用户分组、聚合计算之类的,MongoDB 就不太够看了。Elasticsearch 在这一块确实是强项,聚合查询跑起来那叫一个快。

这里有个小建议:如果团队之前没用过 Elasticsearch,上手确实需要一些时间。建议先在测试环境跑一段时间,熟悉一下查询语法和调优手段。我们当时就是吃了这个亏,线上环境配置没调好,前期查询特别慢,后来又是调缓存又是优化索引,折腾了好一阵。

部署准备

正式部署前,我们大概花了一周时间做准备。现在回想起来,有些工作真的不能省。

代码审查和测试

这个不用多说,但我要强调的是,这次我们特意增加了压测环节。用 JMeter 模拟了一波高并发场景,看看采集服务能不能扛住。结果还不错,单机 QPS 能跑到 3000 左右,满足当前需求是没问题。不过后来想想,还是保守了一点,应该按 10 倍峰值来设计的。

数据迁移方案

因为要兼容之前的一些历史数据,我们写了个迁移脚本,把老系统的日志数据导到新的 MongoDB 里。这里有个坑:老数据的时间戳格式跟新系统不一致,导致迁移后时间全部错乱了。后来还是同事灵机一动,写了个转换函数才解决。所以啊,数据迁移前一定要先抽样检查一下格式对不对!

回滚预案

这个必须要有!我们当时制定了详细的回滚方案:

1. 如果新服务启动失败,立即切回旧服务

2. 如果数据写入异常,暂停采集服务,启动旧的数据通道

3. 如果 Elasticsearch 查询超时,先用 MongoDB 兜底

实践证明这个预案很有必要——后面真的遇到问题了,多亏有回滚方案才没造成线上事故。

部署过程

终于说到正题了,部署当天的情况真的是一波三折。

第一步:服务部署

我们用的是 Docker 来部署服务,不得不说 Docker 确实是省心。把镜像一拉,容器一跑,基本就完事儿了。贴一下我们的 docker-compose 配置:

version: '3.8'

services:

collector:

image: our-registry/collector:latest

ports:

- "8080:8080"

environment:

- MONGODB_URI=mongodb://mongo:27017/analytics

- ES_HOSTS=elasticsearch:9200

depends_on:

- mongo

- elasticsearch

mongo:

image: mongo:6.0

volumes:

- mongo-data:/data/db

elasticsearch:

image: elasticsearch:8.10.0

environment:

- discovery.type=single-node

- "ES_JAVA_OPTS=-Xms512m -Xmx512m"

这里有个小插曲,Elasticsearch 8.x 默认开启了安全验证,我们一开始没配置证书,客户端一直连不上。后来查了文档,把 xpack.security.enabled 关掉才解决。当然了,生产环境还是建议开启的,我们这只是测试环境偷懒。

第二步:数据验证

服务跑起来后,我们没急着全量接入,先用小流量跑了一波验证。找了几台测试机器改了配置,让它们把数据同时发到新旧两套系统里,然后对比数据是否一致。

结果一对比,发现问题了:新系统的数据延迟比老系统高很多,一查原因,是我们采集服务里有个异步处理的逻辑设计得不太合理,队列堆积导致延迟。还好发现得早,不然全量上线后数据延迟那么高,产品经理肯定要炸锅。

第三步:全量上线

问题修复后,终于开始全量上线了。我们采用的是灰度发布的策略,先切 10% 的流量,观察一段时间没问题再逐步提升。

具体操作就是在 Nginx 那层做了个加权轮询,把 10% 的请求打到新服务上:

upstream backend {

server old-service:8080 weight=9;

server new-service:8080 weight=1;

}

上线那天说实话还是挺紧张的,一直盯着监控面板,生怕出什么问题。好在一切顺利,观察了两天没有任何异常,第三天就把流量全部切到新系统了。

踩坑总结

最后来聊聊这次部署踩到的几个坑,给大家提个醒。

第一个坑:时区问题

上线第一天,运营同学反馈数据看板显示的时间不对,比实际时间少了 8 小时。一查代码,发现是 Elasticsearch 默认用的 UTC 时区,而我们存储的时间戳是北京时间。解决方案是在写入数据的时候统一转换为 UTC 时间戳,读取的时候再转回来。

第二个坑:内存溢出

Elasticsearch 刚上线那几天,运行得好好的,突然有一天服务直接挂掉了。一看日志,是内存溢出。我们配置的 JVM 堆内存是 512MB,但实际数据量上来后完全不够用。后来加到 2GB,总算稳定了。这里建议大家生产环境一定要给足内存,Elasticsearch 这玩意儿是真的吃内存。

第三个坑:索引命名

这个是低级错误了。我们在创建索引的时候用的是动态索引名,日期格式写错了,导致每天的索引名字都不一样,查询的时候一直查不到数据。还好及时发现,改成正确的格式就解决了。

结尾

好了,以上就是这次新功能部署的全部记录。总的来说,虽然过程有点曲折,但最后结果还是不错的。目前系统已经稳定运行了两周,数据采集和分析都在正常进行,产品经理那边反馈也很好。

如果要说有什么经验可以分享的话,我觉得主要有三点:

1. 充分测试:特别是压测和数据迁移,一定要在上线前做好

2. 灰度发布:不要一次性全量上线,先小流量验证

3. 做好监控:这次我们加了详细的监控和告警,一旦出问题能第一时间发现

好了,文章写到这里也差不多了。如果你也在做类似的事情,希望能对你有所帮助。有什么问题的话,欢迎在评论区留言交流,咱们下期再见!