I have 2 laravel (5.2) apps sharing the same database. The 2 apps are used by 2 different user types. Now I want to get them connected to each other so that when a request is sent from a user using the first app to save data to specific database table, a notification is sent to the second app with this data. How can I do this ??
I did the following in the past:
Create a Queue-able job in APP1 with an empty handler.
Trigger a queued job in APP1
Create a Queue-able job in APP2 with a filled out handler.
Work the Queue in APP2 (php artisan queue:listen for example)
Then queue the job in APP1. For example:
# APP1 - trigger
\Queue::push(new \App\Jobs\EventToBeWorkedOnInApp1());
# app1/Jobs/EventToBeWorkedOnInApp1.php - job
class EventToBeWorkedOnInApp1 extends Job implements SelfHandling, ShouldQueue
{
use SerializesModels;
public function handle()
{
// #handler is in APP2
}
(...)
And in APP2 Create the handler for the job
# APP2
# app1/Jobs/EventToBeWorkedOnInApp1.php - job
class EventToBeWorkedOnInApp1 extends Job implements SelfHandling, ShouldQueue
{
use SerializesModels;
public function handle()
{
// WORK WITH THE JOB
}
(...)
Related
I've been implemented stripe webhook in laravel 7 using library https://github.com/spatie/laravel-stripe-webhooks
The goal is to subscribe my users to plan created in stripe and generate an invoice on charge succeeded webhook request. Now to achieve this I created a cron script that dispatches the job to subscribe users. Also, set up a webhook endpoint in stripe. All setup is done and configuration in environment variables. It works perfectly when set the queue connection to "sync". However, when setting the queue connection to Redis it doesn't work.
Here's the code inside my config/stripe-webhooks.php
<?php
return [
/*
* Stripe will sign each webhook using a secret. You can find the used secret at the
* webhook configuration settings: https://dashboard.stripe.com/account/webhooks.
*/
'signing_secret' => env('STRIPE_WEBHOOK_SECRET'),
/*
* You can define the job that should be run when a certain webhook hits your application
* here. The key is the name of the Stripe event type with the `.` replaced by a `_`.
*
* You can find a list of Stripe webhook types here:
* https://stripe.com/docs/api#event_types.
*/
'jobs' => [
'charge_succeeded' => \App\Jobs\StripeWebhooks\ChargeSucceededJob::class,
// 'source_chargeable' => \App\Jobs\StripeWebhooks\HandleChargeableSource::class,
// 'charge_failed' => \App\Jobs\StripeWebhooks\HandleFailedCharge::class,
],
/*
* The classname of the model to be used. The class should equal or extend
* Spatie\StripeWebhooks\ProcessStripeWebhookJob.
*/
'model' => \Spatie\StripeWebhooks\ProcessStripeWebhookJob::class,
];
And inside the command that dispatches SubscribeCustomerJob:
$subscribed = 0;
$users = User::role('admin')->where(function ($q) {
$q->where('stripe_id', '!=', null)->where('verified_employees', '>=', 5);
})->get();
foreach ($users as $user) {
if ( $user->subscribed('default') && now()->format('Y-m-d') >= $user->trial_ends_at->format('Y-m-d')) {
SubscribeCustomerJob::dispatch($user)->onQueue('api');
$subscribed++;
}
}
Handling webhook requests using jobs
<?php
namespace App\Jobs\StripeWebhooks;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Spatie\WebhookClient\Models\WebhookCall;
class HandleChargeableSource implements ShouldQueue
{
use InteractsWithQueue, Queueable, SerializesModels;
/** #var \Spatie\WebhookClient\Models\WebhookCall */
public $webhookCall;
public function __construct(WebhookCall $webhookCall)
{
$this->webhookCall = $webhookCall;
}
public function handle()
{
// At this point, I store data to Payments table,
// generate invoice and send email notification to subscribed user.
}
}
The output inside the logs of job:
[2021-04-14 07:53:46][Nr84GbvR3kxqnBGRrrWHtkTj34XRYsGv] Processing: App\Jobs\SubscribeCustomerJob
[2021-04-14 07:53:53][Nr84GbvR3kxqnBGRrrWHtkTj34XRYsGv] Processed: App\Jobs\SubscribeCustomerJob
Inside table in webhook_calls:
webhook_calls table click to see it
All thigs works well with queue connection set to sync. However, the problem is when I set queue connection to "redis".
I know webhook call works because there's data inside webhook_calls table but I guess fails to reach the job.
Any idea with stripe webhook and laravel using redis as queue driver, please add your comment below and thanks in advance.
I've fixed this issue it's my bad that I accidentally put an invalid queue name in my queue worker setup in supervisor.
[program:queue-worker]
process_name=%(program_name)s_%(process_num)02d
command=php /var/www/html/artisan queue:work --queue=api,notification
autostart=true
autorestart=true
user=root
numprocs=8
redirect_stderr=true
stdout_logfile=/var/www/html/worker.log
I have a table users with 5000 records (5000 users) and in my server i can just send 1000 emails every hour.
How i can send 1000 emails every hour using queue ?
or how to make queues sleep inside loop?
EmailController:
class EmailController extends Controller
{
public function sendEmail(Request $request){
$event=(object)['content' => "Hello Laravel fans",'subject' => 'Test Email'];
$users=User::all();
App\Jobs\SendReminderEmail::dispatch($users,$event)
}
}
SendReminderEmail
class SendReminderEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $event;
public $email;
public $users;
public function __construct($users,$event)
{
$this->users = $users;
$this->event = $event;
}
public function handle()
{
foreach ($this->users as $user) {
Mail::to($user->email)->queue(new Reminder($this->event));
}
}
}
Laravel has a neat feature which fits your case perfectly, it's called Task Scheduling, instead of making the job sleep for one hour, you could, instead, call it every hour.
To do so add the job schedule to the schedule() method located on App\Console\Kernel like this:
protected function schedule(Schedule $schedule)
{
$schedule->job(new SendReminderEmail)->hourly();
}
I also would recommend you to make the job self contained, that will make this task much simpler, I'm thinking in something like this:
class SendReminderEmail implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $event;
public $users;
public function __construct()
{
$this->users = User::all();
$this->event = (object)['content' => "Hello Laravel fans",'subject' => 'Test Email'];;
}
public function handle()
{
foreach ($this->users as $user) {
Mail::to($user->email)->queue(new Reminder($this->event));
}
}
}
Now you can just get rid of your controller because this job will be executed every hour automatically.
Remember that you will need to run a cron on your server to check if the job needs to be executed. You can also run it manually if you want to test it using php artisan schedule:run.
Hope this helps you.
Laravel has a built-in throttle feature for rate-limited jobs. From the docs:
If your application interacts with Redis, you may throttle your queued
jobs by time or concurrency. This feature can be of assistance when
your queued jobs are interacting with APIs that are also rate limited.
For example, using the throttle method, you may throttle a given type
of job to only run 10 times every 60 seconds. If a lock can not be
obtained, you should typically release the job back onto the queue so
it can be retried later:
Redis::throttle('key')->allow(10)->every(60)->then(function () {
// Job logic...
}, function () {
// Could not obtain lock...
return $this->release(10);
});
In your case, that might look like Redis::throttle(...)->allow(1000)->every(3600)->...
If you're not using Redis, another possible solution which is specific to queued mail is to add a delay. Again, from the docs:
Delayed Message Queueing
If you wish to delay the delivery of a queued
email message, you may use the later method. As its first argument,
the later method accepts a DateTime instance indicating when the
message should be sent:
$when = now()->addMinutes(10);
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->later($when, new OrderShipped($order));
Using this method would require you to calculate the delay for each email during the loop that dispatches emails to the queue. For example, every 1000th iteration of the loop you could increase the delay by 1 hour.
If you're using the Amazon SQS queue service, the same maximum delay of 15 minutes that applies to other queued jobs might also apply here (I'm not sure on this part), in which case you would have to come up with another solution for checking if you're over your rate limit and releasing the job back to the queue.
In Laravel 5.5, I've created a queue job like this:
class EmailJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
private $emailModel;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(MyEmail $emailModel)
{
//
$this->emailModel = $emailModel;
}
// ...
}
MyEmail is a model to store email data.
I use database driver on queue.
Now, I want to catch the failed job (in failed_jobs table), and link them to related MyEmail object (e.g: store in an attribute failed_job_id), then later on my app can manually retry specific email's failed job. How can I achieve this ?
I have a workaround approach, in which I grab all failed jobs through failed_jobs table, and check the payload one by one, determine if the MyEmail object has the id we want. but I don't think it's efficient.
I parse some HTML pages and API endpoints, for example, every 5 minutes to track changes. For this purpose, I have created ParseJob where I do parsing and save changes to a database. ParseJob implements interface ShouldQueue and I have changed queue driver to Redis. In order to run the ParseJob on a regular basis, I have created ParseCommand and added it to schedule:
class ParseCommand extends Command
{
protected $signature = 'application:my-parse-command';
public function handle()
{
$this->dispatch(new ParseJob());
}
}
class Kernel extends ConsoleKernel
{
protected $commands = [
Commands\ParseCommand::class
];
protected function schedule(Schedule $schedule)
{
$schedule->command('application:my-parse-command')
->everyFiveMinutes();
}
}
And the queue worker is started as a daemon to process the queue. So, every 5 minutes ParseJob is pushed to the queue and the queue worker is processing the job.
Sometimes queue worker process crashes, freezes or for other reasons is not working. But jobs every 5 minutes are pushed into the queue. After an hour of downtime, I have 12 jobs in the queue but they are for that time irrelevant because I do not need to parse 12 times at a certain time, I want just one parse job.
So I want to set TTL for a job that works like expire command in Redis. How to do that? Or maybe you have an alternative solution?
As far a i know it is not possible to set explicitly a Job expiration into Laravel queues. A solution could be setting an expires_at property within your ParseJob and check before executing:
class ParseCommand extends Command
{
protected $signature = 'application:my-parse-command';
public function handle()
{
$this->dispatch(new ParseJob(Carbon::now()->addMinutes(5)));
}
}
then in your Job class
class ParseJob {
protected $expires_at;
public function __construct(Carbon $expires_at) {
$this->expires_at = $expires_at;
}
public function handle()
{
if(!Carbon::now()->gt($this->expires_at)) {
// Parse data
}
}
}
At larvel 8/9 in job
MyJObClass
public function retryUntil(): Carbon
{
return now()->addMinutes(10);
}
Laravel :: Queues #time-based-attempts
I have hooked up Redis with Laravel for queuing emails and all is fine...
but in dev environment I use mailtrap.io (free version).
And the problem is that mailtrap allows to receive only 2 emails per second, so I never get all the emails that are queued, because redis sends emails like crazy... maybe 10 per/s
Is there a way somehow to slow down the queue so that it sends max 2 mails per second?
Yes It is a solution and they're called jobs :)
you can create a file to send emails exclusively in the Jobs Folder and a class like this
class SendPushNotification extends Job implements ShouldQueue
{
use InteractsWithQueue, SerializesModels;
protected $pushNotification;
/**
* Create a new job instance.
*
* #return void
*/
public function __construct(PushNotification $pushNotification)
{
$this->pushNotification = $pushNotification;
}
/**
* Execute the job.
*
* #return void
*/
public function handle()
{
$this->pushNotification->send();
}
}
And then call the class in the controller
$this->dispatch(new SendPushNotification($pushNotification))->delay(1);
Delay is for seconds you can create a constructor with an array to receive two emails o many you want maybe some var than can be changed by the .env to change the number of emails per second