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é

Performances

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 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
  • Ajoutez de nouveaux protocoles sans modifier 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 message
  • 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 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 Interfaces Externes : Chaque interface de protocole est une application autonome. Voir la documentation de chaque 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 de 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 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 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": "Hello, World!",
"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": "Hello, World!",
"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=my-smsc-name

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": "Hello",
"destination_smsc": "my-smsc-name",
"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 d'en-tête)
GET /api/messages
SMSc: my-smsc-name

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 d'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 d'en-tête SMSc permet aux interfaces externes de sonder 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": "Hello, World!",
"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 tentatives de livraison et planifiez un réessai avec un backoff 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 backoff exponentiel via deliver_after :

TentativeFormule de BackoffDé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 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 partielles des champs (corps requis)

Suivi de la Santé des Interfaces

Le cœur de SMS_C suit la santé et la disponibilité des interfaces externes via 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 heartbeat au cœur de SMS_C :

  1. Intervalle de Heartbeat : Les interfaces 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 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/Mise à Jour l'Interface (Heartbeat)
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'interface
  • frontend_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'interface
  • uptime_seconds - Secondes depuis le démarrage de l'interface
  • configuration - 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'adresse 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'Interfaces
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 les Statistiques des 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 schémas 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 en arrière-plan qui envoie des heartbeats :

Exemple (pseudo-code) :

import time
import requests

def send_heartbeat():
"""Envoyer un heartbeat 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("Heartbeat envoyé avec succès")
else:
logger.error(f"Échec du heartbeat : {response.status_code}")

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

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

# Démarrer le heartbeat dans un thread en 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 affiche :

  • 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

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}

Performances

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 conservation à 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.58ms)
  • insert_message (simple) : 1,750 msg/sec (latence moyenne de 0.57ms)
  • ~150 millions de messages par jour de capacité

Performance de Requête :

  • get_messages_for_smsc : 800 msg/sec (1.25ms 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
  • Conservation : Période de conservation 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 conservation 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 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 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 Connecte Pas

Symptômes : Messages bloqués dans la file, les logs de l'interface montrent des erreurs de connexion

Vérifiez :

  • Vérifiez que API_BASE_URL est correctement défini dans l'interface
  • Vérifiez que le cœur de SMS_C fonctionne et est accessible
  • Examinez 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 :

  1. Logs de l'interface pour les erreurs d'envoi
  2. Connectivité réseau externe
  3. Configuration de l'interface (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, backlog dans la file

Vérifiez :

  1. Intervalle de sondage de l'interface (peut nécessiter une diminution 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"'