nsq php,NSQ 最佳实践

目前,全新的异步任务服务每天高效稳定的为唱吧提供数亿次的调用。服务器团队用全新的方式重新定义了异步任务实现方式,以为云计算而生的NSQ、成熟的PHP执行者PHP-FPM、自主开发的中间件NSQProxy以及admin管理后台共同组成了异步任务的队列服务。

唱吧异步任务的前世

打开唱吧服务器代码,一股历史的厚重感扑面而来,“这块代码是历史原因”成了同学们的口头禅。

为什么要用异步任务

为了提高响应,减少用户等待,和线上用户无直接关系的代码挪到异步后台执行,这样可以让前台业务逻辑代码更加简洁,执行速度更快。比如:用户购买礼物时,余额检查、付款、礼物进入用户背包等逻辑放在同步执行,统计等放在异步执行。

流程

同步执行的代码将数据插入队列(MemcacheQ)

以crontab的方式在后台启进程,进程开头代码就是while(true)

从队列中去取出数据消费,然后sleep,一直死循环下去。

20180930150819_710.jpg

弊端

MemcacheQ远不如Nginx等足够成熟稳定,偶尔会莫名其妙的卡主自己好几秒。

消费者自由散漫,分散在各个机器上,开几个进程也都是随心所欲。

不支持订阅发布(推送),只能死循环里去get,get不到就sleep。出现了1次set对应136次get(135次get到空),白白浪费服务器资源。

消费者以死循环的方式常住内存,导致代码更新不及时,生成端的数据消费端不认识,必须上服务器手动kill进程,这期间会造成消费失败的数据丢失。

每一个环节都是单点,无法避免单点故障,坏一个洞全船都要沉。

鸟哥: 不要拿PHP做常驻内存的事情,因为我没给PHP写优雅的GC

唱吧异步任务的今身

NSQ替换MemcacheQ

Golang编写,云计算时代的产物,MQ领域的新星,为分布式消息队列而生,性能强劲,部署及其方便,二次开发难度低,有Go、Python、PHP等多语言客户端。NSQ相关不是重点,不在赘述。

NSQ优势

消息可靠性高:有ack/requeue机制。

磁盘落地:积压的消息可以磁盘落地。

扩展性:优势明显,新增节点极其方便。

易用性:部署简单,甚至安装不需要编译。

分布式:为分布式而生,去中心化,官方推荐的拓扑结构没有单点故障。

延迟投递。

支持订阅发布。

bitly、有赞、docker、digg等大规模部署,并且有赞开源了二次开发版本。

唱吧村特殊村情及主要矛盾

历史上,我们的异步任务入队的数据是:PHP的类名(string)、PHP方法名和该方法的参数拼接成一个字符串。消费者需要new一个类,然后以执行字符串的方式(eval())来执行,由于方法和参数拼接成字符串,带来转义风险,最主要的是,这样就决定了我们的最终消费者,必须是PHP。

全新的架构

该拉取为推送。有数据就执行,没数据就阻塞,避免空轮询。

引入PHP-FPM。PHP-FPM作为非常成熟的PHP执行者,有完善的进程管理、垃圾回收、性能优化,并且常驻内存,非常适合作为最终消费者。并且PHP-FPM常驻内存并监听9000端口,也非常适合承担订阅者的角色,代码实时更新。

将上述两点融合,开发一个中间件,从NSQ订阅数据,再以FAST-CGI协议推送给PHP-FPM。

开发一个管理页面,可以方便的配置和管理。

旧版是线上请求直接写入单点的MemcacheQ机器上,先在是以HTTP写入Nginx,Nginx做负载均衡,转发到NSQ节点上。

20180930150819_558.jpg

中间件NSQProxy

NSQProxy是唱吧服务器部门自住开发的轻量级中间件,Golang编写,性能强劲。

实现方式

20180930150819_357.jpg

启动第一步主备检测:

如果是主机,则一个监听4140端口,此时新启两个协程,该协程等待备机发PING并回PONG,一直阻塞等待accept。另一个协程(主协程)继续向下走。

如果是备机,则死循环向主机发PING,如果收到PONG,则sleep,程序会一直阻塞在这里。如果主机未回复,否则备机转为主机启动。

主备角色等信息由配置文件决定。

信号监听:

新启二个协程,一个是走主流程的(下面第3点),一个是走动态消费流程(下面第4点)。

主协程监听SIGINT(2), SIGTERM(15), SIGTRAP(5),如果是SIGINT(2)和SIGTERM(15)则程序退出,如果是SIGTRAP(5)忽略不管。主协程阻塞在这里。

主流程:

协程从Mysql获取管理后台的配置,每个队列都与NSQ建立一个链接,并根据配置的并发量(N个),启动N个协程,以FAST-CGI协议向PHP-FPM发送消费数据。

在刚进入主流程时,上一小步执行之前,会启一个新协程,以定时器的方式,定时更新系统配置建和管理后台的配置数据。

动态消费:

有些运营活动、push推送等突发性的入队,造成原本的并发量消费能力不足,这时候需要新增协程来帮助消费。

以定时器的方式,定时扫描NSQ中积压的队列,若某队列达到积压阈值,则会启动与NSQ建立新的连接,启动新的协程来增加消费能力。再以定时器的方式,达到设定时间(如300秒),就会关闭链接并协程协程。

性能测试

入队

借助apache-ab工具,消息长度:356(短信验证码的标准长度)。

Golang TCP协议:10个进程,每个进程入队10000,总入队100000

运行时间:8s

单次平均时间:8s / 10w = 0.08ms

单次真实时间: 0.08ms * 10 = 0.8ms

QPS: 10w/8s = 12500 个

CPU * 20核 ≈ 30%

Golang HTTP协议:10个进程,每个进程入队10000,总入队100000

运行时间:2.9s

单次平均时间:2.9s / 10w = 0.029ms

单次真实时间: 0.029ms * 10 = 0.29ms

QPS: 10w/2.9s = 34482 个

CPU * 20核 ≈ 30%

PHP HTTP协议:10个进程,每个进程入队10000,总入队100000

运行时间:3.4s

单次平均时间:3.4s / 10w = 0.034ms

单次真实时间: 0.034ms * c = 0.34ms

QPS: 10w/3.4s = 29412 个

CPU * 4核 ≈ 30%,16个核是0%

出队

消息长度:356(短信验证码的标准长度),消费操作就是打日志。速度是根据打日志的时间戳来统计。

20并发,最多推送40

QPS:2177

PHP-FPM:起初CPU * 20核 > 90%, 后来就长期维持100%。

NSQD:CPU会有个别一两个核,在个别时刻闪现到1%~2%,然后恢复0%,网络读写210k/960k

NSQProxy:CPU一个核30%,一个核15%,其余都是4%。网络读写2400k/3320k,磁盘每30秒写30M(每条消费log)

10并发,最多推送20

QPS:同上

起初CPU * 20核 > 60%, 后来就所有核长期维持100%。

NSQD:CPU会有个别一两个核,最多2个核闪现到1%,然后恢复0%,网络读写210k/960k

NSQProxy:CPU一个核30%,一个核13%,其余都是4%。网络读写2400k/3320k,磁盘每30秒写30M(每条消费log)

谁还不是写BUG的咋滴

PHP-FPM的特性决定了它的瓶颈

PHP-FPM就像HTTP协议一样,每次请求相互独立,不保存上次请求的状态和上下文。抛开优化点不谈,一次请求结束,删除变量,删除引用,释放内存,一切成空。下次个请求到来时,从头再来!

这就造成了一个问题:如敏感词检测的类,在new Class()时,会加载十几万行词库文件并解析,这一步大约耗时700ms,那么PHP-FPM每次消费前,都要重复这一步骤,执行完成后再销毁,这就会使消费速度大幅下降,队列积压严重。如果是Golang、JAVA等语言,则可以一次加载解析,永久使用。

PHP-FPM进程数限制

我们的异步任务几乎都是IO密集型,没有CPU密集型。所以可以开数百个进程跑,反正都在Sleep等待网络返回。而PHP-FPM却不可以,

在PHP7下,PHP-FPM开到200以上性能CPU的占用就开始飙升,同时每个PHP-FPM是同步执行,进程不可复用。那么在PHP-FPM进程数固定的大前提下,如果有一个任务执行特别慢,那么就会占用PHP-FPM进程不释放,这样的任务多来几个,很快就会把服务器上PHP-FPM的进程全部占光,导致其他消费快的队列却无可用的PHP-FPM。

二期畅想

一期工程所暴露的问题,在二期中会逐一解决。

针对以上的坑,列出几点解决方案,待探讨论证。

方式一

维持现状,NSQ和MemcacheQ共存,大多数队列在NSQ + PHP-FPM的方式,个别特殊队列仍然使用MemcacheQ + 死循环的方式。

方式二

安装NSQ-PHP客户端扩展,取代PHP-FPM。

方式三

简单粗暴执行:消费快的队列在PHP-FPM中执行,消费慢的队列仍然在while(true)中执行。NSQProxy不仅仅提供一个消息转发到PHP-FPM的功能(订阅发布的推送模式),同时还要监听一个端口可供消费者主动获取(拉取数据模式)。但是这样,既不优雅,也不统一和规范,尽管它可以快速解决当前问题。

方式四

PHP实现一个常驻内存的,监听某端口的功能,取代PHP-FPM。我小时候写的PHP-Socket项目有了用户之地:MeepoPS是Nginx + PHP-FPM的结合体,即对外监听端口,也可以直接运行PHP代码,压测数据表明比较稳定。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值