作者 | 百度文库App

导读

本文浅显易懂地共享了百度文库App服务端技能栈从PHP搬迁至Go的实战经验,包含了技能选型、根底建设、流量搬迁的详细计划,以及中心项目事例的重构实践。

全文6209字,预计阅览时刻16分钟。

01 动机

长时刻以来,百度文库App服务端选用 PHP 作为首要开发言语,高效地支撑了事务迭代开展。随着渠道流量的继续增长,服务端的负载越来越大逐渐挨近体系瓶颈。为了提高体系的负载才能,咱们采取了一些优化手段,其间最快最有用的办法是增加在线集群的实例数量。此外,还选用过lua开发项目,接受一些逻辑简单而拜访量大的接口来分管负载。由于lua本身的一些局限性,不适合做杂乱的事务逻辑。

伴随着IT技能的开展潮流,咱们积极呼应公司降本增效的召唤,决定在2022年年中搬迁并重构服务端技能栈。旨在晋级技能架构,提高体系负载才能。

掌握技能栈搬迁和项目重构的机遇是很难的一件事情,特别是老练的团队要进行大的体系改动。假如没有呈现真正的痛点,即使研发同学以为技能完结上现已呈现许多规划不合理和有危险的地方,往往并不被答应花许多时刻去做技能项目。可一旦连事务人员(产品经理、出售、运营)也觉得体系功能需求晋级的时分,比方在用户体会上,App文档查找接口推迟比较长,产品同学以为假如首屏渲染能明显提速的话,对点击率、付费率都会有大幅的提高,可是研发这边根据老的技能栈现已难以做优化了,那么此时就很适合做搬迁重构。

恰逢其时,产品同学提出一些提高用户体会,一起适合项目重构的需求,比方:加速查找成果页首屏渲染、新App主页,AIGC智能创作。这些大需求十分有利于搬迁重构工作的发动,它们把搬迁重构所需增加的额外人力占用降到最低。在已有的功能上做搬迁重构,更快更安稳的接口呼应带来流通的用户体会,这有利于促进全体团队的okr方针达到。

回过头来,整理下其时服务端根据php5.6的技能债款:

1、底层技能:言语版别老旧,特性落后,存在履行功率低,安全危险,资源浪费的缺陷;

2、开发质效:事务逻辑交叉耦合,许多废弃的接口和下线的事务逻辑,降低了代码可读性、可保护性,继续增加项目迭代的难度。

从php5.6到golang1.19-文库App性能跃迁之路

△技能债款

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)。

从php5.6到golang1.19-文库App性能跃迁之路

选项一:PHP7结构和Phaster

PHP7结构是公司发布的在线事务开发渠道,其供给了标准的webserver环境、标准php环境、AP结构、根底库、资源拜访层、通用服务等组件,统一事务的逻辑和布置结构。结构的亮点在于Phaster。Phaster能让你运用PHP言语开发高性能的Http、Fastcgi、Nshead服务,进行高性能的RPC调用,以极低的成本完结事务代码并行化。

Phaster和其它业界结构的对比方下。

从php5.6到golang1.19-文库App性能跃迁之路

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灵敏,可是更严谨,更安全,能够在编译阶段查看出来隐藏的绝大多数问题。

从php5.6到golang1.19-文库App性能跃迁之路

△类型转化

另一方面,重构项目怎么管理陈旧代码?概括的说,能够参考《重构-改进既有代码的规划》一书提出的 23 种代码坏滋味,有针对性地对代码进行重构,驯服成整洁和易于阅览的代码。

从php5.6到golang1.19-文库App性能跃迁之路

把前期调研和搬迁战略确认好了,实践的代码开发变得得心应手。在搬迁老接口流量的时分,咱们需求在新接口用go重新完结一遍,调用办法上彻底等同老接口,包括path、method、验签、header规则、参数结构、呼应结构、错误码。只要运用层上的虚拟域名不同。

5.4质量确保

代码ready了,区别于php项目的惯例测验流程,go不能绕过性能测验。由于咱们写php简直不需求重视GC和内存走漏,可是go需求,有时分手动测验和黑盒测验是OK的,可是到线上遇到有一定并发的事务场景,就会暴露问题,常常表现为实例的cpu或许内存利用率继续上涨,直至宕机。

应对go的内存走漏问题。一方面需求在测验流程中增加压测环节;一方面需求日常多重视一下监控仪表盘的实例资源利用率、接口平响、安稳性方针是否符合预期,由于有的隐藏bug即使压测也不能覆盖到。这时需求提高go服务的可观测性,以便及时发现危险。

Go质量确保才能全景矩阵如下:

从php5.6到golang1.19-文库App性能跃迁之路

构建线下质量确保才能:

从php5.6到golang1.19-文库App性能跃迁之路

构建线上质量确保才能:

从php5.6到golang1.19-文库App性能跃迁之路

5.5 流量搬迁

从php5.6到golang1.19-文库App性能跃迁之路

如上图所示,go项目上线后,实践流量还在老项目接受。开端做流量搬迁,用户流量首要到达接入层,在这一层咱们根据不同的拜访域名和路由,分流到不同的运用层load balancer ,为了兼容老版别的App,需求在域名路由不改变的状况下,完结流量搬迁。在接入层网关做分流,把分流到php的规则运用到go运用层load balancer 上,就完结了流量搬迁。留意,假如是非常中心的接口,咱们需求进行灰度发布,能够选用nginx+lua的办法完结,或许选用闻名的开源网关ApiSix、BFE项目,它们都支撑灰度发布。

5.6 中心功能重构实践

这次重构比较突出的亮点,体现在百度文库App的全新主页和查找成果页优化。

(1)定制化新主页
文库用户个性化需求较涣散,期望经过将垂类用户内容需求共性抽象,对接连型特征且运用较高的内容进行提取,选用中心化会集引荐的办法,提高用户垂类内容结构化满足,从而提高用户留存率及续费志愿。重构了App主页的布局和内容展示。增加了个性化的『我的资料库』,『教学进度』,『引荐频道』,定制化展示文档榜单和文件夹榜单。

App新主页的技能计划是全新的,重构的动机来自”事务驱动”,而非”质量驱动”。需求完结上,底层不依赖php老项目。所以直接在go项目开发上线,供给接口服务。这样上线后,go天然替换掉了php本来接受的主页流量。

从php5.6到golang1.19-文库App性能跃迁之路

△文库新主页

(2)查找成果页优化

服务端这边首要重构对象是一个查找接口,实践开发中,和产品沟通是否能够下线不要的tab列表和内化的引荐逻辑;整理多处现已下线的AB试验的事务逻辑,去掉现已推全AB试验的代码判断;优化文档排序算法,和产品、前端同学对齐当前有必要的字段,去除冗余;善用协程优化串行逻辑。

结合前端去除懒加载代码,图片本地化,运用端才能缓存接口数据,建立离线包服务等技能手段,查找成果页优化取得了不错的成果。大幅降低查找成果页的加载速度,安卓平均降低推迟41%;IOS平均降低推迟43% ,查找成果点击率和成交的订单量也有一定提高。

从php5.6到golang1.19-文库App性能跃迁之路

△【新老查找成果页】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安全加固问题与剖析

查找语义模型的大规模量化实践

怎么规划一个高效的分布式日志服务渠道

视频与图片检索中的多模态语义匹配模型:原理、启示、运用与展望

百度离线资源管理