Saltar al contenido principal

SMSc - Aplicación del Centro de Servicio SMS

Aplicación del Centro de SMS construida con Phoenix/Elixir, proporcionando una cola de mensajes centralizada y una API REST para el enrutamiento y 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 frontales 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 de SMS_C maneja la persistencia de mensajes, enrutamiento y API
  • Los frontales externos (SMPP, IMS, SS7/MAP) manejan la comunicación específica del protocolo
  • Todos los frontales 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 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 caída de mensajes
  • Control de carga por ruta
  • Interfaz web para gestión de rutas
  • Actualizaciones de rutas en tiempo real sin interrupción del servicio

📖 Consulte 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 máximos de reintento 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 Frontales Externos: Cada frontal de protocolo es una aplicación independiente. Consulte la documentación individual del frontal para obtener 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 de 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: 1440 minutos (24 horas antes de que el mensaje expire)

Integración de Carga​

Los ajustes de carga se configuran en config/runtime.exs:

  • URL: http://localhost:2080/jsonrpc
  • Inquilino: mnc001.mcc001.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 base de datos que se pueden gestionar 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 caída
  • Control de carga por ruta
  • Gestión en tiempo de ejecución a través de la interfaz web en /routing

📖 Consulte 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 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 del 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 de 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 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_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 (el más antiguo primero)

Nota: El enfoque del encabezado SMSc permite que los frontales 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 Entrega Fallida (Incrementar Contador de Reintentos)​

Cuando una entrega de mensaje falla temporalmente, incremente el contador de intentos de entrega y programe 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 explícito alternativo
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
1º2^1 minutos2 min2 min
2º2^2 minutos4 min6 min
3º2^3 minutos8 min14 min
4º2^4 minutos16 min30 min
5º2^5 minutos32 min1h 2min
6º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 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-de-enlace-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 campo (se requiere cuerpo)

Seguimiento de Salud de Frontales​

El núcleo de SMS_C rastrea la salud y disponibilidad de los frontales externos a través de un sistema de registro. Esto permite monitorear el tiempo de actividad de los frontales, detectar fallos y mantener datos históricos de disponibilidad.

Nota: El registro de frontales 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 de Frontales​

Cada frontal 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 frontales 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 del Estado:
    • Nuevos frontales crean un nuevo registro de registro
    • Frontales activos existentes actualizan su registro (extienden la expiración)
    • Frontales expirados que vuelven a estar en línea crean un nuevo período de registro

Puntos Finales de Registro de Frontales​

Registrar/Actualizar Frontal (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 frontal
  • frontend_type - Tipo de frontal (SMPP, IMS, MAP, etc.)

Campos Opcionales:

  • ip_address - Dirección IP del frontal (detectada automáticamente desde la fuente de la solicitud si no se proporciona)
  • hostname - Nombre del host del servidor frontal
  • uptime_seconds - Segundos desde que se inició el frontal
  • configuration - Cadena JSON con la configuración específica del frontal

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 Frontales​
GET /api/frontends

Devuelve todos los registros de frontales (activos y expirados), ordenados por la actividad más reciente.

Listar Solo Frontales Activos​
GET /api/frontends/active

Devuelve solo los frontales actualmente activos (no expirados).

Obtener Estadísticas de Frontales​
GET /api/frontends/stats

Devuelve estadísticas resumidas:

{
"active": 5,
"expired": 12,
"unique_frontends": 8
}
Obtener Historial de Frontales​
GET /api/frontends/history/{frontend_name}

Devuelve todos los registros históricos para un frontal 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 Frontales Externos​

Los frontales 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-de-enlace-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 de Frontales​

Panel de Control - La interfaz web en https://localhost:8086 muestra:

  • Frontales actualmente activos
  • Marca de tiempo de la última vista para cada frontal
  • Seguimiento de tiempo de actividad
  • Disponibilidad histórica

Consultas API:

# Obtener todos los frontales activos
curl https://localhost:8443/api/frontends/active

# Verificar si un frontal 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 de 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 Puntos 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 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

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 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 frontal muestran errores de conexión

Verifique:

  • Verifique que API_BASE_URL esté configurado correctamente en el frontal
  • Verifique que el núcleo SMS_C esté en ejecución y accesible
  • Revise las reglas de red/firewall
  • Verifique la configuración del frontal

Solución:

# Probar conectividad API desde el frontal
curl http://localhost:4000/api/status

# Reiniciar frontal
export API_BASE_URL="http://localhost:4000"
# Iniciar aplicación frontal

Mensajes No Entregados​

Síntomas: Los mensajes permanecen no entregados, los intentos de reintento se incrementan

Verifique:

  1. Registros del frontal para errores de envío
  2. Conectividad de red externa
  3. Configuración del frontal (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

Verifique:

  1. Intervalo de sondeo del frontal (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"'