Aller au contenu principal

SMSc - Application du Centre de Service SMS

Application du Centre SMS construite avec Phoenix/Elixir, fournissant une file de messages centralisée et une API REST pour le routage et la livraison des SMS.

Documentation

Documentation Principale

Documentation de Conformité

Performance

Vue d'Ensemble de l'Architecture

Le cœur de SMS_C fournit une file de messages indépendante du protocole et une API REST. Les frontaux 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)

Fonctionnalités 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 frontaux externes (SMPP, IMS, SS7/MAP) gèrent la communication spécifique au protocole
  • Tous les frontaux communiquent via une API REST unifiée
  • Ajoutez de nouveaux protocoles sans changer le cœur
  • Chacun peut être mis à 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 Commuté/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 message
  • Contrôle de facturation par route
  • Interface Web pour la gestion des routes
  • Mises à jour de route en temps réel sans interruption de service

📖 Voir Guide de Routage SMS pour une documentation complète

3. Logique de Réessai avec Retour Exponentiel

  • Réessai automatique en cas d'échec de livraison
  • Retour exponentiel : 1min, 2min, 4min, 8min, etc.
  • Tentatives de réessai maximum 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 (ou http://localhost:8080 sans TLS)
  • Panneau de Contrôle : https://localhost:8086
  • Documentation API (Swagger UI) : https://localhost:8443/api/docs

Démarrer les Frontaux Externes : Chaque frontal de protocole est une application autonome. Consultez la documentation individuelle des frontaux 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 Principale

Aucune variable d'environnement de configuration de l'application principale 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 de Cluster

Les paramètres de cluster sont configurés dans config/runtime.exs :

  • Nœuds de cluster : "" (vide - pas de clustering par défaut)
  • Requête DNS de cluster : nil

Configuration de la File de Messages

Les paramètres de la file de messages sont configurés dans config/runtime.exs :

  • Temps de lettre morte : 1440 minutes (24 heures avant l'expiration du message)

Intégration de 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 de 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 le 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 International 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 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 attribue 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 destination
  • message_body - Contenu du texte du message
  • source_msisdn - Numéro de téléphone source
  • source_smsc - Identifiant du système source

Champs Optionnels :

  • dest_smsc - Passerelle de destination (le moteur de routage attribue si non fourni)
  • source_imsi, dest_imsi - Identifiants IMSI
  • tp_dcs_character_set - Encodage des caractères (gsm7, 8bit, latin1, ucs2)
  • tp_dcs_coding_group - Groupe de codage DCS
  • tp_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 message
  • tp_user_data_header - En-tête de données utilisateur (carte)
  • message_part_number, message_parts - Champs de message multipart
  • expires - Horodatage d'expiration (par défaut 24 heures)
  • deliver_after - Horodatage de livraison différée
  • deadletter, 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 des Messages pour SMSC

GET /api/messages/get_by_smsc?smsc=nom-de-mon-smsc

Retourne tous les messages non livrés où :

  • destination_smsc est 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": "nom-de-mon-smsc",
"delivery_attempts": 0
}
]
}

Lister les Messages avec Filtrage SMSC Optionnel

# Lister tous les messages dans la file
GET /api/messages

# Lister les messages pour un SMSC spécifique (avec filtrage par en-tête)
GET /api/messages
SMSc: nom-de-mon-smsc

Sans En-tête SMSc : Retourne tous les messages dans la file, quel que soit l'état de livraison ou l'expiration.

Avec En-tête SMSc : Retourne les messages non livrés où :

  • dest_smsc correspond à la valeur de l'en-tête OU dest_smsc est nul
  • deliver_time est nul (pas encore livré)
  • deliver_after est nul ou avant/égal à l'heure actuelle (prêt à livrer)
  • expires est après l'heure actuelle (non expiré)
  • Ordonné par heure d'insertion (le plus ancien en premier)

Remarque : L'approche par en-tête SMSc permet aux frontaux externes de sonder leurs messages en utilisant le même modèle de point de terminaison, avec 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": "nom-de-mon-smsc",
"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 tentatives de livraison et planifiez un réessai avec retour exponentiel.

Méthode 1 : Utilisation de PUT (Recommandé)

# Simple et sémantique - PUT indique la mise à jour de l'état de livraison
PUT /api/messages/{id}

Méthode 2 : Utilisation d'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 retour exponentiel via deliver_after :

TentativeFormule de RetourDélaiTemps Total
1��re2^1 minutes2 min2 min
2ème2^2 minutes4 min6 min
3ème2^3 minutes8 min14 min
4ème2^4 minutes16 min30 min
5ème2^5 minutes32 min1h 2min
6ème2^6 minutes64 min2h 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 retour 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": "passerelle-mise-à-jour",
"status": "delivered"
}

Important : PUT et PATCH se comportent différemment :

  • PUT → Incrémente les tentatives de livraison avec retour (aucun corps requis)
  • PATCH → Effectue des mises à jour de champs partiels (corps requis)

Suivi de la Santé des Frontaux

Le cœur de SMS_C suit la santé et la disponibilité des frontaux externes via un système d'enregistrement. Cela permet de surveiller le temps de disponibilité des frontaux, de détecter les pannes et de maintenir des données historiques de disponibilité.

Remarque : L'enregistrement des frontaux 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 Frontaux

Chaque frontal externe (passerelle SMPP, IMS, SS7/MAP) envoie périodiquement un enregistrement de cœur battant au cœur de SMS_C :

  1. Intervalle de Battement : Les frontaux doivent s'enregistrer toutes les 30-60 secondes
  2. Temps d'Expiration : Les enregistrements expirent après 90 secondes sans mise à jour
  3. Gestion Automatique des États :
    • Les nouveaux frontaux créent un nouvel enregistrement
    • Les frontaux actifs existants mettent à jour leur enregistrement (prolonge l'expiration)
    • Les frontaux expirés qui reviennent en ligne créent une nouvelle période d'enregistrement

Points de Terminaison d'Enregistrement des Frontaux

Enregistrer/Mise à Jour un Frontal (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 du frontal
  • frontend_type - Type de frontal (SMPP, IMS, MAP, etc.)

Champs Optionnels :

  • ip_address - Adresse IP du frontal (détectée automatiquement à partir de la source de la requête si non fournie)
  • hostname - Nom d'hôte du serveur frontal
  • uptime_seconds - Secondes depuis le démarrage du frontal
  • configuration - Chaîne JSON avec la configuration spécifique au frontal

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 de Frontaux
GET /api/frontends

Retourne tous les enregistrements de frontaux (actifs et expirés), ordonnés par activité la plus récente.

Lister Uniquement les Frontaux Actifs
GET /api/frontends/active

Retourne uniquement les frontaux actuellement actifs (non expirés).

Obtenir des Statistiques sur les Frontaux
GET /api/frontends/stats

Retourne des statistiques récapitulatives :

{
"active": 5,
"expired": 12,
"unique_frontends": 8
}
Obtenir l'Historique des Frontaux
GET /api/frontends/history/{frontend_name}

Retourne tous les enregistrements historiques pour un frontal 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 Frontaux Externes

Les frontaux externes doivent mettre en œuvre une tâche en arrière-plan qui envoie des battements :

Exemple (pseudocode) :

import time
import requests

def send_heartbeat():
"""Envoyer un battement toutes les 30 secondes"""
while True:
try:
data = {
"frontend_name": "mon-gateway-smpp",
"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("Battement envoyé avec succès")
else:
logger.error(f"Échec du battement : {response.status_code}")

except Exception as e:
logger.error(f"Erreur de battement : {e}")

time.sleep(30) # Envoyer toutes les 30 secondes

# Démarrer le battement dans un thread en arrière-plan
threading.Thread(target=send_heartbeat, daemon=True).start()

Surveillance de la Santé des Frontaux

Panneau de Contrôle - L'interface Web à https://localhost:8086 affiche :

  • Les frontaux actuellement actifs
  • Horodatage de la dernière vue pour chaque frontal
  • Suivi du temps de disponibilité
  • Disponibilité historique

Requêtes API :

# Obtenir tous les frontaux actifs
curl https://localhost:8443/api/frontends/active

# Vérifier si un frontal 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

Lieux

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 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 (latence moyenne de 0,58 ms)
  • insert_message (simple) : 1 750 msg/sec (latence moyenne de 0,57 ms)
  • 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 (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 du lot CDR pour l'archivage SQL
batch_insert_flush_interval_ms: 100 # Intervalle de vidage CDR

Pour des conseils détaillés sur l'optimisation, voir : docs/PERFORMANCE_TUNING.md

Surveillance

Panneau de Contrôle - Interface Web à https://localhost:8086

  • Voir la file 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 dans 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>

Frontend Externe Ne Se Connecte Pas

Symptômes : Messages bloqués dans la file, les logs du frontal montrent des erreurs de connexion

Vérifiez :

  • Vérifiez que API_BASE_URL est correctement défini dans le frontal
  • Vérifiez que le cœur SMS_C fonctionne et est accessible
  • Examinez les règles réseau/pare-feu
  • Vérifiez la configuration du frontal

Solution :

# Tester la connectivité API depuis le frontal
curl http://localhost:4000/api/status

# Redémarrer le frontal
export API_BASE_URL="http://localhost:4000"
# Démarrer l'application frontal

Messages Non Livrés

Symptômes : Les messages restent non livrés, les tentatives de réessai s'incrémentent

Vérifiez :

  1. Logs du frontal pour les erreurs d'envoi
  2. Connectivité réseau externe
  3. Configuration du frontal (identifiants, adresses)
  4. 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éré dans la file

Vérifiez :

  1. Intervalle de sondage du frontal (peut n��cessiter une réduction pour un sondage plus fréquent)
  2. Performance de la base de données
  3. Latence réseau vers les systèmes externes

Surveiller la profondeur de la file :

watch -n 5 'curl -s https://localhost:8443/api/messages | jq ".data | length"'