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

news2024/12/25 13:59:47

在这里插入图片描述

作者 | 百度文库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安全加固问题与分析

搜索语义模型的大规模量化实践

如何设计一个高效的分布式日志服务平台

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

百度离线资源治理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/723464.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

异地使用PLSQL远程连接访问Oracle数据库【内网穿透】

文章目录 前言1. 数据库搭建2. 内网穿透2.1 安装cpolar内网穿透2.2 创建隧道映射 3. 公网远程访问4. 配置固定TCP端口地址4.1 保留一个固定的公网TCP端口地址4.2 配置固定公网TCP端口地址4.3 测试使用固定TCP端口地址远程Oracle 转载自cpolar极点云文章:公网远程连接…

【036】读懂C++的强制类型转换static_cast、const_cast、dynamic_cast以及reinterpret_cast

C的强制类型转换 引言一、类型转换简介二、上行、下行转换的概述三、static_cast 静态类型转换四、dynamic_cast 静态类型转换(推荐使用)五、const_cast 常量转换六、reinterpret_cast 重新解释转换(最不安全)总结 引言 &#x1f…

微信支付(一):小程序支付(go+gin+内网穿透)

一、前置条件 (1)go语言,1.18 (2)Gin、第三方依赖包:gopay【github.com/go-pay/gopay/alipay】https://github.com/go-pay/gopay/blob/main/doc/wechat_v3.md (3)微信支付相关信息…

Ubuntu安装:显卡驱动、CUDA、Anaconda

Ubuntu安装:显卡驱动、CUDA、Anaconda 摘要1.安装NVIDIA显卡驱动2.安装CUDA3.安装Anaconda Windows环境安装CUDA和Pytorch见:Pytorch入门:3.安装 环境:x86_64 Linux ubuntu18 4.150.0-20-generic 摘要 本篇博客对Ubuntu系统安装…

#消防知识#自动灭火系统是什么?

自动灭火系统是指能够在发生火灾时自动检测、控制和扑灭火灾的系统,包括自动喷水灭火系统、气体灭火系统、干粉灭火系统、气溶胶灭火系统等。不同的自动灭火系统有不同的组成部件、工作原理和适用范围,以下是一些简要的介绍:• 自动喷水灭火系…

汽车远程升级(OTA)定义与技术体系

1.汽车OTA定义 1.1. OTA概述 OTA(Over-the-air technology)是一种通过无线方式而不是使用电缆或其他本地连接进行数据传输的远程升级。能够实现对现有性能/功能的优化、新功能推送等。OTA技术最早应用于PC,而后在手机上普及,终结…

Go语言程序设计(二)常量、变量、布尔类型与运算符

一、常量、变量与命名规则 常量使用关键字const声明;变量可以使用关键字var声明,也可以使用快捷变量声明语法。Go语言可以自动推断出所声明变量的类型,但是如果需要显式指定其类型也是合法的,比如声明一种与Go语言的常规推断不同的…

途乐证券|沪指缩量跌0.69%,汽车等板块走弱,稀土概念逆市活跃

6日早盘,三大股指股指盘中震动回落,科创50指数逆市上扬;两市半日成交超5000亿元,北向资金小幅净流出。 到午间收盘,沪指跌0.53%报3205.97点,深成指跌0.39%,创业板指跌0.61%,科创50指…

新版 Alist + RaiDrive 挂载阿里云盘 Open 到本地,实现网盘本地化

新版 Alist RaiDrive 挂载阿里云盘 Open 到本地,实现网盘本地化 1. 下载 Alist 和 RaiDrive2. 配置 Alist3. 填写挂载路径和刷新令牌4. 获取刷新令牌5. 使用 RaiDrive 挂载 1. 下载 Alist 和 RaiDrive 下载地址:Alist RaiDrive 安装 提取码: qu38 Al…

XXX SAP系统中支持XX动力替代件功能(投稿数字化月报三)

XXX集团、XX动力、XXX汽车IT组成替代件开发小组,快速高效完成XX动力替代件业务需求。提供从XX动力的PLM系统中开发新增含有替代件功能的BOM物料清单、在XXX汽车SAP系统中启用替代件功能、再到现场替换件业务执行的全套解决方案。 在BOM上体现出物料的替换关系&#…

汽车电子—制作DBC文件

文章目录 一、前期准备二、新建DBC文件三、添加报文(Msg)四、添加信号4.1 大小端模式4.2 创建数值表4.3 添加信号 五、关联信号和报文六、设置报文发送类型和周期七、编辑通信矩阵 一、前期准备 首先需要安装制作DBC文件的软件,这里使用的是…

计算机网络概述(二)

计算机网络的定义 计算机网络并没有一个统一的定义,不同阶段是有不同的定义。 最简单的定义:计算机网络是一些互联的,自治的计算机集合。互联:指计算机之间可以通过有线或无线的方式进行数据通信;自治指的是独…

MySQL_01删除主键索引

文章目录 添加主键索引删除主键索引 添加主键索引 alter table 表名 add primary key(字段名)删除主键索引 -- 如果这个主键是自增的,先取消自增长 alter table 表名 modify 字段 int; alter table 表名 drop primary key;

消息队列 原理剖析

什么是消息队列? ​​消息队列是一种用来存储消息的队列。 消息队列能用来做什么? 1、消息或任务的延迟处理。 2、在复杂的业务场景下给系统减压。 3、提示用户体验感,增强用户体验。 4、敏感操作高安全环境处理。 场景举例1&#xff1a…

途乐证券|股票低开好还是高开好?股票低开高走再回落什么意思?

对于一向不涨的股票,出资者常常感到困惑和不安。那么一向不涨的股票要留吗?什么股票更简单上涨?为我们预备了相关内容,以供参阅。 一向不涨的股票要留吗? 一向不涨的股票要不要留没有一个绝对的答案,出资者…

Vue2.0-3.0 入门到实战 - 2 初始及插件安装

1 Vue 指令 v- 前缀的 特殊标签属性 1 v-html"表达式" 可以解析 文本html 类似 innerHtml 2 v-show 与 v-if v-show"表达式" true 表示 显示 false 标识隐藏 v-if"表达式" true 表示 显示 false 标识隐藏 区别 当v-show 的值…

青岛大学_王卓老师【数据结构与算法】Week04_05_双向链表的删除_学习笔记

本文是个人学习笔记,素材来自青岛大学王卓老师的教学视频。 一方面用于学习记录与分享,另一方面是想让更多的人看到这么好的《数据结构与算法》的学习视频。 如有侵权,请留言作删文处理。 课程视频链接: 数据结构与算法基础–…

【CMake】构建类型详细解读

1. CMAKE_BUILD_TYPE官方资料 CMake可以配置构建类型,例如:Debug、Release等,控制生成构建系统使用的配置变量 是 CMAKE_BUILD_TYPE 。该变量默认为空,CMake识别的值为:Debug:用于在没有优化的情况下,使用带…

音频格式怎么转换成WAV?分享这三个WAV转换器!

随着数字时代的进步与发展,音频格式也变得越来越多样化。人们为了满足自己的需求,开始使用各种音频格式来存储和播放音乐。然而,不同的音频格式和不同的播放器之间常常存在兼容性问题,这就需要我们进行音频格式转换,以…

Spark SQL、DataFrame、DataSet是什么

在很多情况下,开发人员并不了解Scala语言,也不了解Spark常用的API,但又非常想要使用Spark框架提供的强大的数据分析能力。Spark的开发工程师们考虑到了这个问题,于是利用SQL语言的语法简洁、学习门槛低以及在编程语言中普及程度和…