Throttling Jobs in Laravel to Prevent API Flooding
Introduction
Laravel devs, here's a gem for you: π
When sending emails with AWS SES, or any external API, it's crucial to ensure you don't flood the service with too many requests. This can be done effectively using the Redis::throttle
method to manage rate limits. In this post, I'll show you how to throttle your jobs to keep your requests under control.
Why Throttle Your Jobs?
Throttling is essential when dealing with APIs that have rate limits to prevent being temporarily or permanently blocked. AWS SES, like many other APIs, has limits on the number of requests you can make in a given time frame. By throttling your jobs, you ensure that you stay within these limits and maintain a smooth operation.
Implementing Throttling with Redis
Let's implement a simple middleware that uses Redis::throttle
to manage the rate limits for sending emails through AWS SES.
Step 1: Create the Middleware
First, we'll create the middleware using the artisan
command:
php artisan make:middleware SesRateLimited
This will generate a new middleware class in app/Http/Middleware/SesRateLimited.php
. We'll modify it as follows:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Redis;
class SesRateLimited
{
/**
* Handle an incoming job.
*
* @param \Illuminate\Queue\InteractsWithQueue $job
* @param \Closure $next
* @return mixed
*/
public function handle($job, Closure $next)
{
Redis::throttle('ses-rate-limited')
->block(2)->allow(10)->every(2)
->then(function () use ($job, $next) {
$next($job);
}, function () use ($job) {
$job->release(30);
});
}
}
Explanation of Middleware:
Redis::throttle('ses-rate-limited')
: This sets up a throttle with the key'ses-rate-limited'
.->block(2)
: Specifies that the job should wait up to 2 seconds to obtain a lock.->allow(10)->every(2)
: Allows up to 10 operations every 2 seconds.->then(function () use ($job, $next) { $next($job); })
: If the lock is obtained, the job proceeds.->then(function () use ($job) { $job->release(30); })
: If the lock is not obtained within the blocking time, the job is released back onto the queue to be tried again after 30 seconds.
Step 2: Create the Notification
Next, we'll create a notification class that extends Notification
and implements ShouldQueue
.
Create the notification class using the artisan
command:
php artisan make:notification SendEmailNotification
Modify the SendEmailNotification
class as follows:
namespace App\Notifications;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
use App\Http\Middleware\SesRateLimited;
class SendEmailNotification extends Notification implements ShouldQueue
{
use Queueable;
protected $email;
/**
* Create a new notification instance.
*
* @return void
*/
public function __construct($email)
{
$this->email = $email;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
return (new MailMessage)
->subject($this->email['subject'])
->line($this->email['body']);
}
/**
* Get the middleware the notification should use.
*
* @return array
*/
public function middleware(): array
{
return [new SesRateLimited];
}
}
Step 3: Create the Controller
Finally, dispatch the notification from a controller:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
use App\Notifications\SendEmailNotification;
class EmailController extends Controller
{
public function sendEmail(Request $request)
{
// Example email data
$emailData = [
'subject' => 'Test Email',
'body' => 'This is a test email sent using AWS SES.'
];
// Get the user or notifiable instance
$user = User::find($request->user_id);
// Send the notification
$user->notify(new SendEmailNotification($emailData));
return response()->json(['message' => 'Email sent successfully.']);
}
}
Conclusion
By implementing the SesRateLimited
middleware and using Redis::throttle
, you can efficiently manage your rate limits when sending emails with AWS SES or any other external API. This approach ensures that you stay within the API's limits and avoid any potential service disruptions.
Found this helpful?
If this guide was helpful to you, subscribe to my daily newsletter and give me a follow on X/Twitter. It helps a lot!