找回密码
 会员注册
查看: 14|回复: 0

PHP配置化读取优化方案

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
65944
发表于 2024-10-9 17:32:55 | 显示全部楼层 |阅读模式
PHP配置化读取优化方案 PHP配置化读取优化方案 卢阳@贝壳找房 贝壳产品技术 贝壳产品技术 “贝壳产品技术公众号”作为贝壳官方产品技术号,致力打造贝壳产品、技术干货分享平台,面向互联网/O2O开发/产品从业者,每周推送优质产品技术文章、技术沙龙活动及招聘信息等。欢迎大家关注我们。 242篇内容 2021年01月06日 15:58 1背景随着贝壳开通城市的增多,在贝壳app中每个城市会启用不同的功能,或同种功能表现出不同的交互方式。为了配置这么多城市和功能的组合,并能动态的修改配置,选用了apollo(携程框架部门研发的分布式配置中心)来记录和维护这些配置。随着配置数据增多,当数据较大时,读取数据成了负担,会消耗较多的CPU和内存的资源。当前开通城市例如1000左右,每个城市的配置项有几十个项,每次请求需要读取如此大的一个配置数据,在极端情况下,例如并发较多时,首先会大量开销服务器资源,严重时会耗尽资源,其次会导致处理的时间变长,有请求挤占了连接一部分请求没能处理,导致整个链路的处理时间变长,最终导致一定时间服务不可用。能否有效的降低这种资源开销,处理掉这个读取配置的瓶颈,保障服务的正常运行?2分析配置数据和使用的整个过程即:图:配置数据读取和使用过程为了处理读取配置的性能问题,可以从以下环节来入手:避免多次重复读取和做最小的处理读取的速度提升解决多次重复读取和处理:在一次请求中,对配置做一次读取和解析并存储,在后续的执行过程中都使用这个存储的配置来完成执行的逻辑。解决最小处理问题:读取配置后可以只处理需要的数据,不需要的数据可以不做处理,这样可以得到需要使用的最小配置。解决访问读取速度的问题:这里已经使用了apollo + agent 读取的方式,读取的是本地内存,这个提升空间不大。但是可以对数据格式选择可以使用更小的格式,或者是使用更高压缩比的方式。3优化方案实施3.1 原先场景3.1.1 原先场景描述存储在Apollo中原始的数据为json_encode以下数组之后的字符串。由于apollo的数据存储用的是mysql,对每个value存储有长度限制,所以不能全部序列化当做一个字符串存储。这个数据定义为:二维数组,第一维度为每个city_id的key value 结构,value为一个json_encode之后的字符串,第二维度为city的各个属性。如此有1000个城市。3.1.2 原先代码问题由于历史原因等,之前的使用配置的方式较为简单粗暴:首先,将读取city config封装为一个function,function中先对这个二维数组做json_decode,对第二维度循环做json_decode;其次,在每个使到的地方直接调用这个function。这样会造成每个使用到的地方,都会去apollo读取配置,循环json_decode;最终在一次访问量较大的情况下,部分机器由于这里的资源消耗占满了cpu,导致一段时间服务不可用;3.2 下载及安装xhprof为了更直观的看清性能的比对,我们需要先行安装xhprof。参考:https://github.com/longxinH/xhprof3.3 避免重复解析3.3.1 使用静态变量存储配置场景构造:FuncA使用了读取apollo和解析其读取的配置,并调用了FuncB,FuncB中也使用了读取apollo和解析其读取的配置,同时也调用了FuncC,FuncC中也是用了了读取apollo和解析其读取的配置。图:FuncA FuncB FuncC调用关系比对数据如下:Advantages:同种配置只会读取一次和在首次读取后的json_decode执行完就可以在随后的调用中直接读取static 变量,省去了再次读取apollo和json_decode的过程。Disadvantage:读取的配置在本次请求的生命周期内不会更新,如果本次请求的生命周期较长,比如超过1min ,或者使用脚本执行较长时间的不会读取到新的配置。这里需要给出一个解决方法,比如可以在一个长时间的执行脚本中,这种脚本往往不考虑性能,执行起来是隔离的。每次都拉取新的配置来执行逻辑,避免由于配置更新了但并不被使用的场景。3.3.2 最小json_decode在完全解析这个json数据需要做K+1次json_decode,K为一维数组的个数。但实际场景不是每个一维数组都在被使用。如果只有第二维度的数组被消费了,只需要解析2次。比对数据如下:代码调整如下:App\Library\Apollo\ApolloHelperpublicstaticfunctiondecode($string,$key=null){$data=[];$list=json_decode($string,true);//keyisnullreturnallif(empty($key)){foreach($listas$k=>$v){$data[$k]=json_decode($v,true);}return$data;}//keyisnotnullonlyonlyreturnkeypointedjsondataif(!empty($list[$key])){$data[$key]=json_decode($list[$key],true);}return$data;}以上代码所示,会将目标city_id传入,只对这个目标key 做第二维度的解析。Advantages:最少decode相比与原有decode 方式只针对目标key 做二级数组的decode,并返回目标key的数据。Disadvantages:在目标key和整体数量相当的时候整个办法性能和原有等同。3.4 降低解析成本3.4.1 使用yac或 apcu 作为本地存储能否从本地存储的角度来解决读取的效率问题,于是可以尝试yac和apcu。yac:Yac (Yet Another Cache) - 无锁共享内存Cache安装yac:https://github.com/laruence/yac为了使多核提升读写效率,使用了不加锁的读,避免了加锁带来的竞争和效率的降低。效率相当于本地的memcached。图:Yac无锁读取和有锁读取比对不加锁的读, 拿到以后做数据校验, 如果校验成功, 增说明查询成功, 否则就认为查询失败, 这是一种常用的采用CPU来换锁的方法. 对于目前的服务器, 大部分都是多核的, 如果是加锁, 那么对CPU是一个极大的浪费。apcu:https://github.com/krakjoe/apcuAPCU的前身是APC(Alternative PHP Cache),APC的主要用途有两项:将PHP代码编译之后所产生的bytecode暂存在共享内存内供重复使用,以提升应用的运行效率。(Opcode Cache)提供用户数据缓存功能。(User Data Cache)注:它的作用和redis和memcached重合,测试表明, APC的User Data Cache的效率和本地memcached几乎相当(在单机性能上,APCu通常比Memcached更高。memcached本身的设计就是为了分布式应用,大规模内存缓存,集群,易扩展等,如果只有一台服务器且内存足够缓存用户数据时推荐apcu)。安装apcu:https://github.com/krakjoe/apcu/blob/master/INSTALL图:yac/apcu 与 apollo集成读取配置流程yac / apcu 中的数据应该如何更新,可以在启动一个常驻进程的脚本,订阅apollo agent的拉取事件,当apollo agent 拉取完成后,将配置更新到yac / apcu中。比对数据如下:Advantages:yac 和 apcu在内存使用上略小于与 apollo + json,执行时间只有其方式的约四分之一。Disadvantages:引入了扩展和扩展数据管理的环节,增加了同步数据的常驻进程脚本,需要对该做监控,如果这里出问题,会带来程序的问题。3.4.2 逆向工程生成数组配置实现流程:首先,判断需要读取的文件是否存在,如果存在则读取,如果不存在则拉取。其次,读取的时候判断是否过期,过期也重新拉取。再次,拉取的时候使用php var_export方法写入文件,同时会写入文件的过期是时间。最后,如此往复。图:require file组合apollo读取数据方式流程比对数据如下:Advantages:使用require file 的方式并使用了opcache来提升读取配置的过程,读取配置效率是有了较大提升。Disadvantages:增加了维护file的环节。注:这里如果使用submodule 的方式来读取配置文件,优势:效率等同于require file的方式;劣势:实时性较差,更新需要更新submodule的代码和重新部署。3.5 其它格式3.5.1 使用message pack格式存储和解析message pack 介绍:MessagePack is an efficient binary serialization format. It lets you exchange data among multiple languages like JSON. But it's faster and smaller. Small integers are encoded into a single byte, and typical short strings require only one extra byte in addition to the strings themselves.下载及安装地址:https://github.com/msgpack/msgpack-php原理:1)true、false 之类的:这些太简单了,直接给1个字节,(0xc3 表示true,0xc2表示false)2)不用表示长度的:就是数字之类的,他们天然是定长的,是用一个字节表示后面的内容是什么,比如用(0xcc 表示这后面,是个uint 8,用oxcd表示后面是个uint 16,用 0xca 表示后面的是个float 32)。对于数字做了进一步的压缩处理,根据大小选择用更少的字节进行存储,比如一个长度
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-31 03:50 , Processed in 0.498571 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表