Laravel 项目深度优化过程

简述

公司的系统是面向商户营销的CRM系统,采用的是saas模式而非独立部署,用户表200W,日活3W,日增长5000+,每天的请求量总量目前是200多W次,其中服务端的请求量主要来自前端API请求+商户ERP数据同歩+微信事件回调。以下均在抛去系统设计的问题不讨论,小公司的产品活下去才是第一要素,只能现有的情况下尽量优化,一步步摸着石头过河。

以下均由pinkr公司同事合作完成
@will_lin
@bin

服务配置:

  1. 单台阿里云ECS服务器 - 12核24G (redis/nginx/php7.1/laravel5.7)

  2. 单台阿里云RDS数据库 - 2核4G (mysql)

  3. Supervisor开多个进程跑队列任务

  4. php的woker进程设为100个

  5. 跟据laravel优化文档做了优化https://learnku.com/articles/2020/ten-laravel-5-program-optimization-techniques
    Laravel 项目深度优化过程

  6. php-fpm实际并发如下(siege压测-2核4G测试环境 ):

  7. 大部分简单逻辑接口并发300QPS

  8. 数据库操作多的接口并发70QPS

  9. 全查缓存接口接口并发550QPS

存在问题

当三个十万粉丝的商家同时发推文和多个商家线下门店做促销活动时,一秒点击人数过多或线下ERP消费小票、积分回传过多,服务器就会崩溃掉,服务器崩溃的时间如果超过几分钟不能响应接口,就会触发微信事件的3次重推机制和ERP回传5次重推机制,触发重推机制的情况下我们是可以关闭erp的回传,但无法停止80家商户的微信事件重推,这个时侯系统一般就会瘫痪1个多小时,瘫痪结束后又会面临数据找回的问题

一阶段方案

引入Laravel-S,利用swoole的长驻内存的机制, 常驻内存后每个请求减少了一大堆的初始化开销,能一定程度的增加并发数,实测要连接数据库的接口中的并发数提升了35% ~ 50%,不连接数据库的接口中的并发数提升400% ~ 500%,整体的请求失败率大幅度降低,正式环境下每天200多W个请求整体是正常的,没有发现有内存泄漏,在整体修改不大的情况下得到这个提升是很值得满意的。在此感谢Laravel-S作者和swoole社区,以下是Laravel-S作者对数据库连接提升不大的解释

Laravel 项目深度优化过程

laravel-s文档
https://github.com/hhxsv5/laravel-s

在此swoole实际并发如下(siege压测-2核4G测试环境):

  1. 大部分简单逻辑接口600QPS
  2. 数据库操作多的接口并发96QPS
  3. 全查缓存接口接口并发2000QPS

Laravel 项目深度优化过程

引入心得

  • 任务队列和定时任务是可以直接用laravel原生的,这部份还是用php-fpm跑,不用过多改动,这样引入的过程中只用去改控制器上的业务代码,当然也可以把定时任务也写到swoole中,这样就可能得到毫秒级的定时任务。项目整体的运行模式为swoole(业务代码)+php-fpm(任务队列和定时任务)
  • 控制器的业务逻辑中不要用$this来保存和调用变量,laravel下这一部份会被处理为单例模式的,Swoole Server下,所有单例对象会常驻于内存,这个时候单例对象的生命周期与FPM不同,单例对象依在请求结束后不会被清除,需要开发者自己维护单例的状态
  • 认真的看文档的注意事项,文档能解决90%的问题,不能解决的加群或提issue

二阶段方案

在swoole的基础上引入负载均衡做服务器集群和分离项目内的模块(独立ERP模块、微信模块),用堆机器来提高总的并发数

负载均衡存在问题

  1. 文件如何同步 - 已解决,大部分文件已上云,小部分用nginx转发到特定服务器
  2. 日志如何收集 - 未解决
  3. session、缓存如何同步 - 已解决,全局切换redis
  4. 代码如何自动更新 - 未解决,手动更新多台服务器

分离模块存在问题

  1. 模型、队列、事件监听器高度藕合如何分离 - 半解决,多个服务器代码相同,只是不同业务访问特定服务器

以上的问题附了代码如何自动更新外,其实都可以在项目开始前设计好,希望各位如果开新项目不妨直接考虑上负载均衡会遇到的问题,尽量文件上云,尽量不要用本地文件缓存,尽量在一开始分离模块。

后续

3台机器搭好了负载均衡,同时代码层和业务层也做了很多的优化,能加缓存接口的加缓存,能走异步的不走同步,在全解决二阶段方案现有的问题后,相信在现有的下架构是可以撑半年时间来开发业务的了。
到此,laravel的优化应该是到头了,后面就是数据库的优化、引入mq队列、引入Elasticsearch、用协程的swoole框架如hyperf、easyswoole之类的重构,这类型的框架都相对应的提供了微服务的使用。

在12月在深圳开源中国开发者大会上听了韩天峰对php的分享,也听了下午的架构专场,目前看来在对于中大型的项目上,php的生态是远远比不上java的,像Sharding-JDBC的分布式数据库、Spring Cloud的微服务实现,php在这方面上还是很初级,希望以后在面向中大型项目上的生态能更好吧,在小型项目上php已经十分优秀了。

技术问题交流

关于负载均衡的问题上,如何更自动化地部署代码和日志收集?各位真实解决方案是如何实现的?在这抛砖引玉一下,大家有好的解决方案望在下方评论

后续更新细节

更新中~

优秀文案推荐

mysql的事务、索引所产生死锁的概率是很大的,下面的文案有详细解说
一张图彻底搞懂 MySQL 的锁机制

本作品采用《CC 协议》,转载必须注明作者和本文链接

未经允许禁止转载 -- 苦力小林,

本帖由 Summer 于 2周前 加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
讨论数量: 11

代码部署方案应该很多吧,自己可以搭建jenkins,syncd或者用云服务商的持续集成,但是原理都是差不多的;日志我们使用的自建ELK,也可以使用阿里云的日志服务,挺方便的

1个月前 评论
Double-Jin (楼主) 1个月前
oidns 1个月前

超链貌似有问题

1个月前 评论
Double-Jin (楼主) 1个月前

多机部署,简单的话,好像有个叫 deployer 的 php 插件,部署都不用进远程

1个月前 评论
Double-Jin (楼主) 1个月前

Supervisor 跑着正常吗?我用着总是死表,不知道怎么解决

1个月前 评论
Double-Jin (楼主) 1个月前
609468798 (作者) 1个月前
_null_ 1个月前

General error: 2006 MySQL server has gone away 错误有可能是MySQL服务端超时主动关闭长连接导致的,可以往这个方向排查

1个月前 评论

12 核 24G建议换成多台4核4G试试

1个月前 评论

你用laravel-s是不是没打开Mysql长连接选项

1个月前 评论
Double-Jin (楼主) 1个月前

General error: 2006 MySQL server has gone away 貌似是mysql没有断线重连,常驻进程的都有这个毛病好像
搞个断线重连机制

1个月前 评论
Double-Jin (楼主) 1个月前

可以直接上阿里云的kubernetes (serverless) 服务

1个月前 评论

不要单机部署,你可以考虑 2核4G作为主机,前面套一程负载均衡。你的12核24G可以分成6台ECS,然后给这6台分组,每组3台。负载均衡中的7层负载中吧特定的URL导向A组,其他导向B组。然后弹性伸缩1台。这一台备用,也是母鸡,用来操作和更新软件,更新完打包成ecs镜像,然后滚动升级替换到其他的ECS。这样你特定的功能即使由于性能全部导向A组ECS,相当于你B组来负载核心业务,至少不让他挂了。

2周前 评论
Double-Jin (楼主) 1周前
terranc 1周前
terranc 1周前
Double-Jin (楼主) 1周前
terranc 1周前
方圆百里找对手 (作者) 1周前

优秀的文章。

Redis 可以把很多接口缓存化,ElasticSearch 可以大幅提高搜索速度:例如某商品的关联商品。

架构上还是尽量单机,可以先尝试对 web server 和数据库提高配置,能解决大部分性能问题。负载均衡是会让不可用时间上升一个数量级的,能不用就不用,300W 这个量级还行,用好缓存的话单机没问题的。

5天前 评论
Double-Jin (楼主) 5天前

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!