Concurrent Conflict Problem in crond Script Execution

Posted by Hangston on Sat, 22 Jun 2019 02:41:51 +0200

In planning tasks, you will occasionally see repetitive execution:

Examples of our company's planned tasks are as follows:

*/2 * * * * root cd /opt/xxxx/test_S1/html/xxxx/admin; php index.php task testOne >/dev/null 2>&1
*/2 * * * * root cd /opt/xxxx/test_S1/html/xxxx/admin; php index.php task testTwo >/dev/null 2>&1

This is a two-minute task. It does not guarantee that every process can be completely closed in two minutes. If the process keeps piling up, the system resources may be exhausted, resulting in system downtime.

Give an example:

New test.php file, code as follows:

<?php
sleep(70);
?>

Add scheduled tasks:

*/1 * * * * root cd /home/ganjincheng;php test.php

Waiting for execution, piling up

root     26722  0.0  0.0   9232  1064 ?        Ss   12:05   0:00 /bin/sh -c cd /home/ganjincheng;php test.php
root     26744  0.0  0.0 112304  8840 ?        S    12:05   0:00 php test.php
root     29102  0.0  0.0   9232  1060 ?        Ss   12:06   0:00 /bin/sh -c cd /home/ganjincheng;php test.php
root     29116  0.1  0.0 112304  8840 ?        S    12:06   0:00 php test.php
root     29906  0.0  0.0 103320   904 pts/3    S+   12:06   0:00 grep test.php

Solution

The first is to control concurrency in code

This method is to transform the code. Increase the judgment of whether there is process execution or not. The following code:

<?php  
$lockfile = '/tmp/mytest.lock';  
   
if(file_exists($lockfile)){  
    exit();  
}
file_put_contents($lockfile, date("Y-m-d H:i:s"));
   
sleep(70);
 
unlink($lockfile);  
?>

This way of judging whether a document does not exist is problematic. That is, it is possible that the program has not been executed to the end, that is, the mytest.lock file created before has not been deleted. This will cause the program to fail to execute properly afterwards.

Second, database controls concurrency

The first scheme can be transferred to redis, memache for key value judgment.

If the planned task is to access the database, the lock table operation can be performed. Occasionally, we can also use the uniqueness of the unique index and the joint index to avoid repeated insertions.

Third, determine whether the process exists

Give an example:

$fp = popen("ps aux | grep 'test.php' | wc -l", "r");
$proc_num = fgets($fp);
if ($proc_num > 3) { //Here we should pay attention to why the number of processes is greater than 3. Once you have done it, you will understand.
    exit;
}
sleep(70);

The disadvantage of this method is that the ps command should be written accurately. Avoid counting processes that are not executing test.php scripts. Such as:
We open the test.php file through vim. This will cause the above commands to be mistakenly counted. So when we accidentally open the test.php file by vim, we can't execute it.

Fourth, use the flock command of linux

Let linux help us to judge ah, the flock command provides the function of file lock. The command parameters are as follows:

[root@qkzj_Multi-Purpose_1A_113.107.248.124 ganjincheng]# flock -h
flock (util-linux-ng 2.17.2)
Usage: flock [-sxun][-w #] fd#
       flock [-sxon][-w #] file [-c] command...
       flock [-sxon][-w #] directory [-c] command...
  -s  --shared     Get a shared lock
  -x  --exclusive  Get an exclusive lock
  -u  --unlock     Remove a lock
  -n  --nonblock   Fail rather than wait
  -w  --timeout    Wait for a limited amount of time
  -o  --close      Close file descriptor before running command
  -c  --command    Run a single command string through the shell
  -h  --help       Display this text
  -V  --version    Display version

Examples of configuration:

*/1 * * * * root flock -xn /tmp/mytest.lock -c 'php ./test.php'

Topics: PHP Linux Database vim