SMSc - Application du Centre de SMS
Application du Centre de SMS construite avec Phoenix/Elixir, fournissant une file d'attente de messages centralisée et une API REST pour le routage et la livraison des SMS.
Documentation
Documentation de base
- Référence de configuration - Options de configuration complètes
- Référence API - Documentation de l'API REST
- Guide des opérations - Opérations quotidiennes et surveillance
- Guide de routage SMS - Gestion et configuration des routes
- Guide de traduction de numéros - Normalisation et réécriture des numéros
- Centre MMS (MMSc) - Gestion MMS MM1/MM4 et intégration de la file d'attente OmniMessage
- Optimisation des performances - Optimisation pour différentes charges de travail
- Guide des métriques - Métriques Prometheus et surveillance
- Guide de dépannage - Problèmes courants et solutions
- Schéma CDR - Schéma de base de données des enregistrements de détails d'appel et référence des champs
Documentation de conformité
- Conformité à l'interception ANSSI R226 - Spécifications techniques françaises pour l'interception légale
Performance
- Benchmarks - Tests de performance
Vue d'ensemble de l'architecture
Le cœur de SMS_C fournit une file d'attente de messages indépendante du protocole et une API REST. Les interfaces SMSC externes (SMPP, IMS, SS7/MAP) se connectent en tant que passerelles autonomes qui communiquent avec le cœur via l'API REST.
Flux de messages
Flux de messages sortants (MT - Mobile Terminated)
Flux de messages entrants (MO - Mobile Originated)
Caractéristiques clés
1. Conception indépendante du protocole
- Le cœur de SMS_C gère la persistance des messages, le routage et l'API
- Les interfaces externes (SMPP, IMS, SS7/MAP) gèrent la communication spécifique au protocole
- Toutes les interfaces communiquent via une API REST unifiée
- Ajouter de nouveaux protocoles sans changer le cœur
- Chacune peut être mise à l'échelle indépendamment
2. Routage des messages
- Moteur de routage dynamique avec configuration à l'exécution
- Routage basé sur les préfixes (numéros appelants/appelés)
- Filtrage par type de SMSC et source (IMS/Circuit Switched/SMPP)
- Routage basé sur la priorité avec équilibrage de charge basé sur le poids
- Routage de réponse automatique et capacités de suppression de messages
- Contrôle de facturation par route
- Interface Web pour la gestion des routes
- Mises à jour des routes en temps réel sans interruption de service
📖 Voir Guide de routage SMS pour une documentation complète
3. Logique de réessai avec backoff exponentiel
- Réessai automatique en cas d'échec de livraison
- Backoff exponentiel : 1min, 2min, 4min, 8min, etc.
- Tentatives de réessai maximales configurables
- Gestion de l'expiration des messages
- Suivi des réessais par message
Guide des opérations
Points d'accès :
- API REST :
https://localhost:8443(ouhttp://localhost:8080sans TLS) - Panneau de contrôle :
https://localhost:8086 - Documentation API (Swagger UI) :
https://localhost:8443/api/docs
Démarrer les interfaces externes : Chaque interface de protocole est une application autonome. Voir la documentation individuelle de l'interface pour les instructions de démarrage.
Configuration
Toute la configuration est gérée directement dans config/runtime.exs. Aucune variable d'environnement n'est utilisée.
Configuration de base
Aucune variable d'environnement de configuration de l'application de base n'est actuellement utilisée. Les ports du serveur et les noms d'hôte sont configurés dans config/runtime.exs :
- Serveur API : Port 8443 (HTTPS), écoutant sur 0.0.0.0
- Panneau de contrôle : Port 80 (HTTP), écoutant sur 0.0.0.0
Configuration de la base de données
Les paramètres de la base de données sont configurés dans config/runtime.exs :
- Nom d'utilisateur :
omnitouch - Mot de passe :
omnitouch2024 - Nom d'hôte :
localhost - Port :
3306 - Nom de la base de données :
smsc_new - Taille du pool :
1
Configuration du cluster
Les paramètres du cluster sont configurés dans config/runtime.exs :
- Nœuds du cluster :
""(vide - pas de clustering par défaut) - Requête DNS du cluster :
nil
Configuration de la file d'attente de messages
Les paramètres de la file d'attente de messages sont configurés dans config/runtime.exs :
- Temps de lettre morte :
1440minutes (24 heures avant que le message n'expire)
Intégration de la facturation
Les paramètres de facturation sont configurés dans config/runtime.exs :
- URL :
http://localhost:2080/jsonrpc - Locataire :
mnc057.mcc505.3gppnetwork.org - Destination :
55512341234 - Source :
00101900000257 - Sujet :
00101900000257 - Compte :
00101900000257
Configuration du routage SMS
Le système de routage SMS utilise des routes dynamiques, soutenues par une base de données, qui peuvent être gérées via l'interface Web ou un fichier de configuration. Les routes sont chargées depuis config/runtime.exs au premier démarrage.
Exemple de configuration :
config :sms_c, :sms_routes, [
%{
called_prefix: "+44",
dest_smsc: "InternationalGW",
weight: 100,
priority: 100,
description: "SMS internationaux UK",
enabled: true
},
%{
called_prefix: "1900",
dest_smsc: "PremiumGW",
charged: :yes,
priority: 50,
description: "Numéros premium US",
enabled: true
}
]
Fonctionnalités :
- Correspondance basée sur les préfixes (numéros appelants/appelés)
- Filtrage par SMSC source et type
- Routage basé sur la priorité et le poids
- Capacités de réponse automatique et de suppression
- Contrôle de facturation par route
- Gestion à l'exécution via l'interface Web à
/routing
📖 Voir Guide de routage SMS pour une documentation complète, des exemples et une référence API.
Points de terminaison de l'API REST
Opérations sur la file d'attente de messages
Soumettre un SMS (Créer un message)
POST /api/messages
Content-Type: application/json
{
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "Bonjour, le monde !",
"source_smsc": "web-app",
"dest_smsc": "smpp-provider", # Optionnel - le moteur de routage assigne si nul
"tp_dcs_character_set": "gsm7", # Optionnel : gsm7, 8bit, latin1, ucs2
"tp_dcs_coding_group": "general_data_coding",
"expires": "2025-10-17T10:30:00Z" # Optionnel - par défaut 24h à partir de maintenant
}
Champs requis :
destination_msisdn- Numéro de téléphone de destinationmessage_body- Contenu du texte du messagesource_msisdn- Numéro de téléphone sourcesource_smsc- Identifiant du système source
Champs optionnels :
dest_smsc- Passerelle de destination (le moteur de routage assigne si non fourni)source_imsi,dest_imsi- Identifiants IMSItp_dcs_character_set- Encodage des caractères (gsm7, 8bit, latin1, ucs2)tp_dcs_coding_group- Groupe de codage DCStp_dcs_compressed- Drapeau de compression (booléen)tp_dcs_has_message_class- Drapeau de classe de message (booléen)tp_dcs_message_class- Valeur de classe de messagetp_user_data_header- En-tête de données utilisateur (carte)message_part_number,message_parts- Champs de message multipartexpires- Horodatage d'expiration (par défaut 24 heures)deliver_after- Horodatage de livraison différéedeadletter,raw_data_flag,raw_sip_flag- Drapeaux booléens
Réponse :
{
"status": "success",
"data": {
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"dest_smsc": "smpp-provider",
"message_body": "Bonjour, le monde !",
"deliver_time": null,
"delivery_attempts": 0,
"expires": "2025-10-17T10:30:00Z",
"inserted_at": "2025-10-16T10:30:00Z"
}
}
Obtenir les messages pour SMSC
GET /api/messages/get_by_smsc?smsc=my-smsc-name
Retourne tous les messages non livrés où :
destination_smscest nul OU correspond au nom SMSC fourni- Le message n'est pas expiré
- Prêt à être envoyé (deliver_after est nul ou dans le passé)
Réponse :
{
"status": "success",
"data": [
{
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "Bonjour",
"destination_smsc": "my-smsc-name",
"delivery_attempts": 0
}
]
}
Lister les messages avec filtrage SMSC optionnel
# Lister tous les messages dans la file d'attente
GET /api/messages
# Lister les messages pour un SMSC spécifique (avec filtrage par en-tête)
GET /api/messages
SMSc: my-smsc-name
Sans en-tête SMSc : Retourne tous les messages dans la file d'attente, quel que soit l'état de livraison ou l'expiration.
Avec en-tête SMSc : Retourne les messages non livrés où :
dest_smsccorrespond à la valeur de l'en-tête OUdest_smscest nuldeliver_timeest nul (pas encore livré)deliver_afterest nul ou avant/égal à l'heure actuelle (prêt à livrer)expiresest après l'heure actuelle (non expiré)- Ordonné par heure d'insertion (le plus ancien en premier)
Remarque : L'approche de l'en-tête SMSc permet aux interfaces externes de vérifier leurs messages en utilisant le même modèle de point de terminaison, l'en-tête contrôlant le comportement de filtrage.
Réponse :
[
{
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "Bonjour, le monde !",
"dest_smsc": "my-smsc-name",
"deliver_time": null,
"delivery_attempts": 0,
"expires": "2025-10-17T10:30:00Z",
"inserted_at": "2025-10-16T10:30:00Z"
}
]
Obtenir un message unique
GET /api/messages/{id}
Mettre à jour un message
PATCH /api/messages/{id}
Content-Type: application/json
{
"status": "delivered",
"delivered_at": "2025-10-16T10:30:00Z"
}
Supprimer un SMS
DELETE /api/messages/{id}
Gérer l'échec de livraison (incrémenter le compteur de réessai)
Lorsqu'une livraison de message échoue temporairement, incrémentez le compteur de tentative de livraison et planifiez un réessai avec un backoff exponentiel.
Méthode 1 : Utiliser PUT (Recommandé)
# Simple et sémantique - PUT indique la mise à jour de l'état de livraison
PUT /api/messages/{id}
Méthode 2 : Utiliser un point de terminaison explicite
# Point de terminaison explicite alternatif
POST /api/messages/{id}/increment_delivery_attempt
Les deux méthodes incrémentent delivery_attempts et définissent le délai de backoff exponentiel via deliver_after :
| Tentative | Formule de backoff | Délai | Temps total |
|---|---|---|---|
| 1ère | 2^1 minutes | 2 min | 2 min |
| 2ème | 2^2 minutes | 4 min | 6 min |
| 3ème | 2^3 minutes | 8 min | 14 min |
| 4ème | 2^4 minutes | 16 min | 30 min |
| 5ème | 2^5 minutes | 32 min | 1h 2min |
| 6ème | 2^6 minutes | 64 min | 2h 6min |
Réponse :
{
"id": 123,
"delivery_attempts": 1,
"deliver_after": "2025-10-20T19:05:00Z",
"deliver_time": null,
"expires": "2025-10-21T19:03:00Z",
...
}
Remarque : Les messages avec un deliver_after futur sont automatiquement filtrés des requêtes GET jusqu'à ce que la période de backoff expire.
Mettre à jour un message (Mise à jour partielle)
Pour mettre à jour des champs spécifiques d'un message (comportement inchangé) :
PATCH /api/messages/{id}
Content-Type: application/json
{
"dest_smsc": "updated-gateway",
"status": "delivered"
}
Important : PUT et PATCH se comportent différemment :
- PUT → Incrémente les tentatives de livraison avec backoff (aucun corps requis)
- PATCH → Effectue des mises à jour de champs partiels (corps requis)
Suivi de la santé des interfaces
Le cœur de SMS_C suit la santé et la disponibilité des interfaces externes grâce à un système d'enregistrement. Cela permet de surveiller le temps de disponibilité des interfaces, de détecter les pannes et de maintenir des données historiques de disponibilité.
Remarque : L'enregistrement des interfaces n'est PAS utilisé pour la livraison ou le routage des messages. Les messages sont routés en fonction du champ dest_smsc. Le système d'enregistrement existe uniquement pour la surveillance de la santé et la visibilité opérationnelle.
Comment fonctionne l'enregistrement des interfaces
Chaque interface externe (passerelle SMPP, IMS, SS7/MAP) envoie périodiquement un enregistrement de cœur de battement au cœur de SMS_C :
- Intervalle de battement : Les interfaces doivent s'enregistrer toutes les 30 à 60 secondes
- Temps d'expiration : Les enregistrements expirent après 90 secondes sans mise à jour
- Gestion automatique des états :
- Les nouvelles interfaces créent un nouvel enregistrement d'enregistrement
- Les interfaces actives existantes mettent à jour leur enregistrement (prolonge l'expiration)
- Les interfaces expirées qui reviennent en ligne créent une nouvelle période d'enregistrement
Points de terminaison d'enregistrement des interfaces
Enregistrer/Mettre à jour l'interface (Cœur de battement)
POST /api/frontends
Content-Type: application/json
{
"frontend_name": "smpp-gateway-1",
"frontend_type": "SMPP",
"ip_address": "10.0.1.5",
"hostname": "smpp-gw-01",
"uptime_seconds": 3600,
"configuration": "{\"port\": 2775, \"system_id\": \"smpp_user\"}"
}
Champs requis :
frontend_name- Identifiant unique pour l'instance de l'interfacefrontend_type- Type d'interface (SMPP, IMS, MAP, etc.)
Champs optionnels :
ip_address- Adresse IP de l'interface (détectée automatiquement à partir de la source de la requête si non fournie)hostname- Nom d'hôte du serveur de l'interfaceuptime_seconds- Secondes depuis le démarrage de l'interfaceconfiguration- Chaîne JSON avec la configuration spécifique à l'interface
Remarque : Si ip_address n'est pas fourni, le cœur de SMS_C utilisera automatiquement l'IP source de la requête HTTP. Cela fonctionne avec les connexions directes et les requêtes proxy (via l'en-tête X-Forwarded-For).
Réponse :
{
"id": 42,
"frontend_name": "smpp-gateway-1",
"frontend_type": "SMPP",
"ip_address": "10.0.1.5",
"hostname": "smpp-gw-01",
"uptime_seconds": 3600,
"status": "active",
"last_seen_at": "2025-10-20T10:30:00Z",
"expires_at": "2025-10-20T10:31:30Z",
"inserted_at": "2025-10-20T10:00:00Z"
}
Lister tous les enregistrements d'interface
GET /api/frontends
Retourne tous les enregistrements d'interface (actifs et expirés), ordonnés par activité la plus récente.
Lister uniquement les interfaces actives
GET /api/frontends/active
Retourne uniquement les interfaces actuellement actives (non expirées).
Obtenir des statistiques sur les interfaces
GET /api/frontends/stats
Retourne des statistiques récapitulatives :
{
"active": 5,
"expired": 12,
"unique_frontends": 8
}
Obtenir l'historique des interfaces
GET /api/frontends/history/{frontend_name}
Retourne tous les enregistrements historiques pour une interface spécifique, utile pour analyser les modèles de disponibilité/indisponibilité.
Exemple :
GET /api/frontends/history/smpp-gateway-1
Obtenir un enregistrement spécifique
GET /api/frontends/{id}
Mise en œuvre dans les interfaces externes
Les interfaces externes doivent mettre en œuvre une tâche d'arrière-plan qui envoie des cœurs de battement :
Exemple (pseudocode) :
import time
import requests
def send_heartbeat():
"""Envoyer un cœur de battement toutes les 30 secondes"""
while True:
try:
data = {
"frontend_name": "my-smpp-gateway",
"frontend_type": "SMPP",
"ip_address": get_local_ip(),
"hostname": get_hostname(),
"uptime_seconds": get_uptime()
}
response = requests.post(
"https://smsc-core:8443/api/frontends",
json=data,
timeout=5
)
if response.status_code in [200, 201]:
logger.debug("Cœur de battement envoyé avec succès")
else:
logger.error(f"Échec du cœur de battement : {response.status_code}")
except Exception as e:
logger.error(f"Erreur de cœur de battement : {e}")
time.sleep(30) # Envoyer toutes les 30 secondes
# Démarrer le cœur de battement dans un thread d'arrière-plan
threading.Thread(target=send_heartbeat, daemon=True).start()
Surveillance de la santé des interfaces
Panneau de contrôle - L'interface Web à https://localhost:8086 montre :
- Interfaces actuellement actives
- Horodatage de la dernière vue pour chaque interface
- Suivi du temps de disponibilité
- Disponibilité historique
Requêtes API :
# Obtenir toutes les interfaces actives
curl https://localhost:8443/api/frontends/active
# Vérifier si une interface spécifique est en ligne
curl https://localhost:8443/api/frontends/history/smpp-gateway-1 | jq '.[0].status'
# Obtenir des statistiques de santé
curl https://localhost:8443/api/frontends/stats
Autres points de terminaison
Statut
GET /api/status
Localisations
GET /api/locations
POST /api/locations
GET /api/locations/{id}
PATCH /api/locations/{id}
DELETE /api/locations/{id}
Événements SS7
GET /api/ss7_events
POST /api/ss7_events
GET /api/ss7_events/{id}
PATCH /api/ss7_events/{id}
DELETE /api/ss7_events/{id}
File d'attente de messages MMS
GET /api/mms_message_queues
POST /api/mms_message_queues
GET /api/mms_message_queues/{id}
PATCH /api/mms_message_queues/{id}
DELETE /api/mms_message_queues/{id}
Performance
Le cœur de SMS_C offre un débit exceptionnel en utilisant Mnesia pour le stockage de messages en mémoire avec archivage automatique vers SQL pour la rétention à long terme des CDR.
Résultats des benchmarks
Mesuré sur Intel i7-8650U @ 1.90GHz (8 cœurs) :
Performance d'insertion de messages :
insert_message(avec routage) : 1 750 msg/sec (0,58 ms de latence moyenne)insert_message(simple) : 1 750 msg/sec (0,57 ms de latence moyenne)- Capacité d'environ 150 millions de messages par jour
Performance de requête :
get_messages_for_smsc: 800 msg/sec (1,25 ms en moyenne)list_message_queues: Accès rapide en mémoire- Utilisation de la mémoire : 62 Ko par opération d'insertion
Architecture
Stratégie de stockage :
- Messages actifs : Stockés dans Mnesia (en mémoire + disque) pour un accès ultra-rapide
- Archive de messages : Archivés automatiquement vers SQL pour le stockage à long terme des CDR
- Rétention : Période de rétention configurable (par défaut : 24 heures)
- Pas de goulet d'étranglement SQL : Toutes les opérations de messages actifs contournent SQL
Configuration
Le stockage et la rétention des messages sont configurés dans config/runtime.exs :
config :sms_c,
message_retention_hours: 24, # Archiver les messages de plus de 24 heures
batch_insert_batch_size: 100, # Taille de lot CDR pour l'archivage SQL
batch_insert_flush_interval_ms: 100 # Intervalle de vidage CDR
Pour des conseils détaillés sur le réglage, voir : docs/PERFORMANCE_TUNING.md
Surveillance
Panneau de contrôle - Interface Web à https://localhost:8086
- Voir la file d'attente de messages
- Soumettre des messages de test
- Gérer le routage SMS (voir Guide de routage)
- Simuler des décisions de routage
- Voir les ressources système
- Suivre les statistiques des travailleurs de lot
Statistiques des travailleurs de lot :
# Obtenir les statistiques actuelles des travailleurs de lot
SmsC.Messaging.BatchInsertWorker.stats()
Retourne :
%{
total_enqueued: 10000,
total_flushed: 9900,
current_queue_size: 100,
last_flush_duration_ms: 45
}
Logs - Les logs de l'application sont écrits sur stdout
# Voir les logs en temps réel
tail -f log/dev.log
Dépannage
Port déjà utilisé
# Trouver le processus utilisant le port
lsof -i :4000
# Tuer le processus
kill -9 <PID>
Interface externe ne se connectant pas
Symptômes : Messages bloqués dans la file d'attente, les logs de l'interface montrent des erreurs de connexion
Vérifiez :
- Vérifiez que
API_BASE_URLest correctement défini dans l'interface - Vérifiez que le cœur SMS_C fonctionne et est accessible
- Vérifiez les règles réseau/pare-feu
- Vérifiez la configuration de l'interface
Solution :
# Tester la connectivité API depuis l'interface
curl http://localhost:4000/api/status
# Redémarrer l'interface
export API_BASE_URL="http://localhost:4000"
# Démarrer l'application de l'interface
Messages non livrés
Symptômes : Les messages restent non livrés, les tentatives de réessai s'incrémentent
Vérifiez :
- Logs de l'interface pour les erreurs d'envoi
- Connectivité réseau externe
- Configuration de l'interface (identifiants, adresses)
- Compatibilité du format de message
Voir les messages échoués :
# Obtenir les messages avec tentatives de réessai
curl https://localhost:8443/api/messages | jq '.data[] | select(.delivery_attempts > 0)'
Latence élevée des messages
Symptômes : Messages prenant plus de temps que prévu, arrière-plan de file d'attente
Vérifiez :
- Intervalle de sondage de l'interface (peut nécessiter une diminution pour un sondage plus fréquent)
- Performance de la base de données
- Latence réseau vers les systèmes externes
Surveiller la profondeur de la file d'attente :
watch -n 5 'curl -s https://localhost:8443/api/messages | jq ".data | length"'