I am using Symfony 3 framework with pheanstalk php library. I run the app on server with Linux Debian Jesse. The job creation works ok and if run the worker from terminal it works like it should. But when I added the command to crontab I see that the command do not work. There is not any log in /var/main/user to help me with debuging. I will be very glad for any help.
This is my worker command (only the execute function):
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln("\n<info>Beanstalk worker service started</info>");
$tubes = $this->getContainer()->get('app.job_manager.power_plant')->getTubes();
if ($input->getOption('one-check')) {
// run once
foreach ($tubes as $tubeName) {
if ($tubeName != "default") {
$this->getContainer()->get('app.queue_manager')->fetchQueue($tubeName);
}
}
$output->writeln("\n<info>Beanstalk worker completed check and stoped</info>");
} else {
// run forever
set_time_limit(0);
max_execution_time(0);
while (1) {
foreach ($tubes as $tubeName) {
if ($tubeName != "default") {
$this->getContainer()->get('app.queue_manager')->fetchQueue($tubeName);
}
}
}
$output->writeln("\n<info>Beanstalk worker stoped</info>");
}
}
This is my app.queue_manager function to get job from queue and run command:
public function fetchQueue($tubeName)
{
if ($this->pheanstalk->getConnection()->isServiceListening()) {
while (true === is_object($job = $this->pheanstalk->watch($tubeName)->ignore('default')->reserve(self::WATCH_TIMEOUT))) {
$data = json_decode($job->getData(), true);
$this->worker($data);
$this->pheanstalk->delete($job);
}
}
}
And the worker to run command
public function worker($data)
{
$application = new Application($this->kernel);
$application->setAutoExit(false);
$parameters = [];
$parameters['command'] = $data['command'];
foreach ($data['meta'] as $key => $param) {
$parameters[$key] = $param;
}
$input = new ArrayInput($parameters);
$output = new NullOutput();
return $application->run($input, $output);
}
This is my crontab that do not work:
#reboot /usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start
I created another cron tab that works ok. It works every 15 min, and the difference is that do not have infinite loop (while(1)) so it goes only once thru tubes and than finished. But it is not what i want. I want infinite loop that works all the time like I created it with first crontab:
*/15 * * * * /usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start --one-check
If it works in the console, it doesn't work for your file user permissions. Check it.
You can report your error by email with this job
*/15 * * * * /usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start --one-check 2>&1 | mail -s "mysql_dump" example#mail.example
I used rc.local. It solve the problem. Tnx FreudianSlip
Related
I want to add cron job dynamically once user install a php application on their server, like admin configuration, I need to set cron job dynamically once going thought its configuration settings in php?
I am using codeigniter to set the cron job, also I added the same from cpanel manually and it is working fine.
You can create a file in
/etc/cron.d
and use PHP's file_put_contents() to update the file. However, you'll need to elevate PHP or Apache's permissions (depending on your PHP handler). This isn't recommended since it leads to security issues.
If you're using Cron to run PHP scripts, then you can call the scripts directly using PHP every X amount of time. Create a variable in your database representing the last time your script was run. If X amount of time passed since the script was run, then you run the script and update the variable.
If the script takes a long time to execute, then use PHP to run it in a separate process, so the user doesn't have to wait for it to finish.
Thanks
First You have to create one txt file for example crontab.txt
then you have to use shell script like below
exec ( 'sudo crontab -u apache -r' );
file_put_contents ( '/var/www/html/YOUR_PROJECT/crontab.txt',"25 15 * * * php /var/www/html/YOUR_PROJECT/YOUR_CONTROLLER/YOUR_METHOD'.PHP_EOL);
And Lastly, you have to execute that file like
exec ( 'crontab /var/www/html/YOUR_PROJECT/crontab.txt' );
Hope this will help you.
you can do this using shell script
shell_exec('echo "25 15 * * * <path to php> /var/www/cronjob/helloworld.php > /var/www/cronjob/cron.log" | crontab -')
you can use these functions or class
class Crontab {
// In this class, array instead of string would be the standard input / output format.
// Legacy way to add a job:
// $output = shell_exec('(crontab -l; echo "'.$job.'") | crontab -');
static private function stringToArray($jobs = '') {
$array = explode("\r\n", trim($jobs)); // trim() gets rid of the last \r\n
foreach ($array as $key => $item) {
if ($item == '') {
unset($array[$key]);
}
}
return $array;
}
static private function arrayToString($jobs = array()) {
$string = implode("\r\n", $jobs);
return $string;
}
static public function getJobs() {
$output = shell_exec('crontab -l');
return self::stringToArray($output);
}
static public function saveJobs($jobs = array()) {
$output = shell_exec('echo "'.self::arrayToString($jobs).'" | crontab -');
return $output;
}
static public function doesJobExist($job = '') {
$jobs = self::getJobs();
if (in_array($job, $jobs)) {
return true;
} else {
return false;
}
}
static public function addJob($job = '') {
if (self::doesJobExist($job)) {
return false;
} else {
$jobs = self::getJobs();
$jobs[] = $job;
return self::saveJobs($jobs);
}
}
static public function removeJob($job = '') {
if (self::doesJobExist($job)) {
$jobs = self::getJobs();
unset($jobs[array_search($job, $jobs)]);
return self::saveJobs($jobs);
} else {
return false;
}
}
}
Hi thank you for the replies, i got my answers now, based on the replies
I tried to add a new cron job to crontab file using php (codeigniter):
following is my answer
$phppath = exec('which php');
$user_file_path = getcwd();
$cronjob1 = "0 0 1 * * $phppath $user_file_path/index.php
automatic_updates/leave_update_cron";
// run each cron job
// get all current cron jobs
$output = shell_exec('crontab -l');
// add our new job
file_put_contents('/tmp/crontab.txt', $output.$cronjob1.PHP_EOL);
// once append the job, execute the new file
exec('crontab /tmp/crontab.txt');
This will add a new cron job without deleting anything on the current cron job file
I have a hosting platform that at midnight resets all cronjob added on there system to 15 from what ever they where set to. Our plan is to set up a script that run's every 8hrs and resets these back, so the process they manage are minimally interrupted. It is shared hosting, but hosting companies solution it an upgrade and a massive rise in costs.
The server specs are cPanel Version - 70.0 (build 69) | Apache Version - 2.4.39 |PHP Version - 7.2.18 |MySQL Version - 10.2.24-MariaDB-cll-lve |Architecture - x86_64 | Operating System - linux
We have been suggested using a PHP class called Crontab to achieve this.
When we add a job, it appears in our cron job section of our server, but if we try to delete it from the server we get the following error 'Error New Lines are not permitted in crontab entries'. How ever we can remove the job via the Crontab class.
<?php
class Crontab {
static private function stringToArray($jobs = '') {
$array = explode("\r\n", trim($jobs)); // trim() gets rid of the last \r\n
foreach ($array as $key => $item) {
if ($item == '') {
unset($array[$key]);
}
}
return $array;
}
static private function arrayToString($jobs = array()) {
$string = implode("\r\n", $jobs);
return $string;
}
static public function getJobs() {
$output = shell_exec('crontab -l');
return self::stringToArray($output);
}
static public function saveJobs($jobs = array()) {
$output = shell_exec('echo "'.self::arrayToString($jobs).'" | crontab -');
return $output;
}
static public function doesJobExist($job = '') {
$jobs = self::getJobs();
if (in_array($job, $jobs)) {return true;} else {return false;}
}
static public function addJob($job = '') {
if (self::doesJobExist($job)) {
return false;
} else {
$jobs = self::getJobs();
$jobs[] = $job;
return self::saveJobs($jobs);
}
}
static public function removeJob($job = '') {
if (self::doesJobExist($job)) {
$jobs = self::getJobs();
unset($jobs[array_search($job, $jobs)]);
return self::saveJobs($jobs);
} else {
return false;
}
}
}
//$output = shell_exec('crontab -l'); //Check server output
//echo nl2br($output); //Make it readable
Crontab::addJob('*/15 * * * * /opt/alt/php56/usr/bin/php -q /home/path/bob.php process-log >/dev/null 2>&1');
Crontab::removeJob('*/15 * * * * /opt/alt/php56/usr/bin/php -q /home/path/bob.php process-log >/dev/null 2>&1');
?>
The reason this is a concern is that for there to be a server error, then code inputting the cronjob must not be doing it right, I have hear of issue when running similar processes on windows servers, but the php file running the code is in the root of the linux server, so wasn't expecting to have issues.
IF someone could explain why this error is occurring and how to fix it, that would be greatly appreciated, let me know if you need any more info.
Cheers in advance.
When using the Laravel5 scheduler:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
We receive the following default output if no command is ready to run:
# No scheduled commands are ready to run.
How to disable this default Laravel5 message? We don't want to have an output if there is no command ready to run. The best would be, when we were able to configure that message and return code on our self.
You can create a new command in app/Console/Commands similar to below, which extends the default schedule:run command.
It overrides the handle method while leaving everything else as-is to avoid having Laravel output the "No scheduled commands are ready to run." line when it didn't do anything.
By using a different name there's no need to worry about conflicts, and you can still run the original php artisan schedule:run command at any time if you so desire.
<?php
namespace App\Console\Commands
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Console\Scheduling\ScheduleRunCommand;
class RunTasks extends ScheduleRunCommand
{
/**
* The console command name.
*
* #var string
*/
protected $name = 'run:tasks';
/**
* The console command description.
*
* #var string
*/
protected $description = 'Custom task runner with no default output';
/**
* Create a new command instance.
*
* #param \Illuminate\Console\Scheduling\Schedule $schedule
* #return void
*/
public function __construct(Schedule $schedule)
{
parent::__construct($schedule);
}
/**
* Execute the console command.
*
* #return void
*/
public function handle()
{
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
continue;
}
if ($event->onOneServer) {
$this->runSingleServerEvent($event);
} else {
$this->runEvent($event);
}
$this->eventsRan = true;
}
if (! $this->eventsRan) {
// Laravel would output the default text here. You can remove
// this if statement entirely if you don't want output.
//
// Alternatively, define some custom output with:
// $this->info("My custom 'nothing ran' message");
}
}
}
Verify that Laravel sees your new command:
php artisan | grep run:tasks
Finally update your cron to run the new command:
* * * * * cd /path-to-your-project && php artisan run:tasks >> /dev/null 2>&1
As I mentioned in the comments I see two possibilities
You can filter the output by removing what you don't want
* * * * * cd /path-to-your-project && php artisan schedule:run | awk '{ if (/No scheduled commands are ready to run./ && !seen) { seen = 1 } else print }'
Or you can override with your own command:
$ php artisan make:command ScheduleRunCommand
By making your own command (mostly copy/past from ScheduleRunCommand) or extending the ScheduleRunCommand as #dave-s proposed
And if you want to still run php artisan schedule:run with your new command,
you need to register it in a service provider
$this->app->extend('schedule.run', function () {
return new \App\Console\Commands\ScheduleRunCommand;
});
If you look at the code for Laravel at https://github.com/laravel/framework/blob/78505345f2a34b865a980cefbd103d8eb839eedf/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php#L82
public function handle()
{
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
continue;
}
if ($event->onOneServer) {
$this->runSingleServerEvent($event);
} else {
$this->runEvent($event);
}
$this->eventsRan = true;
}
if (! $this->eventsRan) {
$this->info('No scheduled commands are ready to run.');
}
}
You see that it's handled via $this->info handler.
The info handler is defined in Command.php Which calls the line method, which calls the output handler which is defined in the run command
So in essence to be able to intercept this you should be able to override the OutputStyle which is based on the symfonystyle by binding your own output handler before running the commands in the file you call in your cron job.
The best working scenario I can think of is by using an OutputFormatter where you simply return null when the string matches your target string.
$this->output->setFormatter( new MyCatchemAllFormatter() );
And in the class you would define something along the lines of:
use Symfony\Component\Console\Formatter\OutputFormatter;
class MyCatchemAllFormatter extends OutputFormatter
{
public function formatAndWrap(string $message, int $width)
{
if($message != 'No scheduled commands are ready to run.') {
return parent::formatAndWrap($message, $width);
}
return null;
}
}
I understand that my solution is DIRTY and I'll get downvotes by most of SO users, but it's quick to do without registering additional service providers, modifying classes and etc.
I've checked sources and found this at line 81 of ScheduleRunCommand which is
public function handle()
{
foreach ($this->schedule->dueEvents($this->laravel) as $event) {
if (! $event->filtersPass($this->laravel)) {
continue;
}
if ($event->onOneServer) {
$this->runSingleServerEvent($event);
} else {
$this->runEvent($event);
}
$this->eventsRan = true;
}
if (! $this->eventsRan) { // L81
$this->info('No scheduled commands are ready to run.'); // L82
}
}
The quickest way to "cheat" with it is to copy that class to app/Console/ScheduleRunCommand.php and copy that file to original source path every-time when composer dump-autoload called.
1) Copy original file to app/Console folder:
cp vendor/laravel/framework/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php patch/ScheduleRunCommand.php app/Console/ScheduleRunCommand.php
2) add such line in composer.json scripts:post-autoload-dump section:
cp app/Console/ScheduleRunCommand.php vendor/laravel/framework/src/Illuminate/Console/Scheduling/ScheduleRunCommand.php
3) modify Your message in app/Console/ScheduleRunCommand.php (L82):
if (! $this->eventsRan) {
$this->info('NOTHING');
}
4) run: composer dump
and result:
Hellow , I need some hel regarding executing symfony commands inside another command. I am not new with this and I created many commands and run them from inside commands, controllers and it always work. But this one I do not understand why It do not working like the others are. I am running one command all the time and from time to time I created some extra workers when there are many jobs to get this one worker some help (one-check option).
I created command to run beanstalk worker with this class:
<?php
namespace AppBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Command\LockableTrait;
/**
* Class BeanstalkWorkerCommand
* Command start the Beanstalk worker. Get the job from queue and preform job
*
* #method configure()
* #method execute(InputInterface $input, OutputInterface $output)
* #method authenticateUser(InputInterface $input, OutputInterface $output)
* #method checkAuthentication($username, $password)
*/
class BeanstalkWorkerCommand extends ContainerAwareCommand
{
use LockableTrait;
protected function configure()
{
$this
->setName('beanstalk:worker:start')
->setDescription('Start the Beanstalk infinitive worker. Get the job from queue and preform job')
->addOption(
'one-check',
'o',
InputOption::VALUE_NONE,
'If set, the worker will check tubes only once and died if there is no jobs in queue'
)
;
}
protected function execute(InputInterface $input, OutputInterface $output)
{
$output->writeln("\n<info>Beanstalk worker service started</info>");
$tubes = $this->getContainer()->get('app.job_manager.power_plant')->getTubes();
if ($input->getOption('one-check')) {
// run once
foreach ($tubes as $tubeName) {
if ($tubeName != "default") {
$this->getContainer()->get('app.queue_manager')->fetchQueue($tubeName);
}
}
$output->writeln("\n<info>Beanstalk worker completed check and stoped</info>");
} else {
// run forever
set_time_limit(0);
ini_set('xdebug.max_nesting_level', 1000);
if (!$this->lock()) {
$output->writeln('The command is already running in another process.');
return 0;
}
while (1) {
foreach ($tubes as $tubeName) {
if ($tubeName != "default") {
$this->getContainer()->get('app.queue_manager')->fetchQueue($tubeName);
}
sleep(0.1);
}
}
$output->writeln("\n<error>Beanstalk worker service has stoped</error>");
}
}
}
Than I run another command to create some extra workers with this functions:
public function startExtraWorkers(OutputInterface $output)
{
$numOfExtraWorkers = $this->getContainer()->getParameter('num_of_extra_beanstalk_workers');
for ($i=0; $i < $numOfExtraWorkers; $i++) {
$payload = [
"--one-check" => TRUE
];
$this->getContainer()->get('app.job_manager.queue')->createAndSendToQueue('beanstalk:worker:start', $payload, 10);
}
$output->writeln("\n<question>".$numOfExtraWorkers." extra benastalk workers started!</question>");
return TRUE;
}
public function createAndSendToQueue($command, $payload, $priority = 65536)
{
$jobData = $this->createJob($command, $payload);
return $this->job->enqueue($command, $jobData, $priority);
}
public function enqueue($job, array $args, $priority = 65536, $delay = 0, $ttr = 120)
{
if (!preg_match('/[a-z0-9\.]+/i', $job)) {
throw new InvalidArgumentException("Invalid job name");
}
$args = json_encode($args);
return $this->pheanstalk->put($args, $priority, $delay, $ttr);
}
And the problem is that if I run this command from terminal or with cron job it forks but if i run it like that with this function it do not work. I see that command has been executed but for some unknown reason it do not work.
If I executed this command i can see all commands has been executed bot they do not perform job like if i run the same command from terminal or with cron job:
ps ax |grep "beanstalk:worker:start --one-check"
Output (first one has been run from this function and second one with cron job. And only second one works):
31934 ? Ss 0:00 /bin/sh -c /usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start --one-check
31935 ? S 0:00 /usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start --one-check
Can any one give me some advice why is this not working like other commands? And why the same command run OK if i run it with cron job or inside terminal?
Thanks for help!
Try using:
/bin/sh -c '/usr/bin/php /var/www/mose-base/bin/console beanstalk:worker:start --one-check'
Mind the quotes.
I have a cron file cron/cron1.php. i have set up this for cron running 1 minute.
so for next process it will take 1 minute to execute.
now i want to run this file parallel three times in minute. this file takes execution time more than 2 min.
can i run this file parallel in a single file like this
file1.php
<?php
include("cron/cron1.php"); // run seperately
sleep(5);
include("cron/cron1.php"); // run seperately
sleep(5);
include("cron/cron1.php"); // run seperately
?>
in above file cron1.php will execute 5 seconds difference but when above one is completed its process. as i told you each cron1.php will takes more than 2 minutes to complete. so i couldn't achieve it.
is there any process or multithreading or approch so that i can run each cron1.php every 5 seconds delay. then i will set the file1.php as a cron job.
PHP DOES SUPPORT MULTI-THREADING
http://php.net/pthreads
Here is a multi-threaded example of the kind of logic you require:
<?php
define("SECOND", 1000000);
define("LOG", Mutex::create());
/*
* Log safely to stdout
* #param string message the format string for log
* #param ... args the arguments for sprintf
* #return void
*/
function slog($message, $args = []) {
$args = func_get_args();
if ((count($args) > 0) &&
($message = array_shift($args))) {
$time = microtime(true);
Mutex::lock(LOG);
echo vsprintf(
"{$time}: {$message}\n", $args);
Mutex::unlock(LOG);
}
}
class MyTask extends Thread {
public $id;
public $done;
public function __construct($id) {
$this->id = $id;
$this->done = false;
}
public function run() {
slog("%s#%d entered ...", __CLASS__, $this->id);
/* don't use sleep in threads */
$this->synchronized(function(){
/* simulate some work */
$this->wait(10 * SECOND);
});
slog("%s#%d leaving ...", __CLASS__, $this->id);
$this->done = true;
}
}
$threads = [];
function get_next_id(&$threads) {
foreach ($threads as $id => $thread) {
if ($thread->done) {
return $id;
}
}
return count($threads);
}
do {
slog("Main spawning ...");
$id = get_next_id($threads);
$threads[$id] = new MyTask($id);
$threads[$id]->start();
slog("Main sleeping ...");
usleep(5 * SECOND);
} while (1);
?>
This will spawn a new thread every 5 seconds, the threads take 10 seconds to execute.
You should try to find ways of increasing the speed of individual tasks, perhaps by sharing some common set of data.
What you could do is run multiple processes at the same time, with something like this:
exec('php cron/cron1.php > /dev/null 2>&1 &');
exec('php cron/cron1.php > /dev/null 2>&1 &');
exec('php cron/cron1.php > /dev/null 2>&1 &');
Each exec call will run in the background so you can have as many as needed.