supervisorctl配置,php脚本启动多个进程
supervisorctl
是 Supervisor 的命令行工具,用于管理和控制 Supervisor 中配置的进程。Supervisor 是一个进程管理工具,用于启动、停止和监控进程。
[program:tdcrm_chat_message_0]
command=/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message 0
stdout_logfile=/data/logs/supervisor/tdcrm_syn_chat_message_0.log
autostart=true
autorestart=true
startsecs=5
priority=1
stopasgroup=true
killasgroup=true
user=www
[program:tdcrm_chat_message_1]
command=/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message 1
stdout_logfile=/data/logs/supervisor/syn_chat_message_1.log
autostart=true
autorestart=true
startsecs=5
priority=1
stopasgroup=true
killasgroup=true
user=www
[program:tdcrm_chat_message_2]
command=/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message 2
stdout_logfile=/data/logs/supervisor/tdcrm_syn_chat_message_2.log
autostart=true
autorestart=true
startsecs=5
priority=1
stopasgroup=true
killasgroup=true
user=www
[program:syn_chat_message_ai]
command=/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message_ai %(process_num)d
stdout_logfile=/data/logs/supervisor/syn_chat_message_ai.log
autostart=true
autorestart=true
process_name=%(program_name)s_%(process_num)d
numprocs=5
startsecs=5
priority=1
stopasgroup=true
killasgroup=true
user=www
[program:syn_chat_ai_list_return_ai]
command=/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_ai_list return_ai %(process_num)d
stdout_logfile=/data/logs/supervisor/syn_chat_ai_list_return_ai.log
autostart=true
autorestart=true
process_name=%(program_name)s_%(process_num)d
numprocs=5
startsecs=5
priority=1
stopasgroup=true
killasgroup=true
user=www
字段解释[program ]: 定义一个名为 syn_chat_message_ai
的程序。program:
后面的名字是你为这个程序定义的标识符。
-
command: 执行的命令。这里是运行一个PHP脚本,
%(process_num)d
是一个占位符,会被替换为numprocs
中定义的进程编号。 -
stdout_logfile: 标准输出日志文件的路径。程序运行时的输出会被写入这个文件。
-
autostart: 是否在Supervisor启动时自动启动这个程序。
true
表示自动启动。 -
autorestart: 当程序退出时,是否自动重启。
true
表示自动重启。 -
process_name: 进程名称模板。
%(program_name)s
是程序名称的占位符,%(process_num)d
是进程编号的占位符。例如,如果程序名是syn_chat_message_ai
,进程编号是0
,那么进程名将是syn_chat_message_ai_0
。 -
numprocs: 启动的进程数量。这里设置为
5
,表示将启动 5 个进程,每个进程的command
参数中的%(process_num)d
会被替换为0
到4
。 -
startsecs: 程序启动后在这段时间内(秒)没有退出,Supervisor会认为启动成功。这里设置为
5
秒。 -
priority: 程序启动的优先级。值越小,优先级越高。这里设置为
1
。 -
stopasgroup: 是否将
stop
信号发送到进程组。true
表示会将停止信号发送给进程组中的所有进程。 -
killasgroup: 是否将
kill
信号发送到进程组。true
表示会将杀死信号发送给进程组中的所有进程。 -
user: 以哪个用户的身份运行这个程序。这里设置为
www
,表示以www
用户身份运行。
进程编号的替换
当 numprocs
设置为 5
时,Supervisor会启动5个进程,command
中的 %(process_num)d
会被分别替换为 0
、1
、2
、3
和 4
。这样每个进程都会有一个唯一的编号,并且会分别执行以下命令:
/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message_ai 0
/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message_ai 1
/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message_ai 2
/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message_ai 3
/usr/local/php/bin/php /data/wwwroot/tdcrm/artisan app:syn_chat_message_ai 4
每个进程的输出会被写入到同一个日志文件 /data/logs/supervisor/syn_chat_message_ai.log
。
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Mockery\Exception;
use Modules\Brain\Service\ChatMessage;
use Modules\Brain\Service\WorkFlow;
use Modules\System\Models\ConfigCenter;
use function Laravel\Prompts\select;
class syn_chat_ai_list extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'app:syn_chat_ai_list {opt} {process_num}';
protected $signature_full = 'app:syn_chat_ai_list ';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
private $processNum = 0;
/**
* Execute the console command.
*/
public function handle()
{
/* $redis = Redis::connection('default');*/
$this->processNum = $this->argument('process_num');
$opt = $this->argument('opt');
$this->signature_full .= $opt;
/* $binNum = exec("ps -ef |grep '" . $this->signature_full . "' |grep -v grep |grep -v '/bin/sh -c' |wc -l");
if (intval($binNum) > 1) {//最多1个进程
echo '不能启用太多进程';
exit;
}*/
while (true) {
try {
switch ($opt) {
case 'create':
$this->create_message_list();
break;
case 'return_ai':
$this->return_ai();
break;
}
} catch (Exception $e) {
Log::error($this->signature_full . "执行异常", ['error' => $e->getMessage(), 'line' => $e->getLine()]);
}
usleep(100);
}
}
private function create_message_list()
{
$redis = Redis::connection('default');
$c_k = "c_s_h";
$keys = $redis->hkeys($c_k);
if (empty($keys)) {
return [];
}
foreach ($keys as $key) {
$payloads = [];
$item = $redis->hget($c_k, $key);
if (empty($item)) {
Log::error("读取消息" . $c_k, [$key, $item]);
continue;
}
$item = json_decode($item, true);
extract($item);
// 时间小于10s的等候一下
/* if ((time() - $t) <= 10) {
continue;
}*/
$m_k = "c_m_h_" . $u . "_wxid_" . $w;
$message_keys = $redis->hkeys($m_k);
if (empty($message_keys)) {
/* $err = [
'msg' => '消息记录不存在',
'm_k' => $m_k
];
WorkFlow::add_message_log("error", "syn_chat_message_ai", $err, 0);*/
Log::error("消息记录不存在syn_chat_message_ai", [$message_keys]);
continue;
}
foreach ($message_keys as $mk) {
$i = $redis->hget($m_k, $mk);
if (empty($i)) {
Log::error("消息记录不存在", [$m_k, $i, $mk]);
continue;
}
$i = json_decode($i, true);
// Log::info("消息记录详情", [$i]);
$payloads[] = $i;
$redis->hdel($m_k, $mk);
$message = [];
$message['botUserId'] = $botUserId;
$message['customerExternalUserId'] = $customerExternalUserId;
$message['externalUserId'] = $externalUserId;
$message['coworker'] = $coworker;
$message['projectId'] = $projectId;
$message['period_id'] = $period_id;
$message['period'] = $period;
$message['stage'] = $stage;
$message['wxid'] = $w;
$message['bot_id'] = $bot_id;
$message['customer_profile'] = $customer_profile;
$message['ex_nickname'] = $ex_nickname;
$message['nickname'] = $nickname;
$message['payloads'] = $payloads;
$message['timestamp'] = time();
$message['member_id'] = $member_id;
Log::info("ai请求数据", $message);
$redis->lpush("chat_message_ai_list", json_encode($message));
}
$redis->hdel($c_k, $key);
}
}
private function return_ai()
{
$redis = Redis::connection('default');
$key = "chat_message_ai_list_" . $this->processNum;
$info = $redis->lpop($key);
if (empty($info)) {
return [];
}
$info = json_decode($info, true);
// Log::error("请求ai",[$info['messageId']??'']);
if (empty($info)) {
Log::error("请求ai数据为空", [$info]);
return [];
}
if ($info['wxid'] == $info['bot_id']) {
Log::error("给自己发送消息", [$info]);
return [];
}
Log::info("ai请求数据", [$info]);
if (empty($info['roomWecomChatId'])) {
$returns = ChatMessage::getAiReturn($info);
} else {
$returns = ChatMessage::getGroupAiReturn($info);
}
Log::info("ai返回数据", [$info, $returns]);
$answers = $returns['data']['answers'] ?? [];
if (empty($answers)) {
return [];
}
$data = ['answers' => $answers, 'wxid' => $info['wxid'], 'bot_id' => $info['bot_id'],
'botUserId' => $info['botUserId'], 'projectId' => $info['projectId'],
"roomWecomChatId" => $info['roomWecomChatId'] ?? '', "imContactId" => $info['imContactId'] ?? ''];
$redis->lpush("chat_message_ai_return_" . $this->processNum, json_encode($data));
}
}