Si vous envoyez vos SMS marketing ou transactionnels directement dans le même thread que la réponse HTTP au client (ex: l'utilisateur clique sur "S'inscrire", le serveur fait un appel [API SMS](/fr/api/) synchrone, puis renvoie la page Web), votre architecture est vulnérable.
Si la [passerelle SMS marocaine (IAM, Orange, inwi) met 3 secondes à répondre](/fr/blog/votre-taux-de-delivrance-sms-baisse-soudainement-au-maroc-voici/) à cause d'une surcharge réseau, l'utilisateur attendra 3 secondes face à un écran blanc. Pire, si l'API est temporairement hors ligne, votre script plantera, l'utilisateur verra une page d'erreur 500, et le SMS sera définitivement perdu.
Pour une production robuste, l'envoi de SMS doit **toujours** être asynchrone via une File d'Attente (Queue) gérée par Redis. Voici comment implémenter cette résilience dans l'écosystème PHP et Node.js.
Pourquoi une simple boucle "for" ne suffit pas en production
Imaginons que vous devez router une campagne vers 50 000 clients marocains. Une simple boucle `foreach` ou `for` va saturer la mémoire de votre serveur (Memory Exhaustion) et, surtout, va déclencher le mécanisme de [Rate Limiting (Throttling)](/fr/blog/throttling-sms-au-maroc-a-quelle-vitesse-pouvez-vous-vraiment/) de l'API. Vous obtiendrez des dizaines de milliers d'erreurs `429 Too Many Requests`.
L'architecture asynchrone règle ce problème :
1. **Délégation (Le Producer) :** Votre application Web place instantanément 50 000 tâches (Jobs) contenant le numéro et le message dans une base Redis (en mémoire, ultra-rapide). La page Web répond au client en moins de 50ms.
2. **Exécution (Le Consumer / Worker) :** Un processus en arrière-plan "dépile" les requêtes Redis à son propre rythme (ex: 30 SMS par seconde) pour respecter les limites de l'API SMS.
Exemple Laravel Horizon pour un envoi de masse (PHP)
Dans l'écosystème PHP/Laravel, **Horizon** (associé à Redis) est l'outil parfait. Il permet de configurer facilement des files d'attente spécifiques, du throttling et d'avoir un dashboard visuel.
**1. Configuration du Throttling (App/Jobs/SendSmsJob.php) :**
Plutôt que d'inonder l'API, nous allons forcer le worker Laravel à ne pas dépasser 50 requêtes par seconde, avec une [gestion automatique des retries](/fr/blog/gerer-les-retries-et-la-file-d-attente-sms-en-cas-d-echec-d/).
```php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\Middleware\RateLimited;
class SendSmsJob implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public $contact;
public $message;
// Définir la limite de débit : "sms-api" (défini dans AppServiceProvider)
public function middleware()
{
return [new RateLimited('sms-api')];
}
public function handle()
{
// Appel API réel vers EnvoiSMS.ma
$response = EnvoiSms::send($this->contact->phone, $this->message);
if ($response->failed()) {
throw new \Exception("Erreur API, Horizon relancera le job selon l'exponential backoff.");
}
}
}
```
**2. Isoler les flux critiques :**
Dans `config/horizon.php`, configurez deux processus de "workers" distincts. Un worker dédié à la file `otp_critical` (qui ne doit jamais être ralenti) et un autre dédié à la file `marketing_bulk` (qui peut tourner tranquillement en tâche de fond).
Exemple Node.js avec BullMQ
Si votre backend est en Express ou NestJS, **BullMQ** (le successeur de Bull) est le standard industriel adossé à Redis pour gérer des millions de jobs.
**Configuration du Rate Limiting natif dans BullMQ :**
```javascript
import { Queue, Worker } from 'bullmq';
// Création de la file d'attente liée à Redis
const smsQueue = new Queue('sms_dispatch', {
connection: { host: 'localhost', port: 6379 }
});
// Ajout d'un SMS dans la file (Hyper rapide, le client n'attend pas)
await smsQueue.add('send_otp', { phone: '+212600000000', message: 'Code: 1234' });
// Le Worker qui traite l'envoi en respectant les limites de l'opérateur
const smsWorker = new Worker('sms_dispatch', async job => {
const { phone, message } = job.data;
try {
await sendSmsViaApi(phone, message); // Votre appel HTTP axios ou fetch
} catch (error) {
// Si l'erreur est temporaire (ex: 503), BullMQ relancera le job
throw new Error('Erreur API, réessayer plus tard.');
}
}, {
connection: { host: 'localhost', port: 6379 },
limiter: {
max: 20, // Maximum 20 requêtes...
duration: 1000 // ...par seconde. Respect parfait du Rate Limiting !
}
});
```
En utilisant ces outils, non seulement vous garantissez que la charge de l'API est parfaitement lissée, mais surtout, si votre serveur crashe au milieu de l'envoi de la campagne, les SMS non envoyés restent stockés dans Redis. Au redémarrage du serveur, les workers reprendront exactement là où ils s'étaient arrêtés. Zéro doublon, zéro perte.
💡 Pourquoi choisir EnvoiSMS pour votre entreprise ?
⚡Délivrabilité Critique
Moins de 4 secondes pour vos OTP via des canaux directs opérateurs IAM, Inwi et Orange Maroc.
💰Optimisation du Budget
WhatsApp Business API à 0,13 MAD seulement par session. Le meilleur ROI conversationnel.
🛡️Données Souveraines (CNDP)
Hébergement conforme aux réglementations de protection des données personnelles locales.