cover_image

腾讯新闻插件接入层重构实践:代码量锐减,迭代效率提升50%!

常桐 腾讯云开发者
2025年01月02日 04:30
图片
图片

👉目录


1 背景介绍

2 现状分析

3 “重写”设计

4 “重写”收益

5 “重构”的时机

6 总结




作为程序员,我们的职业生涯中往往会经历多次项目重构。许多业内专家在项目升级方面提出了各种方法论和原则,层出不穷。本文将以新闻插件接入层的重构过程为例,探讨实践中的重构范围和时机选择。

新闻插件接入层的重构项目严格来讲是个“重写”过程,整个项目历时9个月,改动范围涉及到插件接入层的23个接口,范围广泛,时间跨度长。之前的服务如果维护的好的话,其实本可以通过小步快跑的方式,分批次进行小范围的重构和优化,不需要花这么大的成本进行“重写”。但由于种种历史原因,这一过程一直未能启动,使得历史包袱越来越重,再进行小步快跑的话,临时工作量会增加到无法承受的范围。最终,我们在2023年初决定进行对整体服务直接进行“重写”升级。接下来,我们将详细介绍“重写”的过程和带来的收益,并探讨在后续维护中在什么时机进行真正的“重构”。

关注腾讯云开发者,一手技术干货提前解锁👇


//////////


1 月 7 日晚 7:30,腾讯云开发者视频号「鹅厂程序员面对面」直播间,为你揭晓过去一年狂飙、烧钱的大模型背后,是如何为企业降本增效,应用落地的,预约观看准时抢鹅厂周边好礼!








01



背景介绍

首先插件接入层涉及到的业务范围有以下这些:
信息流类:微信首页、手 q 首页、724页、早报页、双列视频页、竖版视频流等。

图片

底层页类:图文底层页、问答底层页。

图片

用户类:评论页、用户登陆页。

图片




02



现状分析

   2.1 架构图


图片


   2.2 当前服务整体问题


   2.2.1 维护成本高、开发迭代效率低


  • 服务语言框架不统一、框架老旧

    • 共2个服务,2种框架,都不是 trpc-go 框架,学习和维护成本高;

  • 数据链路不统一,同一种功能多种实现

    • 主要功能与端内相似,分为信息流、底层页、用户三大类接口,却与端内完全分散在两个服务中。同时相同的功能还分散在了不同的服务中,实现不统一,甚至同一服务中的相同功能实现也不统一。例如:信息流接口,分散在两个服务中,10+个接口,6种接口格式;评论接口,分散在2个服务中;

  • 测试环境搭建困难,联调效率低

    • 非 trpc-go 框架,无框架配置,下游依赖和业务配置都没有单独的配置文件,服务地址写死在代码中,与下游测试环境联调困难;

    • 核心服务是老发布平台 sumeru 迁移服务,现发布平台123不支持搭建个人测试环境,多人开发环境隔离困难;

  • 配置分散:40+个配置分散在4个配置平台,维护效率低


   2.2.2 数据不一致问题


  • 取值逻辑不一致

    • 由于信息流10+个接口6种接口格式,使得前后端的卡片格式化部分都无法复用,出现多场景取值逻辑不一致问题。

  • 下游依赖数据获取链路不一致

    • 信息流获取文章相关信息部分,下游依赖众多,与端内场景需要的数据源一致,却分开管理,存在个别场景数据源老旧导致不一致问题;


   2.2.3 稳定性低


服务框架老旧,本身没有 tp99 和 slo 监控;23年6月前未接入业务网关,也无法通过网关观测到 slo 指标,只能通过故障单汇总、报警和网关的 cls 日志统计稳定性:

(1)重构完成前1年内插件端有1次新闻内部 x 级故障,x 次 oncall

(2)接口成功率99.89%,不足3个9;


  • 依赖老旧的平台服务

    • ons-agent 当前无人维护,其故障造成一次插件 slo 不达标;

  • 服务可观测性差,定位问题困难

    • 框架老旧,无法接入伽利略,导致无 tp99 监控、slo 监控,无异常错误 trace 日志,链路日志不完善。由于定位问题时间长,导致插件一次内部 x 级故障(MTTR 2hour);

  • 框架老旧,性能差,稳定性保障机制不完善:

    1. 依赖的 tab-sdk 无法使用 trpc 请求,导致需要远程访问时性能差,造成一次 slo 降低问题;

    2. 无法使用 trpc-go 框架自动支持的故障熔断机制,下游部分节点故障时无法自动隔离问题节点,阻止问题扩散。


   2.2.4 工程质量差


  1. 单测覆盖率低于10%,无接口测试;

    1. 代码重复率高,接口众多,框架老旧、代码质量差,导致添加单测和接口测试成本极高;

  2. 代码规范问题 x 个,研发指标得分73.69;


   2.3 信息流接口设计问题


先介绍一下信息流接口重要阶段和功能:

  • provider:信息流接口内容列表提供层,负责获取各种来源的内容列表;

  • loader:文章数据加载层,从各种来源并发获取的文章数据,例如:从文章池拿文章基本信息、事件服务拿文章相关事件信息、用户服务拿文章作者相关信息等

  • packer:文章数据打包层,按照从 provider 步骤获取的文章顺序,用 loader 到的内容,给各个卡片打包;


然后再看下当前的模块设计:


图片

有以下问题:

  • 信息流各接口格式不统一:

    • 历史原因信息流10+个接口接口协议有6种,甚至相同接口不同场景下的返回格式也不一致(例如二级页);

  • provider 层分层逻辑不对

    • provider 层功能都是从推荐、配置等数据源获取数据列表,却一个接口一套实现,管理维护难度大;

  • packer 层分层逻辑不对

    • 由于接口格式不统一,packer 层也是每个接口都独立实现,相同卡片在不同场景下 format 的实现逻辑不统一,维护难度大,且存在逻辑不一致问题;




03



“重写”设计

   3.1 架构图


图片

✓ 接口接入业务网关 smartGW;

    并借助网关的限流插件和安全拦截插件进行流量安全治理;

✓ 将6种 web 端信息流接口格式统一为1种;

✓ 除信息流外主要场景与端内统一服务;

    内容类:图文/视频/问答底层页、专题/事件底层页、直播、合集;

    用户类:个人页/媒体页(用户服务)、评论;

✓ 信息流由于各场景业务复杂,单独部署服务处理,核心逻辑与端内对齐;

    与端内使用相同的信息流基础打包服务(feed),简化下游依赖管理;

✓ 当前配置均迁移至七彩石统一管理;

✓ 服务个数收敛为1个,采用 trpc-go 框架;


图片

  • 协议统一,添加协议转换层提供给手 Q 插件 hippy 老版本使用;

  • provider 层插件化,将推荐、配置、热点榜等内容提供方,抽象成插件,各场景按需使用;

  • 卡片内容数据加载和基础打包功能,与端内统一到 feed 服务上;

  • card 层插件化,构建基础卡片 basecard 用于一般场景逻辑复用,特殊场景继承自 basecard,实现特殊卡片组装逻辑;




04



“重写”收益

   4.1 提高迭代效率


以下两种类型的需求,占日常产品需求的70%。重构前单个需求平均规模4人日,重构后单个需求平均规模2人日,迭代效率提升至少50%。例如以下需求


(1)底层页相同场景的同一功能,一处开发,多处可用


case1【图文/视频】: 底层页新增xxx标签;

重构前:端内底层页、插件底层页分别开发;4.5人日 。

重构后:底层页服务统一,开发一次即可; 1.5人日。


case2【问答】: 发布问题支持摘要、文章引用能力;

重构前:需要端内接入层、插件接入层各自开发,2人日。

重构后:数据链路一致,1人日。


(2)新增信息流场景


case1【插件】:外显推送热点精选页面;

重构前:每个场景的处理逻辑是单独的,可复用性差,新增场景需要单独开发,4人日。

重构后:有信息流处理模版方法,有基础的处理逻辑,各步骤也都有不同的插件可复用。0.5人日。

其他需求:无特殊逻辑的频道,只需配置;有特殊逻辑时,只需针对某一步骤开发特殊插件;


   4.2 解决各端不一致的 case


case1:视频封面图尺寸与端内不一致;

case2:手q724板块里视频的缩图模糊;

统一数据链路后,插件与端内,各业务下相同尺寸的图片取图逻辑一致;


   4.3 工程质量提升


1、重构前后代码量对比统计


对比服务名总代码量非测试文件代码量
升级前xxx110218994302
升级后xxx25787025955(-68347)

仅统计了由插件独立维护的服务,与端内共用的服务例如图文底层页、个人页、评论等,与端内相同接口,特殊逻辑不多,这里未做统计。


2、代码质量对比


服务名研发指标得分单测覆盖率
stream73.69<10%
web_feed100(+26.31)66.57%(+56.57%)


3、完善接口文档和接口测试用例


图片

图片


   4.4 服务稳定性提升


(1) 接口成功率


插件接口成功率从约 99.89% 提升至 99.99% 以上,升级后核心接口 SLO 未出现不达标的情况。


旧服务未接入业务网关,自身上报数据也不全,通过旧服务接入的腾讯云 CLS 日志统计上看,插件存在很多异常状态码的访问。从状态码上看,插件接口的整体成功率在 99.89% 左右(还是在做过初步治理的情况下)。


插件旧服务 CLS 统计


图片
图片
图片


新服务监控:


图片


成功提升的同时,核心接口耗时与之前持平。


由于插件之前就是 golang 的服务,除推荐外,获取数据都有缓存,故耗时提升空间有限;


旧服务:


图片

新服务:


图片


(2) 线上故障数量减少


重构完成前1年内插件端有1次新闻内部 x 级故障,x 次 oncall,重构升级后至今1.5年没有故障,x 次 oncall,oncall 数量下降73%;


故障:重构前 xxx 故障。

原因:代码 bug 导致,定位时间长(mttr 2h)升级为内部 x 级故障。排查时间长的原因为日志打印不完善,缺少 trace。

重构后:借助 trpc-go 框架生态的伽利略和鹰眼日志,可快速定位问题,预计 10min 内定位;


   4.5 问题排查效率提升


原有系统仅有通过加代码的方式主动上报主调被调监控,以及在此基础上的基本告警。


(1)伽利略监控系统

    1. 完善 tRPC-go 监控,协程数量、gc 耗时、gc 停顿时间;

    2. 主被调监控,异常率、超时率、成功率、调用量、p99 耗时与相关告警;

    3. 中心化的日志查询工具;

    4. 链路日志追踪工具;

    5. 自定义监控及报警。


图片

(2)染色 trace 日志


借助网关染色插件,打通接入层、微服务和推荐的染色日志,极大方便联调和 case 排查


图片
图片


   4.6 恶意流量治理


(1)接入门神

端外主要接口接入门神系统,借助公司已有系统,对基本的常见漏洞攻击做好防护。


(2)通过业务网关做接口限流、并接入平台治理工具

接入业务网关,对遇到异常流量的接口提交给平台治理侧分析,并加入安全拦截插件。目前有6个接口加入安全拦截插件,2个 feed 流接口加入限流插件。


   4.7 成本优化


(1)插件服务器成本

CPU 资源节省:xxx-xxx=xxx 核(-49%)

内存资源节省:xxx-xxx=xxxG(-20.6%) 


(2)插件 redis 成本优化

插件通过梳理代码、优化逻辑,服务调用的 redis 数量从xx个缩减至x个,缩减比例91%;

共下线/缩容 redis 资源 x 个,容量共 xxxGB,预计节省 2w+元/月。


   4.8 前端收益


重构升级极大依赖前端同事的大力支持,在升级过程中前端也在持续优化,也同样有不小的收益。主要体现在代码清理和开发效率提升。

  1. 重构优化清理无用代码16000+行,打包主 JS 文件从491kb减少到307kb,降幅37%,整页平均耗时降低10%

  2. 信息流接口格式统一,信息流组件可复用,降低开发成本,开发效率提升20%

  3. 信息流接口开启gzip,耗时降低100ms,降幅11%。




05



“重构”的时机

其实重写的收益不仅仅体现在上面的这些指标,更是在重写后的一年中,每次需求评审时,我们都能明显感受到其带来的便利。例如,某些功能点在端内需求中能够自动支持,而另一些功能则无需开发,只需联调即可。


然而,不得不承认,重写项目的周期较长,长达9个月,且工作量庞大,同时还需要前端团队的配合。在重写项目进行的同时,我们还面临着需求压力,尤其是前端团队也在进行他们自己的优化项目。经历过成本如此高的项目之后,大家自然希望下一次重写的时间能够尽量推迟。


那么,我们如何才能推迟下一次重写的时间呢?我的建议是随时进行真正的“重构”。接下来,我们来讨论一下真正的“重构”应该在何时进行以及如何观测。根据个人经验和一些资料的查询,在以下几个点上我们进行实践的落地:


1. 代码质量下降时

当代码的可读性、可维护性和可测试性显著下降时,重构是必要的。例如:代码中出现大量重复代码;函数或类的复杂度过高,难以理解;代码中存在过多的注释来解释复杂的逻辑。

观测方法:定期观测代码 codecc 指标


2. 功能扩展或修改时

在实现新功能时,发现现有代码结构不适合扩展;修改某个功能时,发现相关代码的耦合度过高,影响了其他功能。

观测方法:每个需求记录代码开发量、需改动的模块数、开发工期,对于异常的部分,随需求进行重构改动;


3. 代码审查或测试反馈时

在代码审查或测试过程中,团队成员可能会发现代码中的问题或改进建议;当测试覆盖率低或者发现单元测试添加困难时,大概率代码需要进行结构优化;

观测方法:严格的 code review 机制,所有改动必须 review;流水线必须添加单测覆盖率检查,代码开发人员感觉单测添加困难时,及时优化或者向组内其他成员反馈并讨论优化方案;


4. 性能问题时

当系统出现性能瓶颈时,例如:发现某些算法效率低下,代码中存在不必要的复杂性时;

观测方法:添加成功率和耗时变动监控;


5. 技术栈或工具更新时

当项目的技术栈或工具发生变化时,例如:升级到新的框架或库时,可能需要重构以适应新特性;采用新的开发工具或流程时,重构可以帮助提高开发效率。

观测方法:定期调研使用的框架或库是否有更新;根据 codiumAI、工蜂 AI 等工具的建议进行检测和优化代码;


6. 技术栈或工具更新时

定期进行技术审查和代码评估,可以帮助团队识别需要重构的部分。通过定期的代码质量检查,团队可以主动发现问题并进行重构。

观测方法:各模块 owner 按季度维护和更新架构图,包括调用流程图和类图,组织组内评审,并排期优化;




06



总结

将上面的结论来看,重构的时机基本是“随时”。有一句俗语,“最好的时机是昨天,其次是现在”,对于重构来讲也是很适用的。最好的时机是之前做设计的时候,但往日不可追也,过去也不可能预见现在的变化;其次是现在,每当发现技术债务的时候,尽快小范围重构。


-End-
原创作者|常桐

图片


你对项目重构的时机有什么看法?欢迎评论留言。我们将选取1则优质的评论,送出腾讯云定制文件袋套装1个(见下图)。1月9日中午12点开奖。

图片


📢📢欢迎加入腾讯云开发者社群,享前沿资讯、大咖干货,找兴趣搭子,交同城好友,更有鹅厂招聘机会、限量周边好礼等你来~


图片

(长按图片立即扫码)


图片
图片

图片

图片

图片

腾讯技术人原创集 · 目录
上一篇架构实践:同时支持单体、微服务,单台服务器还能支撑十几万用户?下一篇万字详解高可用架构设计
继续滑动看下一个
腾讯云开发者
向上滑动看下一个