6

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.

Gokul P P
  • 962
  • 8
  • 27
Naveed
  • 879
  • 1
  • 9
  • 20
  • 1
    A daemon using an event loop which triggers every second. You can use a library such as [icicle](https://icicle.io/) for this task and [supervisord](http://supervisord.org/) as the manager which will boot up the process if it exits unexpectedly. This might seem as an overkill, but certain things do look simple until you get to the core of the problem. If you need continuous updates, this is the way to go. – Mjh Jul 27 '16 at 09:08

5 Answers5

8

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
Alexey Shokov
  • 4,775
  • 1
  • 21
  • 22
  • Would you mind clarifying why supervisord should be run? – Eugene van der Merwe Dec 01 '18 at 16:22
  • supervisord is needed to restart the script automatically if it fails, to log STDOUT and STDERR output to a log file a do other infrastructure things that you don't want to add to your PHP code. – Alexey Shokov Dec 05 '18 at 16:24
  • btw no need of supervisord, you can also use systemctl for the same purpose. The reason laravel uses supervisord is because it better at spawning multiple processes at the same time required for queues, but for this case it has no benefits. – Bernard Wiesner Dec 15 '21 at 23:10
2

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);
        }

    }
}
Hamid Naghipour
  • 3,465
  • 2
  • 26
  • 55
  • Note: you might want to implement this command with a locking mechanism and retries, because if the last task is taking a few seconds to complete (in the minute interval), it will overlap with the new minute first task. – Binar Web Jul 19 '21 at 13:11
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.

Inno
  • 46
  • 4
0

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

Ahmed Aboud
  • 1,232
  • 16
  • 19
-3

You can try and duplicate the jobs every second * 60 times using sleep(1).

Riyaz Shaikh
  • 90
  • 1
  • 8