Need a way to selectively kill sleeping php processes - php

I have a website that periodically gets a large number of sleeping php processes. My hosting service sets a limit of 20 concurrent running processes. If it goes over the limit my site goes down with a 503 error.
It is a rare occurrence and doesn't seem to have any correlation to the number of people visiting my site.
As a safeguard I would like to have a cron job with a php script that would kill php processes that have been sleeping for over 10 min.
I have a php function that will kill all sleeping MySql processes that have been sleeping for more than 10 min;
public function kill_sleeping_mysql_processes()
{
$result = $this->db->query("SHOW FULL PROCESSLIST");
foreach($result->result_array() as $row)
{
if ($row['Command'] == "Sleep" && $row['Time'] > 600)
{
$this->db->query("KILL {$row['Id']}")
}
}
}
The question is how can do I do the same with php processes?
I can get a read out of php processes with this code.
exec("ps aux | less", $output);
and I can kill specific php processes with this code if I have the pid;
$pid = 11054;
exec("kill -9 $pid");
But how can I selectively kill php processes that have been sleeping more than 10 min?

I cobbled something together. It is not elegant and is a bit of a hack but it seems to work, although I am going to test it further before putting in a cron job.
public function kill_dormant_php_processes()
{
$output_array = array();
exec("ps aux | grep -v grep", $ps_output);
array_shift($ps_output);
if (count($ps_output) > 0)
{
$i = 0;
foreach ($ps_output as $ps)
{
$ps = preg_split('/ +/', $ps);
$output_array[$i]->pid = $ps[1];
$output_array[$i]->stat = $ps[7];
$output_array[$i]->time = $ps[9];
$i++;
}
}
if( ! empty($output_array))
{
foreach ($output_array as $row)
{
if( $row->stat == 'S' && date('H:i', strtotime($row->time)) > date('H:i', strtotime('00:01')))
{
exec("kill -9 $row->pid");
}
}
}
}
I am sure there must be a better way to do it.
Could someone explain why 00:01 in the read out seems to translate to 6 min?
freedom 6933 6.0 0.1 57040 13040 ? S 16:55 0:01 /usr/local/bin/php53.cgi -c .:/home/freedom/:/etc index.php

As an alternative to the PHP script shared here, you can use the killall command with an "older than" time filter (using the -o option) to kill all those processes.
This command for example will kill all php-cgi processes that have been running for more than 30 minutes:
killall -o 30m /usr/bin/php-cgi

Related

how to get the status of the particular process by the shell_exec()?

I want to know the status of the process by passing the name in the command and execute it with the function shell_exec().
I have tried this:
`
$checkProcessStatus = "ps aux | grep <ProcessName>";
$status = shell_exec($checkProcessStatus);
dd($status);
`
I got this result:
`
user 17072 0.0 0.2 166216 33332 pts/3 S+ 11:31 0:00 <ProcessName> artis
user 20397 0.0 0.0 14232 868 pts/3 S+ 11:52 0:00 grep <ProcessName>
`
I want only the Status Like "Running" OR "Sleeping".
Here is the working code:
<?php
$command = 'ps aux';
$result = shell_exec($command);
//split for rows
$processes = explode("\n", $result);
//delete head row
array_shift($processes);
//analyze
foreach ($processes as $rawProcess) {
//beware, command line column may include spaces, that why last group is (.+)
preg_match('/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(.+)/', $rawProcess,$matches);
//preg match doesn't find anything
if (empty($matches)) {
continue;
}
//is sleeping status
if (strpos($matches[8], 'S') === 0) {
echo $rawProcess;
echo "\n";
continue;
}
//is running status
if (strpos($matches[8], 'R') === 0) {
echo $rawProcess;
echo "\n";
continue;
}
//is not sleeping and not running status
}
You can use $matches[N] for any column.
By the way you can use awk to grep data by status
ps aux | awk 'substr($8,1,1) == "S" || substr($8,1,1) == "R"'
P.S.
Status mean:
D uninterruptible sleep (usually IO)
R running or runnable (on run queue)
S interruptible sleep (waiting for an event to complete)
T stopped by job control signal
t stopped by debugger during the tracing
W paging (not valid since the 2.6.xx kernel)
X dead (should never be seen)
Z defunct ("zombie") process, terminated but not reaped by its parent
Status addition mean
< high-priority (not nice to other users)
N low-priority (nice to other users)
L has pages locked into memory (for real-time and custom IO)
s is a session leader
l is multi-threaded (using CLONE_THREAD, like NPTL pthreads do)
+ is in the foreground process group

Php executing a bash script with at command

Hello I'am trying to execute a shell command in my php script but it is not working.
My php script :
//I save the Order
$holdedOrder->save();
$id = $holdedOrder->id;
$old_path = getcwd();
chdir(__DIR__.'/../');
$scriptFile = 'anacron_job_unhold_order.sh';
$bool = file_exists($scriptFile);
//$bool is true !
//this command works in shell but not in here do not know why
$s = shell_exec("echo \"/usr/bin/bash $scriptFile $id\" | /usr/bin/at now +$when");
chdir($old_path);
return [$s,$bool];
$when has a valid value 4 hours or 4 days ...
The command will be :
echo bash anacron_job_unhold_order.sh 29 | at now +1 minutes
the output is null. Trying it with exec() is returning 127 code
Edit :
I removed www-data from /etc/at.deny and still the same problem
The command shell_exec("echo \"/usr/bin/bash $scriptFile $id\" | /usr/bin/at now +$when"); is probably causing the issue, you should take a closer look on how at works.
The command should be something like
at [options] [job...]
To give a job to at from the command instead of STDIN, use heredoc syntax
at now + 1 minute <<< 'touch /tmp/test'
So the PHP code should be something like;
$s = shell_exec("/usr/bin/at now + {$when} <<< '/usr/bin/bash {$scriptFile} {$id}'");
exec($s, $output, $return_var);

PHP CLI multiple background processes limitation

Server Information:
CentOS 6.5
12GB RAM
Intel(R) Xeon(R) CPU E5-2430 # 6 CPU x 2.20GHz
PHP CLI 5.5.7
I am currently trying to use Perl to fire off 1000 PHP CLI processes in parallel. This however takes 9.9 seconds vs 2.3 seconds for the equivalent Perl script. When I test using the Perl script /opt/test.pl, all 1000 processes are launched in parallel (ps -eLf | grep -ic 'test.pl'). When I test using /opt/testphp.php, using ps -eLf | grep -ic 'testphp.php', I see a count of 250, then it rises to 580 and then it drops to 0 (the script is executed 1000 times, just not in parallel).
Is there a limitation preventing a high number of PHP CLI processes from being launched in parallel?
Has anyone experienced this issue?
Please let me know if I have left out anything that would help to identify the issue.
Thanks
Perl launcher script:
use Time::HiRes qw/ time sleep /;
my $command = '';
my $start = time;
my $filename = '/tmp/report.txt';
# open(my $fh, '>', $filename) or die "Could not open file '$filename' $!";
for $i(1 .. 1000) {
# $command = $command . "(perl /opt/test.pl &);"; // takes 2.3 seconds
$command = $command . "(php -q /opt/testphp.php &);"; // takes 9.9 seconds
}
system($command);
my $end = time;
print 'Total time taken: ', ( $end - $start ) , "\n";
PHP file (testphp.php):
sleep(5);
$time = microtime(true);
file_put_contents('/tmp/report_20140804_php.log', "This is the record: $time\n", FILE_APPEND);
Perl file (test.pl):
#! /usr/bin/perl
use Time::HiRes qw/ time sleep /;
sleep(5);
my $command = '';
my $start = time;
my $filename = '/tmp/report_20140804.log';
open(my $fh, '>>', $filename) or die "Could not open file '$filename' $!";
print $fh "Successfully saved entry $start\n";
close $fh;

Continuing through loop without having to wait foreach file to load

I'm using a for loop to speed up my script. The problem is that each process that happens inside of the loop takes several minutes to load. Is it possible to move on the next sequence in the loop if the previous one hasn't completed? I know that PHP isn't a multi-threaded language, so perhaps Python would be a better choice.
ini_set('memory_limit', '2048M');
ini_set('max_execution_time', 0);
$list = file_get_contents('auth.txt');
$list = nl2br($list);
$exp = explode('<br />', $list);
$count = count($exp);
for($i=0;$i<$count;$i++) {
$auth = $exp[$i];
echo 'Trying '.$auth.' \n';
// This takes several minutes. Is it possible to move on to the next one before it has completed?
exec('python test.py --auth='.$auth);
}
Use & to run script in the background:
exec('python test.py --auth='.$auth . ' > /dev/null 2>&1 &');

Linux : Get total cpu usage by httpd

I need to display the total Percentage of CPU utilized by httpd processes on a server in a php report.
I am calling following from exec :
ps -e -o %mem,%cpu,cmd | grep httpd | awk ' {memory+=$1;cpu+=$2} END {printf("%05.2f ",memory);printf("%05.2f\n",cpu)}'
But the above command's reported CPU usage and the one reported by top command are not matching.
I need to report --> If CPU is busy at 40%, 10% of httpd processes, 20% of mysqld processes, 10% of perl processes, then I need to report the 10% of httpd. (Assuming that there are no other processes).
I saw this : get apache total cpu usage in (linux)
But I understand that ps command returns the percentage of CPU consumed by a process out of the total percentage of CPU consumed. I understand that it is getting messy, so the below example should help.
If httpd is consuming 10% of CPU which is busy at 60% then the actual contribution of httpd to make CPU busy was ((100/60)*10) = 16.66 %. Is this correct? What else are the best way to get cpu usage by a group of processes by the same name.
try this in ssh
ps aux | grep "httpd" | awk '{sum1 +=$3}; END {print sum1}'
output is:
10.5
and this for sum of memory
ps aux | grep "httpd" | awk '{sum1 +=$4}; END {print sum1}'
I'm not 100% sure on what you're asking, but if I'm right, this answer might help you:
<?php
exec('ps -aux', $processes);
foreach($processes as $process){
$cols = split(' ', ereg_replace(' +', ' ', $process));
if (strpos($cols[2], '.') > -1){
$cpuUsage += floatval($cols[2]);
}
}
print($cpuUsage);
?>
and after searching many forms also found the another way:
after searching on forums and trying many methods but I have not tried it:
$stat1 = file('/proc/stat');
sleep(1);
$stat2 = file('/proc/stat');
$info1 = explode(" ", preg_replace("!cpu +!", "", $stat1[0]));
$info2 = explode(" ", preg_replace("!cpu +!", "", $stat2[0]));
$dif = array();
$dif['user'] = $info2[0] - $info1[0];
$dif['nice'] = $info2[1] - $info1[1];
$dif['sys'] = $info2[2] - $info1[2];
$dif['idle'] = $info2[3] - $info1[3];
$total = array_sum($dif);
$cpu = array();
foreach($dif as $x=>$y) $cpu[$x] = round($y / $total * 100, 1);
This works for me on OSX:
<?php
exec('ps -e -o %mem,%cpu,command | grep httpd', $output);
$proc_data = [];
foreach($output as $key => $value) {
// Make sure it's only path httpd and not the grep included
if (strstr($value, '/httpd')) {
$info = explode(' ', trim($value), 5);
unset($info[1]);
unset($info[2]);
$proc_data[] = array_merge($info);
}
}
echo '<pre>';
print_r($proc_data);
echo '</pre>';
// Caclulate total CPU percentages
$total_cpu = 0;
foreach ($proc_data as $key => $value) {
$total_cpu += $value[1];
}
echo $total_cpu;
?>
This is the Terminal output for the bash:
MacBook-Pro:~ user$ ps -e -o %mem,%cpu,command | grep httpd
0,2 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,1 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,1 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 9,0 /Applications/MAMP/Library/bin/httpd -k start
0,0 0,0 grep httpd
I noticed ps -e -o %mem,%cpu,cmd has to be command, but it might be an OSX-only thing tho. Hope you can work with this.
Good luck!

Categories