77

NSQ 最佳实践

 2年前 阅读数 79
以下为 快照 页面,建议前往来源网站查看,会有更好的阅读体验。
原文链接: https://mp.weixin.qq.com/s/W_-Pj-0gHGpdqAQByym1tA?amp%3Butm_medium=referral

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

唱吧异步任务的前世

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

为什么要用异步任务

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

流程

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

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

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

3MBRvar.jpg!web

弊端

  • 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节点上。

VjQRZbR.jpg!web

中间件NSQProxy

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

实现方式

qYNFf2e.jpg!web

  1. 启动第一步主备检测:

  • 如果是主机,则一个监听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(短信验证码的标准长度)。

  1. 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(短信验证码的标准长度),消费操作就是打日志。速度是根据打日志的时间戳来统计。

  1. 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代码,压测数据表明比较稳定。


猜你喜欢

  • 35
    • 微信 mp.weixin.qq.com 2年前
    • 快照

    NSQ最佳实践

  • 42

    php-nsq php-nsq 是nsq的php客户端,采用c扩展编写,性能和稳定性。 安装 : 请提前安装libevent Dependencies: libevent (apt-get install libevent-dev ,yum install libevent-devel) 1....

  • 62
    • bridgeforyou.cn 2年前
    • 快照

    MQ(6) —— Nsq vs Kafka

    Nsq vs Kafka 正如之前说的,Nsq是一款极简的消息中间件,通过学习Nsq,我们可以通过对比的方式,学习其他的Mq。 这一节,就让我们在对比中,学习另一种Mq,Kafka,在对比中,加深对Mq的理解。 首先,先放上...

  • 24
    • studygolang.com 2年前
    • 快照

    NSQ源码-Nsq客户端

    看完lookupd和nsqd之后我们再来看下nsq client端的代码。 我是想把nsq系统完完整整的看一遍,从而对他形成一个更整体的 认识。对message queue来说他的client端就是生产者和消费者,生产者负责想nsq中投递消息,消费者负责...

  • 29
    • studygolang.com 2年前
    • 快照

    NSQ源码-NSQD

    看完了nsqlookupd我们继续往下看, nsqd才是他的核心. 里面大量的使用到了go channel, 相信看完之后对你学习go有很大的帮助.相较于lookupd部分无论在代码逻辑和实现上都要复杂很多. 不过基本的代码结构基本上都是一样的, 进...

  • 47
    • studygolang.com 2年前
    • 快照

    NSQ源码-nsqlookupd

    为什么选择nsq 之前一直在用erlang做电信产品的开发,对erlang的一些生态也比较了解,和erlang相关的产品在互联网公司使用最多的应该就是rabbitmq了,也许很多人听说过erlang就是因为他们公司在使用rabbitmq。在之前也看过一点...

  • 46
    • www.tuicool.com 1年前
    • 快照

    NSQ v0.1.5 源码分析

    NSQ 是实时的分布式消息处理平台,其设计的目的是用来大规模地处理每天数以十亿计级别的消息。NSQ 具有分布式和去中心化拓扑结构,该结构具有无单点故障、故障容错、高可用性以及能够保证消息的可靠传递的特征,是一个成熟的、已在大规模...

  • 64
    • www.tuicool.com 1年前
    • 快照

    nsq源码review:go-nsq producer

    nsq是一个实时分布式的消息队列平台。 核心部分是一个叫nsqd的模块,它负责接收和转发消息。同时在go-nsq的包中,提供了consumer和producer的核心接口。在读nsq源码的时候,很好奇它的数据是怎么从producer给到了consumer的,于...

  • 64
    • www.tuicool.com 1年前
    • 快照

    使用NSQ(附Golang代码)

    上篇文章已经了解了消息中间件相关的知识,这篇文章学习一下Golang语言编写的知名消息中间件 NSQ 。 nsq最初是由bitly公司开源出来的一款简单易用的消息中间件,它可用于大规模系...

  • 45
    • www.tuicool.com 1年前
    • 快照

    golang nsq

    1.安装 https://nsq.io/deployment/installing.html 2.快速入门 在一个终端中,运行

关于极客头条


聚合每日国内外有价值,有趣的链接。

AD