//初始化阿里云消息队列
require ROOT_PATH . 'vendor/aliyun-queue/mns-autoloader.php';
//获取配置信息
$system_config = config('aliyun_config');
//初始化队列client
$mns = new \AliyunMNS\Client($system_config['mns_end_point'], $system_config['key_id'], $system_config['key_secret']);//配置密钥
//获取队列
$queue = $mns->getQueueRef('xiaozhang');
try {
/*
* 获取第一条当前消息信息
*/
$res = $queue->receiveMessage(0);//当前第一条
$msg = $res->getMessageBody();//输出当前数据
} catch (\AliyunMNS\Exception\MnsException $e) {
echo $e;exit;
}
//进程记录数组
public $pid_arr = [
'master' => 0,
'son' => [
]
];
//队列配置
public $config = [
[
//队列名
'queue_name' => 'xiaozhang',
]
];
//liunx命令操作
public function index() {
set_time_limit(0);
global $argv; //获取全局变量 global是设置全局变量
switch ($argv[1]) {
case 'start':
$this->start();
break;
case 'stop':
$this->stop();
break;
}
}
/*
* 队列处理启动
* @主进程会于运行一边就销毁(php运行一边就自动销毁的特性)
* @子进程会一直循环下去
*/
public function start() {
if (file_exists(ROOT_PATH . "/public/queue.pid")) {//进程已经启动
echo "程序运行中! \n";
exit;
}
//取主进程id
$this->pid_arr['master'] = getmypid();
//初始化阿里云消息队列
require ROOT_PATH . 'vendor/aliyun-queue/mns-autoloader.php';
//获取配置信息
$system_config = config('aliyun_config');
//初始化队列client
$mns = new \AliyunMNS\Client($system_config['mns_end_point'], $system_config['key_id'], $system_config['key_secret']);
//初始化分析类
$analysis = new \app\event\controller\MsgController();
//循环创建进程
for ($i = 0; $i < count($this->config); $i++) {
$pid = pcntl_fork();//操作系统创建一个新的进程(子进程),
if ($pid == -1) {
//错误
echo "进程创建失败! \n";
} elseif ($pid == 0) {
//当前是子进程
echo "进程创建成功 id:" . getmypid() . " \n";
//获取队列
$queue = $mns->getQueueRef($this->config[$i]['queue_name']);
//执行任务
$this->queques($queue, $this->config[$i]['queue_name'], $analysis);
break;
} else {
//当前是主进程
$this->pid_arr['son'][] = $pid;
//判断是否为最后一次循环
if (count($this->config) - $i == 1) {
//写入文件
file_put_contents(ROOT_PATH . "/public/queue.pid", json_encode($this->pid_arr));
echo "运行完毕 主进程ID:" . getmypid() . "\n";
}
}
//延迟20ms
usleep(1000 * 20);
}
}
/*
* 队列停止
*/
public function stop() {
$proce_file = file_get_contents(ROOT_PATH . "/public/queue.pid");
if (!$proce_file) {
echo "进程未运行! \n";
exit;
}
$arr = json_decode($proce_file, true);
if (!$arr) {
unlink(ROOT_PATH . "/public/queue.pid");
echo "进程未运行! \n";
exit;
}
foreach ($arr['son'] as $val) {
//发送终止进程信号
posix_kill($val, SIGTERM);
}
// posix_kill($arr['master'], SIGTERM);
unlink(ROOT_PATH . "/public/queue.pid");
echo "停止成功!\n";
}
6.最关键的循环操作
/*
* 队列处理
*/
public function queques($queue, $queue_name, $analysis) {
$sleep = 1000000 * rand(1, 3);
while (1) {
usleep($sleep); //延迟
/*
* 获取消息
*/
try {
$res = $queue->receiveMessage(0);
$msg = $res->getMessageBody();
$receiptHandle = $res->getReceiptHandle();
$sleep = 200000; //取出数据则激活队列,100毫秒执行一次循环
} catch (\AliyunMNS\Exception\MnsException $e) {
// 没有消息
$sleep = 2000000; //没有取出数据则延长循环时间为2秒一次循环
continue;
}
/*
* 处理消息
*/
$arr = json_decode($msg, TRUE);
if ($arr) {
$type = (string) $arr['type'];
if ($type) {
$analysis->$type($arr, $queue_name);
} else {
$this->log($queue_name, "收到一条没有类型的队列消息,MSG:" . $msg);
}
} else {
$this->log($queue_name, "收到一条无法JSON解析的队列消息,MSG:" . $msg);
}
/*
* 删除消息
*/
try {
$res = $queue->deleteMessage($receiptHandle);
} catch (\AliyunMNS\Exception\MnsException $e) {
continue;
}
}
}
0,
'son' => [
]
];
//队列配置
public $config = [
[
//队列名
'queue_name' => 'xiaozhang',
]
];
public function index() {
set_time_limit(0);
global $argv; //获取全局变量 global是设置全局变量
switch ($argv[1]) {
case 'start':
$this->start();
break;
case 'stop':
$this->stop();
break;
}
}
/*
* 队列处理启动
* @主进程会于运行一边就销毁(php运行一边就自动销毁的特性)
* @子进程会一直循环下去
*/
public function start() {
if (file_exists(ROOT_PATH . "/public/queue.pid")) {//进程已经启动
echo "程序运行中! \n";
exit;
}
//取主进程id
$this->pid_arr['master'] = getmypid();
//初始化阿里云消息队列
require ROOT_PATH . 'vendor/aliyun-queue/mns-autoloader.php';
//获取配置信息
$system_config = config('aliyun_config');
//初始化队列client
$mns = new \AliyunMNS\Client($system_config['mns_end_point'], $system_config['key_id'], $system_config['key_secret']);
//初始化分析类
$analysis = new \app\event\controller\MsgController();
//循环创建进程
for ($i = 0; $i < count($this->config); $i++) {
$pid = pcntl_fork();//操作系统创建一个新的进程(子进程),
if ($pid == -1) {
//错误
echo "进程创建失败! \n";
} elseif ($pid == 0) {
//当前是子进程
echo "进程创建成功 id:" . getmypid() . " \n";
//获取队列
$queue = $mns->getQueueRef($this->config[$i]['queue_name']);
//执行任务
$this->queques($queue, $this->config[$i]['queue_name'], $analysis);
break;
} else {
//当前是主进程
$this->pid_arr['son'][] = $pid;
//判断是否为最后一次循环
if (count($this->config) - $i == 1) {
//写入文件
file_put_contents(ROOT_PATH . "/public/queue.pid", json_encode($this->pid_arr));
echo "运行完毕 主进程ID:" . getmypid() . "\n";
}
}
//延迟20ms
usleep(1000 * 20);
}
}
/*
* 队列停止
*/
public function stop() {
$proce_file = file_get_contents(ROOT_PATH . "/public/queue.pid");
if (!$proce_file) {
echo "进程未运行! \n";
exit;
}
$arr = json_decode($proce_file, true);
if (!$arr) {
unlink(ROOT_PATH . "/public/queue.pid");
echo "进程未运行! \n";
exit;
}
foreach ($arr['son'] as $val) {
//发送终止进程信号
posix_kill($val, SIGTERM);
}
// posix_kill($arr['master'], SIGTERM);
unlink(ROOT_PATH . "/public/queue.pid");
echo "停止成功!\n";
}
/*
* 队列处理
*/
public function queques($queue, $queue_name, $analysis) {
$sleep = 1000000 * rand(1, 3);
while (1) {
usleep($sleep); //延迟
/*
* 获取消息
*/
try {
$res = $queue->receiveMessage(0);
$msg = $res->getMessageBody();
$receiptHandle = $res->getReceiptHandle();
$sleep = 200000; //取出数据则激活队列,100毫秒执行一次循环
} catch (\AliyunMNS\Exception\MnsException $e) {
// 没有消息
$sleep = 2000000; //没有取出数据则延长循环时间为2秒一次循环
continue;
}
/*
* 处理消息
*/
$arr = json_decode($msg, TRUE);
if ($arr) {
$type = (string) $arr['type'];
if ($type) {
$analysis->$type($arr, $queue_name);
} else {
$this->log($queue_name, "收到一条没有类型的队列消息,MSG:" . $msg);
}
} else {
$this->log($queue_name, "收到一条无法JSON解析的队列消息,MSG:" . $msg);
}
/*
* 删除消息
*/
try {
$res = $queue->deleteMessage($receiptHandle);
} catch (\AliyunMNS\Exception\MnsException $e) {
continue;
}
}
}
}
sendSms($tel, $msg), TRUE);
if ($res['result'] === 0) {
//发送成功
$this->log($queue_name, "手机:{$tel} 的信息发送成功");
} else {
//发送失败
$err_msg = $res['errmsg'];
$this->log($queue_name, "手机:{$tel} 的信息发送失败,ERROR:{$err_msg}");
}
}
/*
* 信鸽推送处理
*/
public function xinge($param, $queue_name) {
$result = $this->xinge_push($param['uid'], $param['title'], $param['content'], $param['custom'], $param['push_type'], $param['client']);
$app_id = config('xinge')[$param['client']]['id'];
$this->log($queue_name, '使用' . $app_id . "推送");
if ($result['ret_code'] === 0) {
if (is_array($param['uid'])) {
$this->log($queue_name, "推送成功,接收者:" . json_encode($param['uid']) . " 内容:" . json_encode($param) . " 返回值:" . json_encode($result));
} else {
$this->log($queue_name, "推送成功,接收者:{$param['uid']} 内容:" . json_encode($param) . " 返回值:" . json_encode($result));
}
} else {
if (is_array($param['uid'])) {
$this->log($queue_name, "推送失败,接收者:" . json_encode($param['uid']) . " ERROR_CODE:" . $result['ret_code']);
} else {
$this->log($queue_name, "推送失败,接收者:{$param['uid']} ERROR_CODE:" . $result['ret_code']);
}
}
}
/*
* 微信模板消息
*/
public function wechat_tpl($param, $queue_name) {
$result = $this->send_tpl_msg($param['tpl_id'], $param['open_id'], $param['data'], $param['url']);
if ($result['errcode']) {
//发送失败
$this->log($queue_name, "模板消息推送失败,接收者:{$param['open_id']} ERROR_CODE:{$result['errcode']} TPL_ID:{$param['tpl_id']}");
} else {
//发送成功
$this->log($queue_name, "模板消息推送成功,接收者:{$param['open_id']} TPL_ID:{$param['tpl_id']}");
}
}
/*
* 测试
* (如果直接用数据库保存会线程会崩掉)
* 也可以用阿里云上的日志接口
*/
public function record($queue_name){
$path = ROOT_PATH . '/queue_log/' . 'win';
if (!is_dir($path)) {
mkdir($path);
}
$file = $path . '/' . str_replace('-', '_', '奖品') . '.txt';
$msg=[
'uid'=>$queue_name['uid'],
'username'=>$queue_name['msg']['username'],
'win'=>$queue_name['msg']['win']
];
file_put_contents($file,json_encode($msg, JSON_UNESCAPED_UNICODE). "\n", FILE_APPEND);
}
/*
* 空操作(对其他没有影响)
*/
public function __call($name, $arguments) {
$this->log('queue_error', "执行了一条错误的队列,QueueType:{$name} QueueName:{$arguments[1]}");
}
}
/*
* 写日志
*/
public function log($queue_name, $msg) {
$path = ROOT_PATH . '/queue_log/' . date('Y_m_d');
if (!is_dir($path)) {
mkdir($path);
}
$file = $path . '/' . str_replace('-', '_', $queue_name) . '.txt';
$msg = "[" . date('Y-m-d H:i:s') . "]:" . $msg . "\n";
file_put_contents($file, $msg, FILE_APPEND);
}
这里的开启方法是
关闭线程: php service.php stop