SMSc - Aplicación del Centro de Servicios SMS
Aplicación del Centro SMS construida con Phoenix/Elixir, que proporciona una cola de mensajes centralizada y una API REST para el enrutamiento y la entrega de SMS.
Documentación
Documentación Principal
- Índice Completo de Documentación - Comienza aquí para toda la documentación
- Referencia de Configuración - Opciones de configuración completas
- Referencia de API - Documentación de la API REST
- Guía de Operaciones - Operaciones diarias y monitoreo
- Guía de Enrutamiento de SMS - Gestión y configuración de rutas
- Guía de Traducción de Números - Normalización y reescritura de números
- Ajuste de Rendimiento - Optimización para diferentes cargas de trabajo
- Guía de Métricas - Métricas de Prometheus y monitoreo
- Guía de Solución de Problemas - Problemas comunes y soluciones
- Esquema CDR - Formato de registro de detalles de llamadas
Documentación de Cumplimiento
- Cumplimiento de Intercepción ANSSI R226 - Especificaciones técnicas de intercepción legal en Francia
Rendimiento
- Referencias de Rendimiento - Pruebas de rendimiento
Descripción General de la Arquitectura
El núcleo de SMS_C proporciona una cola de mensajes independiente del protocolo y una API REST. Los frontends SMSC externos (SMPP, IMS, SS7/MAP) se conectan como puertas de enlace independientes que se comunican con el núcleo a través de la API REST.
Flujo de Mensajes
Flujo de Mensajes Salientes (MT - Móvil Terminado)
Flujo de Mensajes Entrantes (MO - Móvil Originado)
Características Clave
1. Diseño Independiente del Protocolo
- El núcleo SMS_C maneja la persistencia de mensajes, el enrutamiento y la API
- Los frontends externos (SMPP, IMS, SS7/MAP) manejan la comunicación específica del protocolo
- Todos los frontends se comunican a través de una API REST unificada
- Agregar nuevos protocolos sin cambiar el núcleo
- Cada uno puede escalarse de forma independiente
2. Enrutamiento de Mensajes
- Motor de enrutamiento dinámico con configuración en tiempo de ejecución
- Enrutamiento basado en prefijos (números llamados/llamantes)
- Filtrado de SMSC y tipo de fuente (IMS/Circuito Conmutado/SMPP)
- Enrutamiento basado en prioridad con balanceo de carga basado en peso
- Capacidades de enrutamiento de respuesta automática y eliminación de mensajes
- Control de cargos por ruta
- Interfaz web para gestión de rutas
- Actualizaciones de ruta en tiempo real sin interrupción del servicio
📖 Consulta la Guía de Enrutamiento de SMS para documentación completa
3. Lógica de Reintento con Retroceso Exponencial
- Reintento automático en caso de fallo de entrega
- Retroceso exponencial: 1min, 2min, 4min, 8min, etc.
- Intentos de reintento máximos configurables
- Manejo de expiración de mensajes
- Seguimiento de reintentos por mensaje
Guía de Operaciones
Puntos de Acceso:
- API REST:
https://localhost:8443(ohttp://localhost:8080sin TLS) - Panel de Control:
https://localhost:8086 - Documentación de API (Swagger UI):
https://localhost:8443/api/docs
Iniciar Frontends Externos: Cada frontend de protocolo es una aplicación independiente. Consulta la documentación de cada frontend para instrucciones de inicio.
Configuración
Toda la configuración se gestiona directamente en config/runtime.exs. No se utilizan variables de entorno.
Configuración del Núcleo
Actualmente no se utilizan variables de entorno para la configuración de la aplicación núcleo. Los puertos del servidor y los nombres de host se configuran en config/runtime.exs:
- Servidor API: Puerto 8443 (HTTPS), escuchando en 0.0.0.0
- Panel de Control: Puerto 80 (HTTP), escuchando en 0.0.0.0
Configuración de la Base de Datos
Los ajustes de la base de datos se configuran en config/runtime.exs:
- Nombre de usuario:
omnitouch - Contraseña:
omnitouch2024 - Nombre del host:
localhost - Puerto:
3306 - Nombre de la base de datos:
smsc_new - Tamaño del grupo:
1
Configuración del Clúster
Los ajustes del clúster se configuran en config/runtime.exs:
- Nodos del clúster:
""(vacío - sin agrupamiento por defecto) - Consulta de clúster DNS:
nil
Configuración de la Cola de Mensajes
Los ajustes de la cola de mensajes se configuran en config/runtime.exs:
- Tiempo de carta muerta:
1440minutos (24 horas antes de que el mensaje expire)
Integración de Cargos
Los ajustes de cargos se configuran en config/runtime.exs:
- URL:
http://localhost:2080/jsonrpc - Inquilino:
mnc057.mcc505.3gppnetwork.org - Destino:
55512341234 - Fuente:
00101900000257 - Asunto:
00101900000257 - Cuenta:
00101900000257
Configuración de Enrutamiento de SMS
El sistema de enrutamiento de SMS utiliza rutas dinámicas respaldadas por bases de datos que pueden ser gestionadas a través de la interfaz web o el archivo de configuración. Las rutas se cargan desde config/runtime.exs en el primer inicio.
Ejemplo de Configuración:
config :sms_c, :sms_routes, [
%{
called_prefix: "+44",
dest_smsc: "InternationalGW",
weight: 100,
priority: 100,
description: "SMS Internacional del Reino Unido",
enabled: true
},
%{
called_prefix: "1900",
dest_smsc: "PremiumGW",
charged: :yes,
priority: 50,
description: "Números Premium de EE. UU.",
enabled: true
}
]
Características:
- Coincidencia basada en prefijos (números llamados/llamantes)
- Filtrado de SMSC de origen y tipo
- Enrutamiento basado en prioridad y peso
- Capacidades de respuesta automática y eliminación
- Control de cargos por ruta
- Gestión en tiempo de ejecución a través de la interfaz web en
/routing
📖 Consulta la Guía de Enrutamiento de SMS para documentación completa, ejemplos y referencia de API.
Puntos Finales de la API REST
Operaciones de Cola de Mensajes
Enviar SMS (Crear Mensaje)
POST /api/messages
Content-Type: application/json
{
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "¡Hola, Mundo!",
"source_smsc": "web-app",
"dest_smsc": "smpp-provider", # Opcional - el motor de enrutamiento asigna si es nulo
"tp_dcs_character_set": "gsm7", # Opcional: gsm7, 8bit, latin1, ucs2
"tp_dcs_coding_group": "general_data_coding",
"expires": "2025-10-17T10:30:00Z" # Opcional - por defecto 24h desde ahora
}
Campos Requeridos:
destination_msisdn- Número de teléfono de destinomessage_body- Contenido del texto del mensajesource_msisdn- Número de teléfono de origensource_smsc- Identificador del sistema de origen
Campos Opcionales:
dest_smsc- Puerta de enlace de destino (el motor de enrutamiento asigna si no se proporciona)source_imsi,dest_imsi- Identificadores IMSItp_dcs_character_set- Codificación de caracteres (gsm7, 8bit, latin1, ucs2)tp_dcs_coding_group- Grupo de codificación DCStp_dcs_compressed- Indicador de compresión (booleano)tp_dcs_has_message_class- Indicador de clase de mensaje (booleano)tp_dcs_message_class- Valor de clase de mensajetp_user_data_header- Encabezado de datos de usuario (mapa)message_part_number,message_parts- Campos de mensaje multipartexpires- Marca de tiempo de expiración (por defecto 24 horas)deliver_after- Marca de tiempo de entrega retrasadadeadletter,raw_data_flag,raw_sip_flag- Indicadores booleanos
Respuesta:
{
"status": "success",
"data": {
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"dest_smsc": "smpp-provider",
"message_body": "¡Hola, Mundo!",
"deliver_time": null,
"delivery_attempts": 0,
"expires": "2025-10-17T10:30:00Z",
"inserted_at": "2025-10-16T10:30:00Z"
}
}
Obtener Mensajes para SMSC
GET /api/messages/get_by_smsc?smsc=mi-nombre-smsc
Devuelve todos los mensajes no entregados donde:
destination_smsces nulo O coincide con el nombre SMSC proporcionado- El mensaje no ha expirado
- Listo para enviar (deliver_after es nulo o está en el pasado)
Respuesta:
{
"status": "success",
"data": [
{
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "Hola",
"destination_smsc": "mi-nombre-smsc",
"delivery_attempts": 0
}
]
}
Listar Mensajes con Filtrado Opcional de SMSC
# Listar todos los mensajes en la cola
GET /api/messages
# Listar mensajes para SMSC específico (con filtrado de encabezados)
GET /api/messages
SMSc: mi-nombre-smsc
Sin Encabezado SMSc: Devuelve todos los mensajes en la cola independientemente del estado de entrega o expiración.
Con Encabezado SMSc: Devuelve mensajes no entregados donde:
dest_smsccoincide con el valor del encabezado Odest_smsces nulodeliver_timees nulo (no entregado aún)deliver_afteres nulo o antes/igual al tiempo actual (listo para entregar)expireses después del tiempo actual (no ha expirado)- Ordenado por tiempo de inserción (el más antiguo primero)
Nota: El enfoque del encabezado SMSc permite que los frontends externos consulten sus mensajes utilizando el mismo patrón de punto final, con el encabezado controlando el comportamiento de filtrado.
Respuesta:
[
{
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "¡Hola, Mundo!",
"dest_smsc": "mi-nombre-smsc",
"deliver_time": null,
"delivery_attempts": 0,
"expires": "2025-10-17T10:30:00Z",
"inserted_at": "2025-10-16T10:30:00Z"
}
]
Obtener Mensaje Único
GET /api/messages/{id}
Actualizar Mensaje
PATCH /api/messages/{id}
Content-Type: application/json
{
"status": "delivered",
"delivered_at": "2025-10-16T10:30:00Z"
}
Eliminar SMS
DELETE /api/messages/{id}
Manejar Fallo de Entrega (Incrementar Contador de Reintentos)
Cuando un mensaje falla temporalmente en la entrega, incrementa el contador de intentos de entrega y programa un reintento con retroceso exponencial.
Método 1: Usando PUT (Recomendado)
# Simple y semántico - PUT indica actualizar el estado de entrega
PUT /api/messages/{id}
Método 2: Usando Punto Final Explícito
# Punto final alternativo explícito
POST /api/messages/{id}/increment_delivery_attempt
Ambos métodos incrementan delivery_attempts y establecen un retraso de retroceso exponencial a través de deliver_after:
| Intento | Fórmula de Retroceso | Retraso | Tiempo Total |
|---|---|---|---|
| 1º | 2^1 minutos | 2 min | 2 min |
| 2º | 2^2 minutos | 4 min | 6 min |
| 3º | 2^3 minutos | 8 min | 14 min |
| 4º | 2^4 minutos | 16 min | 30 min |
| 5º | 2^5 minutos | 32 min | 1h 2min |
| 6º | 2^6 minutos | 64 min | 2h 6min |
Respuesta:
{
"id": 123,
"delivery_attempts": 1,
"deliver_after": "2025-10-20T19:05:00Z",
"deliver_time": null,
"expires": "2025-10-21T19:03:00Z",
...
}
Nota: Los mensajes con deliver_after futuro se filtran automáticamente de las solicitudes GET hasta que expire el período de retroceso.
Actualizar Mensaje (Actualización Parcial)
Para actualizar campos específicos del mensaje (comportamiento inalterado):
PATCH /api/messages/{id}
Content-Type: application/json
{
"dest_smsc": "puerta-actualizada",
"status": "delivered"
}
Importante: PUT y PATCH se comportan de manera diferente:
- PUT → Incrementa los intentos de entrega con retroceso (no se requiere cuerpo)
- PATCH → Realiza actualizaciones parciales de campos (se requiere cuerpo)
Seguimiento de Salud del Frontend
El núcleo SMS_C rastrea la salud y disponibilidad de los frontends externos a través de un sistema de registro. Esto permite monitorear el tiempo de actividad del frontend, detectar fallos y mantener datos históricos de disponibilidad.
Nota: El registro del frontend NO se utiliza para la entrega o enrutamiento de mensajes. Los mensajes se enrutan en función del campo dest_smsc. El sistema de registro existe únicamente para el monitoreo de salud y visibilidad operativa.
Cómo Funciona el Registro del Frontend
Cada frontend externo (puerta de enlace SMPP, IMS, SS7/MAP) envía periódicamente un registro de latido al núcleo SMS_C:
- Intervalo de Latido: Los frontends deben registrarse cada 30-60 segundos
- Tiempo de Expiración: Los registros expiran después de 90 segundos sin una actualización
- Gestión Automática del Estado:
- Nuevos frontends crean un nuevo registro de registro
- Frontends activos existentes actualizan su registro (extiende la expiración)
- Frontends expirados que vuelven a estar en línea crean un nuevo período de registro
Puntos Finales de Registro del Frontend
Registrar/Actualizar Frontend (Latido)
POST /api/frontends
Content-Type: application/json
{
"frontend_name": "puerta-sMPP-1",
"frontend_type": "SMPP",
"ip_address": "10.0.1.5",
"hostname": "smpp-gw-01",
"uptime_seconds": 3600,
"configuration": "{\"port\": 2775, \"system_id\": \"usuario_sMPP\"}"
}
Campos Requeridos:
frontend_name- Identificador único para la instancia del frontendfrontend_type- Tipo de frontend (SMPP, IMS, MAP, etc.)
Campos Opcionales:
ip_address- Dirección IP del frontend (detectada automáticamente desde la fuente de la solicitud si no se proporciona)hostname- Nombre de host del servidor frontenduptime_seconds- Segundos desde que se inició el frontendconfiguration- Cadena JSON con la configuración específica del frontend
Nota: Si no se proporciona ip_address, el núcleo SMS_C utilizará automáticamente la IP de origen de la solicitud HTTP. Esto funciona tanto con conexiones directas como con solicitudes proxy (a través del encabezado X-Forwarded-For).
Respuesta:
{
"id": 42,
"frontend_name": "puerta-sMPP-1",
"frontend_type": "SMPP",
"ip_address": "10.0.1.5",
"hostname": "smpp-gw-01",
"uptime_seconds": 3600,
"status": "activo",
"last_seen_at": "2025-10-20T10:30:00Z",
"expires_at": "2025-10-20T10:31:30Z",
"inserted_at": "2025-10-20T10:00:00Z"
}
Listar Todos los Registros de Frontend
GET /api/frontends
Devuelve todos los registros de frontend (activos y expirados), ordenados por la actividad más reciente.
Listar Solo Frontends Activos
GET /api/frontends/active
Devuelve solo los frontends actualmente activos (no expirados).
Obtener Estadísticas del Frontend
GET /api/frontends/stats
Devuelve estadísticas resumidas:
{
"active": 5,
"expired": 12,
"unique_frontends": 8
}
Obtener Historial del Frontend
GET /api/frontends/history/{frontend_name}
Devuelve todos los registros históricos para un frontend específico, útil para analizar patrones de tiempo de actividad/inactividad.
Ejemplo:
GET /api/frontends/history/puerta-sMPP-1
Obtener Registro Específico
GET /api/frontends/{id}
Implementación en Frontends Externos
Los frontends externos deben implementar una tarea en segundo plano que envíe latidos:
Ejemplo (pseudocódigo):
import time
import requests
def send_heartbeat():
"""Enviar latido cada 30 segundos"""
while True:
try:
data = {
"frontend_name": "mi-puerta-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("Latido enviado con éxito")
else:
logger.error(f"Fallo en el latido: {response.status_code}")
except Exception as e:
logger.error(f"Error en el latido: {e}")
time.sleep(30) # Enviar cada 30 segundos
# Iniciar latido en hilo en segundo plano
threading.Thread(target=send_heartbeat, daemon=True).start()
Monitoreo de Salud del Frontend
Panel de Control - La interfaz web en https://localhost:8086 muestra:
- Frontends actualmente activos
- Marca de tiempo de la última vez que se vio cada frontend
- Seguimiento del tiempo de actividad
- Disponibilidad histórica
Consultas API:
# Obtener todos los frontends activos
curl https://localhost:8443/api/frontends/active
# Comprobar si un frontend específico está activo
curl https://localhost:8443/api/frontends/history/puerta-sMPP-1 | jq '.[0].status'
# Obtener estadísticas de salud
curl https://localhost:8443/api/frontends/stats
Otros Puntos Finales
Estado
GET /api/status
Ubicaciones
GET /api/locations
POST /api/locations
GET /api/locations/{id}
PATCH /api/locations/{id}
DELETE /api/locations/{id}
Eventos SS7
GET /api/ss7_events
POST /api/ss7_events
GET /api/ss7_events/{id}
PATCH /api/ss7_events/{id}
DELETE /api/ss7_events/{id}
Cola de Mensajes 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}
Rendimiento
El núcleo SMS_C ofrece un rendimiento excepcional utilizando Mnesia para el almacenamiento de mensajes en memoria con archivo automático a SQL para la retención a largo plazo de CDR.
Resultados de Referencia
Medido en Intel i7-8650U @ 1.90GHz (8 núcleos):
Rendimiento de Inserción de Mensajes:
insert_message(con enrutamiento): 1,750 msg/sec (0.58ms latencia promedio)insert_message(simple): 1,750 msg/sec (0.57ms latencia promedio)- Capacidad de ~150 millones de mensajes por día
Rendimiento de Consulta:
get_messages_for_smsc: 800 msg/sec (1.25ms promedio)list_message_queues: Acceso rápido en memoria- Uso de memoria: 62 KB por operación de inserción
Arquitectura
Estrategia de Almacenamiento:
- Mensajes Activos: Almacenados en Mnesia (en memoria + disco) para acceso ultrarrápido
- Archivo de Mensajes: Archivado automáticamente a SQL para almacenamiento a largo plazo de CDR
- Retención: Período de retención configurable (predeterminado: 24 horas)
- Sin cuello de botella SQL: Todas las operaciones de mensajes activos evitan SQL
Configuración
El almacenamiento y la retención de mensajes se configuran en config/runtime.exs:
config :sms_c,
message_retention_hours: 24, # Archivar mensajes más antiguos de 24 horas
batch_insert_batch_size: 100, # Tamaño del lote de CDR para archivo SQL
batch_insert_flush_interval_ms: 100 # Intervalo de vaciado de CDR
Para orientación detallada sobre ajustes, consulta: docs/PERFORMANCE_TUNING.md
Monitoreo
Panel de Control - Interfaz web en https://localhost:8086
- Ver cola de mensajes
- Enviar mensajes de prueba
- Gestionar enrutamiento de SMS (ver Guía de Enrutamiento)
- Simular decisiones de enrutamiento
- Ver recursos del sistema
- Rastrear estadísticas de trabajadores por lotes
Estadísticas de Trabajadores por Lotes:
# Obtener estadísticas actuales del trabajador por lotes
SmsC.Messaging.BatchInsertWorker.stats()
Devuelve:
%{
total_enqueued: 10000,
total_flushed: 9900,
current_queue_size: 100,
last_flush_duration_ms: 45
}
Registros - Registros de la aplicación escritos en stdout
# Ver registros en tiempo real
tail -f log/dev.log
Solución de Problemas
Puerto Ya Está en Uso
# Encontrar proceso que usa el puerto
lsof -i :4000
# Matar el proceso
kill -9 <PID>
Frontend Externo No Conectando
Síntomas: Mensajes atascados en la cola, registros del frontend muestran errores de conexión
Verifica:
- Verifica que
API_BASE_URLesté configurado correctamente en el frontend - Comprueba que el núcleo SMS_C esté en ejecución y accesible
- Revisa las reglas de red/firewall
- Verifica la configuración del frontend
Solución:
# Probar conectividad API desde el frontend
curl http://localhost:4000/api/status
# Reiniciar frontend
export API_BASE_URL="http://localhost:4000"
# Iniciar aplicación frontend
Mensajes No Entregados
Síntomas: Mensajes permanecen no entregados, los intentos de reintento se incrementan
Verifica:
- Registros del frontend para errores de envío
- Conectividad de red externa
- Configuración del frontend (credenciales, direcciones)
- Compatibilidad del formato del mensaje
Ver mensajes fallidos:
# Obtener mensajes con intentos de reintento
curl https://localhost:8443/api/messages | jq '.data[] | select(.delivery_attempts > 0)'
Alta Latencia de Mensajes
Síntomas: Mensajes tardando más de lo esperado, acumulación en la cola
Verifica:
- Intervalo de sondeo del frontend (puede ser necesario disminuir para un sondeo más frecuente)
- Rendimiento de la base de datos
- Latencia de red hacia sistemas externos
Monitorear profundidad de la cola:
watch -n 5 'curl -s https://localhost:8443/api/messages | jq ".data | length"'