Saltar al contenido principal

SMSc - Aplicación del Centro de Servicio SMS

Aplicación del Centro SMS construida con Phoenix/Elixir, proporcionando una cola de mensajes centralizada y una API REST para el enrutamiento y la entrega de SMS.

Documentación

Documentación Principal

Documentación de Cumplimiento

Rendimiento

Descripción General de la Arquitectura

El núcleo de SMS_C proporciona una cola de mensajes y una API REST independientes del protocolo. 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, enrutamiento y 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 de llamada/llamados)
  • Filtrado por tipo de SMSC y 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 cobro por ruta
  • Interfaz web para gestión de rutas
  • Actualizaciones de rutas en tiempo real sin interrupción del servicio

📖 Consulta la Guía de Enrutamiento 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 (o http://localhost:8080 sin 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 individual del 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 central. 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 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 pool: 1

Configuración de 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 Cola de Mensajes

Los ajustes de la cola de mensajes se configuran en config/runtime.exs:

  • Tiempo de carta muerta: 1440 minutos (24 horas antes de que el mensaje expire)

Integración de Cobro

Los ajustes de cobro 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 SMS

El sistema de enrutamiento SMS utiliza rutas dinámicas respaldadas por bases de datos que se pueden gestionar a través de la interfaz web o 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 de llamada/llamados)
  • Filtrado por SMSC de origen y tipo
  • Enrutamiento basado en prioridad y peso
  • Capacidades de respuesta automática y eliminación
  • Control de cobro por ruta
  • Gestión en tiempo de ejecución a través de la interfaz web en /routing

📖 Consulta la Guía de Enrutamiento 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 es 24h desde ahora
}

Campos Requeridos:

  • destination_msisdn - Número de teléfono de destino
  • message_body - Contenido del texto del mensaje
  • source_msisdn - Número de teléfono de origen
  • source_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 IMSI
  • tp_dcs_character_set - Codificación de caracteres (gsm7, 8bit, latin1, ucs2)
  • tp_dcs_coding_group - Grupo de codificación DCS
  • tp_dcs_compressed - Bandera de compresión (booleano)
  • tp_dcs_has_message_class - Bandera de clase de mensaje (booleano)
  • tp_dcs_message_class - Valor de clase de mensaje
  • tp_user_data_header - Encabezado de datos de usuario (mapa)
  • message_part_number, message_parts - Campos de mensaje multipart
  • expires - Marca de tiempo de expiración (por defecto 24 horas)
  • deliver_after - Marca de tiempo de entrega retrasada
  • deadletter, raw_data_flag, raw_sip_flag - Banderas booleanas

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_smsc es nulo O coincide con el nombre SMSC proporcionado
  • El mensaje no ha expirado
  • Listo para enviar (deliver_after es nulo o 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 encabezado)
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_smsc coincide con el valor del encabezado O dest_smsc es nulo
  • deliver_time es nulo (no entregado aún)
  • deliver_after es nulo o antes/igual al tiempo actual (listo para entregar)
  • expires es después del tiempo actual (no expirado)
  • Ordenado por tiempo de inserción (más antiguo primero)

Nota: El enfoque del encabezado SMSc permite a los frontends externos consultar 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 Entrega Fallida (Incrementar Contador de Reintentos)

Cuando una entrega de mensaje falla temporalmente, 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:

IntentoFórmula de RetrocesoRetrasoTiempo Total
2^1 minutos2 min2 min
2^2 minutos4 min6 min
2^3 minutos8 min14 min
2^4 minutos16 min30 min
2^5 minutos32 min1h 2min
2^6 minutos64 min2h 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 son automáticamente filtrados 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 de 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 monitoreo de salud y visibilidad operativa.

Cómo Funciona el Registro de Frontend

Cada frontend externo (puerta de enlace SMPP, IMS, SS7/MAP) envía periódicamente un registro de latido al núcleo SMS_C:

  1. Intervalo de Latido: Los frontends deben registrarse cada 30-60 segundos
  2. Tiempo de Expiración: Los registros expiran después de 90 segundos sin una actualización
  3. Gestión Automática de 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 de Frontend

Registrar/Actualizar Frontend (Latido)
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\"}"
}

Campos Requeridos:

  • frontend_name - Identificador único para la instancia del frontend
  • frontend_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 del host del servidor frontend
  • uptime_seconds - Segundos desde que se inició el frontend
  • configuration - 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": "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"
}
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 de Frontend
GET /api/frontends/stats

Devuelve estadísticas resumidas:

{
"active": 5,
"expired": 12,
"unique_frontends": 8
}
Obtener Historial de 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/smpp-gateway-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-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("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 de fondo
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

# Verificar si un frontend específico está activo
curl https://localhost:8443/api/frontends/history/smpp-gateway-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 Benchmark

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 ultra-rápido
  • Archivo de Mensajes: Archivados automáticamente a SQL para almacenamiento a largo plazo de CDR
  • Retención: Período de retención configurable (por defecto: 24 horas)
  • Sin cuellos 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 la optimización, 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 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

Verificar:

  • Verificar que API_BASE_URL esté configurado correctamente en el frontend
  • Comprobar que el núcleo SMS_C esté en funcionamiento y accesible
  • Revisar reglas de red/firewall
  • Verificar 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 Están Siendo Entregados

Síntomas: Mensajes permanecen no entregados, intentos de reintento incrementándose

Verificar:

  1. Registros del frontend para errores de envío
  2. Conectividad de red externa
  3. Configuración del frontend (credenciales, direcciones)
  4. 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

Verificar:

  1. Intervalo de sondeo del frontend (puede necesitar disminuir para un sondeo más frecuente)
  2. Rendimiento de la base de datos
  3. Latencia de red hacia sistemas externos

Monitorear profundidad de la cola:

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