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.
Related
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:
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
I have a project that needs to send notifications via WebSockets continuously. It should connect to a device that returns the overall status in string format. The system processes it and then sends notifications based on various conditions.
Since the scheduler can repeat a task as early as a minute, I need to find a way to execute the function every second.
Here is my app/Console/Kernel.php:
<?php
...
class Kernel extends ConsoleKernel
{
...
protected function schedule(Schedule $schedule)
{
$schedule->call(function(){
// connect to the device and process its response
})->everyMinute();
}
}
PS: If you have a better idea to handle the situation, please share your thoughts.
Usually, when you want more granularity than 1 minute, you have to write a daemon.
I advise you to try, now it's not so hard as it was some years ago. Just start with a simple loop inside a CLI command:
while (true) {
doPeriodicStuff();
sleep(1);
}
One important thing: run the daemon via supervisord. You can take a look at articles about Laravel's queue listener setup, it uses the same approach (a daemon + supervisord). A config section can look like this:
[program:your_daemon]
command=php artisan your:command --env=your_environment
directory=/path/to/laravel
stdout_logfile=/path/to/laravel/app/storage/logs/your_command.log
redirect_stderr=true
autostart=true
autorestart=true
for per second you can add command to cron job
* * * * * /usr/local/php56/bin/php56 /home/hamshahr/domains/hamshahrapp.com/project/artisan taxis:beFreeTaxis 1>> /dev/null 2>&1
and in command :
<?php
namespace App\Console\Commands;
use App\Contracts\Repositories\TaxiRepository;
use App\Contracts\Repositories\TravelRepository;
use App\Contracts\Repositories\TravelsRequestsDriversRepository;
use Carbon\Carbon;
use Illuminate\Console\Command;
class beFreeRequest extends Command
{
/**
* The name and signature of the console command.
*
* #var string
*/
protected $signature = 'taxis:beFreeRequest';
/**
* The console command description.
*
* #var string
*/
protected $description = 'change is active to 0 after 1 min if yet is 1';
/**
* #var TravelsRequestsDriversRepository
*/
private $travelsRequestsDriversRepository;
/**
* Create a new command instance.
* #param TravelsRequestsDriversRepository $travelsRequestsDriversRepository
*/
public function __construct(
TravelsRequestsDriversRepository $travelsRequestsDriversRepository
)
{
parent::__construct();
$this->travelsRequestsDriversRepository = $travelsRequestsDriversRepository;
}
/**
* Execute the console command.
*
* #return mixed
*/
public function handle()
{
$count = 0;
while ($count < 59) {
$startTime = Carbon::now();
$this->travelsRequestsDriversRepository->beFreeRequestAfterTime();
$endTime = Carbon::now();
$totalDuration = $endTime->diffInSeconds($startTime);
if($totalDuration > 0) {
$count += $totalDuration;
}
else {
$count++;
}
sleep(1);
}
}
}
$schedule->call(function(){
while (some-condition) {
runProcess();
}
})->name("someName")->withoutOverlapping();
Depending on how long your runProcess() takes to execute, you can use sleep(seconds) to have more fine tuning.
some-condition is normally a flag that you can change any time to have control on the infinite loop. e.g. you can use file_exists(path-to-flag-file) to manually start or stop the process any time you need.
this might be a bit old question but i recommend you to take a look at
laravel-short-schedule package from spatie
it allows you to run a commands at sub-minute or even sub-second
You can try and duplicate the jobs every second * 60 times using sleep(1).
After a slight modification of my units, I wanted the update with a simple php app/console doctrine: update --force. But no action executed and in addition no response. I then did a php app/check.php meaning me no problems (Your system is ready to run Symfony2 projects). I do not understand and it doesn't provide an error. Here's what I've done:
Command: ********: ***** ProjetSymphony $ php app / console***
Answer (none): ******* **** $ ProjetSymphony***
If someone has an idea.
Screen :
Try with:
php app/console doctrine:schema:update --force
Maybe it's only a syntaxis error.
Also, if anyone tries to run php app/console in a newer symfony version (for example symfony 3.0), you will get an error: no file found because the file was moved to 'bin' folder. Now to run from the console, you have to use php bin/console instead. Just in case this change confused anyone who started to learn symfony and updated to 3.0.
I finally found my mistake. I had a command file that prevented the execution of my order (CreateUserCommand.php)
If someone wants to explain to me why this cosait file an error during the execution of my order ...
Here is the file :
<?php
namespace FP\UserBundle\Command;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use FOS\UserBundle\Model\User;
use FOS\UserBundle\Command\CreateUserCommand as BaseCommand;
class CreateUserCommand extends BaseCommand
{
/**
* #see Command
*/
protected function configure()
{
exit;
echo "tes";
parent::configure();
$this
->setName('fp:user:create')
->getDefinition()->addArguments(array(
new InputArgument('age', InputArgument::REQUIRED, 'The age')
))
;
}
/**
* #see Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
exit;
echo "tes";
$username = $input->getArgument('username');
$email = $input->getArgument('email');
$password = $input->getArgument('password');
$age = $input->getArgument('age');
$inactive = $input->getOption('inactive');
$superadmin = $input->getOption('super-admin');
$manipulator = $this->getContainer()->get('fos_user.util.user_manipulator');
$manipulator->setAge($age);
$manipulator->create($username, $password, $email, !$inactive, $superadmin);
$output->writeln(sprintf('Created user <comment>%s</comment>', $username));
}
/**
* #see Command
*/
protected function interact(InputInterface $input, OutputInterface $output)
{
exit;
echo "tes";
parent::interact($input, $output);
if (!$input->getArgument('age')) {
$age = $this->getHelper('dialog')->askAndValidate(
$output,
'Please choose a age:',
function($age) {
if (empty($age)) {
throw new \Exception('Lastname can not be empty');
}
return $age;
}
);
$input->setArgument('age', $age);
}
}
}
Symfony2 enables developers to create their own command-line commands. They can be executed from command line, but also from the controller. According to official Symfony2 documentation, it can be done like that:
protected function execute(InputInterface $input, OutputInterface $output)
{
$command = $this->getApplication()->find('demo:greet');
$arguments = array(
...
);
$input = new ArrayInput($arguments);
$returnCode = $command->run($input, $output);
}
But in this situation we wait for the command to finish it's execution and return the return code.
How can I, from controller, execute command forking it to background without waiting for it to finish execution?
In other words what would be equivalent of
$ nohup php app/console demo:greet &
From the documentation is better use start() instead run() if you want to create a background process. The process_max_time could kill your process if you create it with run()
"Instead of using run() to execute a process, you can start() it: run() is blocking and waits for the process to finish, start() creates a background process."
According to the documentation I don't think there is such an option: http://api.symfony.com/2.1/Symfony/Component/Console/Application.html
But regarding what you are trying to achieve, I think you should use the process component instead:
use Symfony\Component\Process\Process;
$process = new Process('ls -lsa');
$process->run(function ($type, $buffer) {
if ('err' === $type) {
echo 'ERR > '.$buffer;
} else {
echo 'OUT > '.$buffer;
}
});
And as mentioned in the documentation "if you want to be able to get some feedback in real-time, just pass an anonymous function to the run() method".
http://symfony.com/doc/master/components/process.html