作者 | 百度文库 App
导读
本文深入浅出地分享了百度文库 App 服务端技术栈从 PHP 迁徙至 Go 的实战经验,蕴含了技术选型、根底建设、流量迁徙的具体计划,以及外围我的项目案例的重构实际。
全文 6209 字,预计浏览工夫 16 分钟。
01 动机
长期以来,百度文库 App 服务端采纳 PHP 作为次要开发语言,高效地撑持了业务迭代倒退。随着平台流量的持续增长,服务端的负载越来越大逐步靠近零碎瓶颈。为了晋升零碎的负载能力,咱们采取了一些优化伎俩,其中最快最无效的办法是减少在线集群的实例数量。此外,还采纳过 lua 开发我的项目,承接一些逻辑简略而访问量大的接口来分担负载。因为 lua 自身的一些局限性,不适宜做简单的业务逻辑。
随同着 IT 技术的倒退潮流,咱们积极响应公司降本增效的号召,决定在 2022 年年中迁徙并重构服务端技术栈。旨在降级技术架构,晋升零碎负载能力。
把握技术栈迁徙和我的项目重构的机会是很难的一件事件,特地是成熟的团队要进行大的零碎改变。如果没有呈现真正的痛点,即便研发同学认为技术实现上曾经呈现诸多设计不合理和有危险的中央,往往并不被容许花大量工夫去做技术我的项目。可一旦连业务人员(产品经理、销售、经营)也感觉零碎性能须要降级的时候,比方在用户体验上,App 文档搜寻接口提早比拟长,产品同学认为如果首屏渲染能显著提速的话,对点击率、付费率都会有大幅的晋升,然而研发这边基于老的技术栈曾经难以做优化了,那么此刻就很适宜做迁徙重构。
恰逢其时,产品同学提出一些晋升用户体验,同时适宜我的项目重构的需要,比方:减速搜寻后果页首屏渲染、新 App 首页,AIGC 智能创作。这些大需要非常有利于迁徙重构工作的启动,它们把迁徙重构所需减少的额定人力占用降到最低。在已有的性能上做迁徙重构,更快更稳固的接口响应带来晦涩的用户体验,这有利于促成整体团队的 okr 指标达成。
回过头来,梳理下过后服务端基于 php5.6 的技术债权:
1、底层技术:语言版本老旧,个性落后,存在执行效率低,平安危险,资源节约的缺点;
2、开发质效:业务逻辑交叉耦合,大量废除的接口和下线的业务逻辑,升高了代码可读性、可维护性,继续减少我的项目迭代的难度。
△技术债权
02 启动之前的状态
服务部署上,文库 App 的服务端部署形式是 nginx+hhvm(HipHop VM 3.0.1;baidu version:1.1.6 (rel)),HHVM 是 Facebook 开发的高性能 PHP 虚拟机,是传统的 nginx+php-fpm 的一个性能优化版本。近年曾经失去了 hhvm 原创团队的继续保护迭代,它反对的语法个性和执行效率绝对落后,存在肯定平安危险。
查看启动迁徙之初的服务端实例用量,有赖于日常运维,首先确认在线服务的实例 cpu、内存和磁盘使用率在正当的阈值内,排除了利用率较低导致资源节约、利用率过高会有容灾危险的状况。在应用层,咱们一共应用了数以千计的 php5 实例。
03 近景
重构的投入与回报并非呈线性关系。
——《畛域驱动设计:软件外围复杂性应答之道》
直观的说,咱们心愿服务端降级能带来更少的代码,更稳固的零碎,更高的品质效率,更佳的用户体验。这体现在上面几点:
1、技术升级:采纳先进的语言框架,撑持我的项目高效迭代提供强劲底层引擎、安全性和成熟的利用生态;
2、改善设计:梳理代码逻辑,治理冗余,解决代码中的坏滋味,构建高复用、低耦合、可扩大的业务架构;
3、降本增效:一方面底座降级,晋升代码执行效率,升高平响,晋升服务可用性、可观测性;一方面在运维实际上,正当调配容器实例的 cpu,内存和磁盘的配额,优化资源效力。
服务端降级的胜利与否,能够从两个方面来致力达成,别离是 技术栈迁徙 和改善既有代码设计的重构。
04 做技术选型
咱们不打算应用较为小众、生态孤立的语言作为文库 App 服务端的技术栈。同时参考兄弟团队的技术栈降级方向,最终进入技术选型决赛圈的是两种厂内框架,基于 php7 的 odp3 框架(Online Develop Platform)和基于 go 的 gdp2 框架(Go Develop Platform)。
选项一:PHP7 框架和 Phaster
PHP7 框架是公司公布的在线业务开发平台,其提供了规范的 webserver 环境、规范 php 环境、AP 框架、根底库、资源拜访层、通用服务等组件,对立业务的逻辑和部署构造。框架的亮点在于 Phaster。Phaster 能让你应用 PHP 语言开发高性能的 Http、Fastcgi、Nshead 服务,进行高性能的 RPC 调用,以极低的老本实现业务代码并行化。
Phaster 和其它业界框架的比照如下。
Phaster 能够作为 http server,也能够作为 fastcgi server。绝对传统 nginx+cgi 的形式,Phaster 基于以上的能力实现数倍的性能晋升。具备以下亮点:
1、传统的 hhvm 或者 php-fpm 解决申请的逻辑是,每一个申请在解决时,都要先初始化 php 上下文,申请完结时清理上下文,回收各种资源。而 phaster 在开启上下文复用的状况下,能够节俭类加载,文件加载,初始化等过程消耗的工夫。举个例子,如果你的接口每次都要读取一个大文件配置,能够把读取操作放到初始化文件里。在 100 个申请内,这个读取操作只执行一次就够了;
2、hhvm 或 php-fpm 并不间接反对 http 协定,往往后面会加上 nginx 作为 http 服务器,两者之间通过 fastcgi 通信。而 Phaster 能够间接作为 http 服务器启动,缩小一层 nginx 的解决转发;
3、协程的反对为 IO 密集型的业务场景,提供了高并发的根底。对于阻塞性的 IO,能够放入协程里做,将阻塞变为非阻塞,在应用同步编程计划的同时,享受异步成果带来的 IO 性能晋升。
值得一提的是,Go 都反对这些能力。
选项二:Go 框架
Go 语言是由 Google 于 2009 年公布,近几年随同着云计算、微服务、分布式的倒退而迅速崛起,跻身支流编程语言之列,和 Java 相似,它是一门动态的、强类型的、编译型编程语言,为并发而生,所以天生实用于并发编程(网络编程)。
GDP2(Go Develop Platform)框架是一个对厂内基础设施反对好,可扩展性好、易配置、易组装、易测试的 Go 开发框架。具备欠缺的 RPC Client 和 RPC Server 能力,以及配套的通用根底库,能够用来开发 API、Web 及后端服务等各种利用。具备以下亮点:
1、对厂内基础设施反对好;
2、可扩展性好、易配置、易组装;
3、易用性好、对测试敌对 (易 mock,多种 testServer、testClient);
4、组件外部状态易察看;
5、全链路超时 & 流程管制机制,稳定性好;
6、厂内大规模利用,稳固牢靠(根本所有 Go 我的项目都有应用,共有几千我的项目应用)。
综合比照以上对两种框架的特点和落地的可行性等因素,最终咱们更偏向于向 GDP 框架 迁徙。
05 进行重构的要害门路
做好技术选型后,咱们就开始下一步的工作。和常见的 web 我的项目一样,文库 App 业务迭代速度快、工作重,难以保障有短缺的人力长期投入到技术我的项目。所以,技术栈降级重构的前提是在保障业务需要不停的状况下进行,须要有继续重构的意识,往往采纳『麻利式迭代』。
5.1 麻利式迭代
第一步,工作量预估。通过日志聚合剖析,得出当下有流量的 App 接口路由(老我的项目很多接口没有流量,关联需要已下线)。实际操作下,发现刚好依照 qps 值从大到小排序的 top 50 的接口的流量占比达到了总流量的 99%+,这也确定了接口迁徙程序的优先级。
第二步,制订策略。服务端技术栈 go 迁徙的落地,实质上体现为由 php 承接的流量转为 go 承接,当所有流量都在 go 实例上运行,且对 php 我的项目无底层调用的依赖关系,即可认为降级实现。因而,不断扩大 go 实例集群在 App 服务端总的流量占比,就是咱们迁徙的工作指标。以此能够大略能够总结为两种形式:
1、依据业务需要,联合接口重要性和流量占比确定优先级,进行迁徙;
2、新需要的代码实现和 php 我的项目不存在强依赖关系,间接在 go 我的项目开发。
有相当长的一段时间是处于 php+go 进行混合编程的共存状态。因为 App 的 B / S 架构个性,重构完的接口须要通过接入层网关做代理转发,切换服务端承接流量的具体应用层集群(php->go),让客户端放弃 path 不变,从而实现 App 老版本的高可用。采取混合编程的思路在重构初期,可能会一些比拟非凡的需要状况,比方:同一段业务逻辑,须要用 go 写一遍,用 php 写一遍,无疑减少了肯定的工作量,当然这也是防止不了的。
在重构的时候防止走到一个误区:瀑布模型,一口气把整个我的项目都重构了。从工夫、人力老本和稳定性上来讲,这种形式危险比拟大,不举荐。综合来看接口粒度的分批进行重构,这样不论是内化的技术迭代,还是外化的业务影响,都是有显著感知的。用作实现流量迁徙的形式更为适合。
5.2 配套 golang 的根底建设
区别于 php 我的项目的 work flow,有以下几点不同。
1、脚手架:除了定义路由、逻辑分层、生成配置等框架属性外,go 须要额定对协程进行封装,提供给研发同学一个开箱即用的脚手架。
2、公布:封装 build 逻辑,实现打包编译、环境变量治理。
3、部署:是整个二进制文件笼罩,须要重启服务,应用热重启模块,能够实现无损上线,以及更快的上线速度。
4、流量:CS 架构的 App 的接口迁徙须要接入层做路由重写,协调网关变更。
5、监控:日志分级,微服务间放弃 trace 透传,各类日志落盘合乎 agent 采集的格局标准。
5.3 写第一个接口
一方面,在开始技术栈迁徙的时候,须要理解到 go 语言层面反对并发,能够很轻松的开发异步程序强类型语言。go 是强类型的动态语言,编译时确定类型,不如 PHP 灵便,然而更谨严,更平安,能够在编译阶段查看进去暗藏的绝大多数问题。
△类型转换
另一方面,重构我的项目如何治理古老代码?概括的说,能够参考《重构 - 改善既有代码的设计》一书提出的 23 种代码坏滋味,有针对性地对代码进行重构,驯服成整洁和易于浏览的代码。
把后期调研和迁徙策略确定好了,理论的代码开发变得得心应手。在迁徙老接口流量的时候,咱们须要在新接口用 go 从新实现一遍,调用形式上齐全等同老接口,包含 path、method、验签、header 规定、参数构造、响应构造、错误码。只有应用层上的虚构域名不同。
5.4 品质保障
代码 ready 了,区别于 php 我的项目的惯例测试流程,go 不能绕过性能测试。因为咱们写 php 简直不须要关注 GC 和内存泄露,然而 go 须要,有时候手动测试和黑盒测试是 OK 的,然而到线上遇到有肯定并发的业务场景,就会裸露问题,经常体现为实例的 cpu 或者内存利用率继续上涨,直至宕机。
应答 go 的内存泄露问题。一方面须要在测试流程中减少压测环节;一方面须要日常多关注一下监控仪表盘的实例资源利用率、接口平响、稳定性指标是否合乎预期,因为有的暗藏 bug 即便压测也不能笼罩到。这时须要晋升 go 服务的可观测性,以便及时发现危险。
Go 品质保障能力全景矩阵如下:
构建线下品质保障能力:
构建线上品质保障能力:
5.5 流量迁徙
如上图所示,go 我的项目上线后,理论流量还在老我的项目承接。开始做流量迁徙,用户流量首先达到接入层,在这一层咱们依据不同的拜访域名和路由,分流到不同的应用层 load balancer,为了兼容老版本的 App,须要在域名路由不变更的状况下,实现流量迁徙。在接入层网关做分流,把分流到 php 的规定利用到 go 应用层 load balancer 上,就实现了流量迁徙。留神,如果是十分外围的接口,咱们须要进行灰度公布,能够采纳 nginx+lua 的形式实现,或者采纳驰名的开源网关 ApiSix、BFE 我的项目,它们都反对灰度公布。
5.6 外围性能重构实际
这次重构比较突出的亮点,体现在百度文库 App 的全新首页和搜寻后果页优化。
(1)定制化新首页
文库用户个性化需要较扩散,心愿通过将垂类用户内容需要共性形象,对连续型特色且应用较高的内容进行提取,采纳中心化集中举荐的形式,进步用户垂类内容结构化满足,进而晋升用户留存率及续费志愿。重构了 App 首页的布局和内容展现。减少了个性化的『我的资料库』,『教学进度』,『举荐频道』,定制化展示文档榜单和文件夹榜单。
App 新首页的技术计划是全新的,重构的动机来自 ” 业务驱动 ”,而非 ” 品质驱动 ”。需要实现上,底层不依赖 php 老我的项目。所以间接在 go 我的项目开发上线,提供接口服务。这样上线后,go 天然替换掉了 php 本来承接的首页流量。
△文库新首页
(2)搜寻后果页优化
服务端这边次要重构对象是一个搜寻接口,理论开发中,和产品沟通是否能够下线不要的 tab 列表和内化的举荐逻辑;清理多处曾经下线的 AB 试验的业务逻辑,去掉曾经推全 AB 试验的代码判断;优化文档排序算法,和产品、前端同学对齐以后必须的字段,去除冗余;善用协程优化串行逻辑。
联合前端去除懒加载代码,图片本地化,应用端能力缓存接口数据,搭建离线包服务等技术手段,搜寻后果页优化获得了不错的成绩。大幅升高搜寻后果页的加载速度,安卓均匀升高提早 41%;IOS 均匀升高提早 43%,搜寻后果点击率和成交的订单量也有肯定晋升。
△【新老搜寻后果页】AB 试验时的白屏时长统计
06 指标达成
从 2022 年 8 月启动 go 迁徙至今,靠近实现 App 服务端的全副流量迁徙工作。
1、技术迭代:得益于 go 语言个性先进、内存治理和丰盛的生态,晋升了代码执行效率、安全性和可观测性;通过梳理业务逻辑,治理冗余,清理代码中的坏滋味,封装公共类等办法,晋升品质效率,代码可读性和可维护性;
2、晋升性能:一方面通过协程、通道技术扭转同步阻塞的代码执行形式;另一方面,编译后的二进制文件执行效率远高于 nginx+php-cgi 的网络模型。均匀缩小了约 30% 的接口耗时,TP90 缩小了 35% 的耗时;
3、降本增效:得益于 go 语言高性能的个性,应用层实例的负载能力失去晋升。流量迁徙后,文库 App 服务端在线集群缩减了约 50% 的的实例数量。
07 思考与总结
1、手机 App 属于 CS 架构的利用,在迁徙过程中要保障老版本 Client 能够应用服务;
2、在面对一个长期我的项目时,拆解指标是很重要的,慢步试错即时反馈也是互联网思维的一部分;
3、迁徙实践无损,但须要把危险同步 pm 同学,及时关注各业务指标,同时制订预案,保障可回滚的灵活性;
4、接口刚上线或者 AB 试验推全后,迁徙的接口流量回升,要养成常常察看可用性仪表盘的习惯,及时处理 http status 异样的问题,防止危险扩大化为故障。
08 结语
知而不行,是为不知;行而不知,能够致知。
回忆我的项目迁徙重构的整个过程,最有意思的是做技术选型和探讨流量迁徙具体履行计划的起步阶段,那时面对臃肿宏大的 php 单体我的项目如何进行迁徙,是有些迷茫的。在实际的摸索过程中逐步加深对我的项目的了解,通过所得的启发来推导制订下一步的口头,造成正向循环。心愿本文的内容对大家的工作实际有所帮忙。
——END——
举荐浏览:
扫光动效在挪动端利用实际
Android SDK 平安加固问题与剖析
搜寻语义模型的大规模量化实际
如何设计一个高效的分布式日志服务平台
视频与图片检索中的多模态语义匹配模型:原理、启发、利用与瞻望
百度离线资源治理