PHP7生产环境队列Beanstalkd用法详解


Posted in PHP onMay 19, 2020

应用场景

为什么要用呢,有什么好处?这应该放在最开头说,一件东西你只有了解它是干什么的,适合干什么,才能更好的与自己的项目相结合,用到哪里学到哪里,学了不用等于不会,我们平时就应该多考虑一些这样的问题:自己做个什么项目功能能跟 xx 技术相结合呢?这个 xx 技术放在这种业务场景下行不行呢?而不是 “学了这个 xx 技术能干嘛呢,公司现在也没有用这个的呀,学了也没用啊”,带着这样心情去学习 xx 技术,肯定很痛苦。

队列大家都知道是将一些耗时的操作先不去做,先埋点,再异步去处理,这样对一些发邮件发短信之类的耗时操作,用户是感觉不到的,因为埋点结束,操作也就结束了,消费队列都是在服务器上做的。主要应用在短信或邮件通知,访问第三方接口订阅消息,商城的一些秒杀活动,都可以结合队列来完成。

Beanstalkd 介绍

Beanstalkd 是一个高性能,轻量级的分布式内存队列,C 代码,典型的类 Memcached 设计,协议和使用方式都是同样的风格,所以使用过 memcached 的用户会觉得 Beanstalkd 似曾相识。

beanstalkd 的最初设计意图是在高并发的网络请求下,通过异步执行耗时较多的请求,及时返回结果,减少请求的响应延迟。

Ubuntu 安装

sudo apt-get install beanstalkd

配置文件

vim /etc/default/beanstalkd

查看状态

service beanstalkd status
# 命令回显 #
root@:/www/server/php/72/etc# service beanstalkd status
● beanstalkd.service - Simple, fast work queue
  Loaded: loaded (/lib/systemd/system/beanstalkd.service; enabled; vendor preset: enabled)
  Active: active (running) since Tue 2018-10-16 10:42:28 CST; 6 days ago
   Docs: man:beanstalkd(1)
 Main PID: 7033 (beanstalkd)
  Tasks: 1 (limit: 4634)
  CGroup: /system.slice/beanstalkd.service
      └─7033 /usr/bin/beanstalkd -l 0.0.0.0 -p 11300 -b /var/lib/beanstalkd
Oct 16 10:42:28 ip-10-93-2-137 systemd[1]: Started Simple, fast work queue.

配置连通性 + 持久化

ip 用 0.0.0.0 允许所有连接,靠配置安全组或防火墙去约束连接,放开 -b 参数 (默认没有持久化),内存的队列消息可以落地到硬盘 binlog 实现持久化,断电可重新读取队列消息。

vim /etc/default/beanstalkd
BEANSTALKD_LISTEN_ADDR=0.0.0.0
BEANSTALKD_LISTEN_PORT=11300
BEANSTALKD_EXTRA="-b /var/lib/beanstalkd"

beanstalkd 任务状态

状态 注释
delayed 延迟状态
ready 准备好状态
reserved 消费者把任务读出来,处理时
buried 预留状态
delete 删除状态

管理工具

亲测了很多网上能找到的 beanstalkd 工具,这两款是我最中意的了,一个命令行,一个 web 的。

命令行:https://github.com/src-d/beanstool

web 界面:https://github.com/ptrofimov/beanstalk_console

编程语言客户端

PHP 客户端

https://packagist.org/packages/pda/pheanstalk

composer require pda/pheanstalk

写入 job

<?php
//创建队列消息
require_once('./vendor/autoload.php');
use Pheanstalk\Pheanstalk;
$pheanstalk = new Pheanstalk('127.0.0.1',11300);
$tubeName = 'email_list';
$jobData = [
  'email' => '123456@163.com',
  'message' => 'Hello World !!',
  'dtime' => date('Y-m-d H:i:s'),
];
$pheanstalk->useTube( $tubeName)->put( json_encode( $jobData ) );

消费 job

<?php
ini_set('default_socket_timeout', 86400*7);
ini_set( 'memory_limit', '256M' );
// 消费队列消息
require_once('./vendor/autoload.php');
use Pheanstalk\Pheanstalk;
$pheanstalk = new Pheanstalk('127.0.0.1',11300);
$tubeName = 'email_list';
while ( true )
{
  // 获取队列信息, reserve 阻塞获取
  $job = $pheanstalk->watch( $tubeName )->ignore( 'default' )->reserve();
  if ( $job !== false )
  {
    $data = $job->getData();
    /* TODO 逻辑操作 */
    /* 处理完成,删除 job */
    $pheanstalk->delete( $job );
  }
}

default_socket_timeout 这个参数是一定要加的,php 默认一般是 60s,假如您没有在代码里面设置,采用默认的话(60s),60s 之内如果没有 job 产生,脚本就会报 socket 错误,我写的是 7 天超时,您可以根据业务去调整,记住一定要配置,网上很多搜的 consumer 脚本都没有配置这个,根本不能投入生产环境使用,这是我亲自实践的结果。

关于 while true 是否死循环,很明确告诉你是死循环,但是不会一直耗性能的那样执行下去,它会在 reserve 这里阻塞不动,直到有消息产生才会往下走,所以大可放心使用,我的项目代码里面是使用了方法调用方法自身去实现循环的。

就是这样的代码,供参考:

public function watchJob()
{
  $job = $this->pheanstalk->watch( config( 'tube' ) )->ignore( 'default' )->reserve();
  if ( $job !== false )
  {
    $job_data = $job->getData();
    $this->subscribe( $job_data );
    $this->pheanstalk->delete( $job );
    /* 继续 Watch 下一个 job */
    $this->watchJob();
  }
  else
  {
    $this->log->error( 'reserve false', 'reserve false' );
  }
}

监控 beanstalkd 状态

<?php
//监控服务状态
require_once('./vendor/autoload.php');
use Pheanstalk\Pheanstalk;
$pheanstalk = new Pheanstalk('127.0.0.1',11300);
$isAlive = $pheanstalk->getConnection()->isServiceListening();
var_dump( $isAlive );

可以配合 email 做一个报警邮件,脚本每分钟去执行,判断状态是 false,就给管理员发送邮件报警。

一些相关命令

查看 beanstalkd 服务内存占用

top -u beanstalkd

后台运行 consumer 脚本

nohup php googlehome_subscribe.php &

查看 consumer 脚本运行时间

ps -A -opid,stime,etime,args | grep consumer.php

手工重启 consumer 脚本

ps auxf|grep 'googlehome_subscribe.php'|grep -v grep|awk '{print $2}'|xargs kill -9 
nohup php googlehome_subscribe.php &

一些总结

php 要把错误日志打开,方便收集 consumer 脚本 crash 的 log,脚本跑出一些致命的 error 一定要及时修复,因为一旦有错就会挂掉,这会影响你脚本的可用性,后期稳定之后可以上 supervisor 这种进程管理程序来管控脚本生命周期。

一些网络请求操作,一定要 try catch 到所有错误,一旦没有 catch 到,脚本就崩。我用的是 Guzzle 去做的网络请求,下面是我 catch 的一些错误,代码片段供参考。

try
{
  /* TODO: 逻辑操作 */
}
catch ( ClientException $e )
{
  $results['mid']  = $this->mid;
  $results['code']  = $e->getResponse()->getStatusCode();
  $results['reason'] = $e->getResponse()->getReasonPhrase();
  $this->log->error( 'properties-changed ClientException', $results );
}
catch ( ServerException $e )
{
  $results['mid']  = $this->mid;
  $results['code']  = $e->getResponse()->getStatusCode();
  $results['reason'] = $e->getResponse()->getReasonPhrase();
  $this->log->error( 'properties-changed ServerException', $results );
}
catch ( ConnectException $e )
{
  $results['mid'] = $this->mid;
  $this->log->error( 'properties-changed ConnectException', $results );
}

job 消费之后一定要删除掉,如果长时间不删除,php 客户端会有 false 返回,是因为有 DEADLINE_SOON 这个超时错误产生,所以处理完任务,一定要记得删除,这一点跟 kafka 不一样,beanstalkd 需要开发者自己去删除 job。

以上就是PHP7生产环境队列Beanstalkd用法详解的详细内容,更多关于PHP7中Beanstalkd正确用法的资料请关注三水点靠木其它相关文章!

PHP 相关文章推荐
第八节--访问方式
Nov 16 PHP
用PHP读取flv文件的播放时间长度
Sep 03 PHP
PHP 采集程序原理分析篇
Mar 05 PHP
PHP三层结构(上) 简单三层结构
Jul 04 PHP
PHP结合JQueryJcrop实现图片裁切实例详解
Jul 24 PHP
PHP判断文章里是否有图片的简单方法
Jul 26 PHP
php通过文件流方式复制文件的方法
Mar 13 PHP
php+html5实现无刷新图片上传教程
Jan 22 PHP
php基于SQLite实现的分页功能示例
Jun 21 PHP
PHP开发实现微信退款功能示例
Nov 25 PHP
PHP5.0 TIDY_PARSE_FILE缓冲区溢出漏洞的解决方案
Oct 14 PHP
使用PHP开发留言板功能
Nov 19 PHP
PHP项目多语言配置平台实现过程解析
May 18 #PHP
Yii 框架使用Forms操作详解
May 18 #PHP
thinkphp 框架数据库切换实现方法分析
May 18 #PHP
PHP接口类(interface)的定义、特点和应用示例
May 18 #PHP
php + ajax 实现的写入数据库操作简单示例
May 16 #PHP
PHP date_default_timezone_set()设置时区操作实例分析
May 16 #PHP
Yii框架应用组件用法实例分析
May 15 #PHP
You might like
深入解析PHP内存管理之谁动了我的内存
2013/06/20 PHP
javascript检查日期格式的函数[比较全]
2008/10/17 Javascript
浅谈javascript的数据类型检测
2010/07/10 Javascript
js arguments对象应用介绍
2012/11/28 Javascript
javascript四舍五入函数代码分享(保留后几位)
2013/12/10 Javascript
setTimeout()与setInterval()方法区别介绍
2013/12/24 Javascript
中止javascript执行的方法
2014/02/14 Javascript
js实现类似新浪微博首页内容渐显效果的方法
2015/04/10 Javascript
一款简单的jQuery图片标注效果附源码下载
2016/03/22 Javascript
JavaScript面向对象程序设计教程
2016/03/29 Javascript
jQuery实现的购物车物品数量加减功能代码
2016/11/16 Javascript
jQuery Ajax前后端使用JSON进行交互示例
2017/03/17 Javascript
详解ES6 Fetch API HTTP请求实用指南
2018/11/14 Javascript
jquery ajax 请求小技巧实例分析
2019/11/11 jQuery
JavaScript变量基本使用方法实例分析
2019/11/15 Javascript
简述Vue中容易被忽视的知识点
2019/12/09 Javascript
微信小程序转化为uni-app项目的方法示例
2020/05/22 Javascript
解决vue net :ERR_CONNECTION_REFUSED报错问题
2020/08/13 Javascript
JS时间戳与日期格式互相转换的简单方法示例
2021/01/30 Javascript
JavaScript 实现轮播图特效的示例
2020/11/05 Javascript
[42:04]DOTA2上海特级锦标赛主赛事日 - 2 胜者组第一轮#3Secret VS OG第一局
2016/03/03 DOTA
Python中尝试多线程编程的一个简明例子
2015/04/07 Python
Python 用Redis简单实现分布式爬虫的方法
2017/11/23 Python
python使用json序列化datetime类型实例解析
2018/02/11 Python
Python 没有main函数的原因
2020/07/10 Python
css3实现图片遮罩效果鼠标hover以后出现文字
2013/11/05 HTML / CSS
英国手机零售商:Metrofone
2019/03/18 全球购物
日本最大美瞳直送网:Morecontact(中文)
2019/04/03 全球购物
为什么会有内存对齐
2016/10/10 面试题
AJax面试题
2014/11/25 面试题
群众路线教育实践活动心得体会
2014/03/07 职场文书
消防安全月活动总结
2015/05/08 职场文书
2016年国培研修日志
2015/11/13 职场文书
2016年“抗战胜利纪念日”71周年校园广播稿
2015/12/18 职场文书
2016年社会管理综治宣传月活动总结
2016/03/16 职场文书
【2·13】一图读懂中国无线电发展
2022/02/18 无线电