Idioma: C++ con pegamento lógico Lua.
El código ha sido probado para compilarse y ejecutarse en Ubuntu x86, Ubuntu x86_64 y Gentoo x86_64 siempre que se utilice la versión adecuada de LuaJIT. Modificaciones a libjansson para eliminar el formato forzado de flotantes con .0 después de realizar números enteros para admitir adecuadamente "enteros" y aún tener la conversión automática de Lua, que solo admite dobles.
También se compila en Ubuntu 12.04 LTS x86_64, dado que libjansson y google-glog se compilan manualmente.
LuaJIT
libev
cliente evhttp
google-glog
google-perftools (tcmalloc): opcional con modificaciones menores de fuente y archivos MAKE
libansson
contratados
libicu - ver administrador de paquetes local
curl - ver administrador de paquetes local
boost: opcional si reemplazas los punteros intrusivos con algo propio.
Es probable que se requieran modificaciones en src/Makefile para reflejar sus carpetas de instalación. Se agradece un sistema de construcción más sólido, pero no era necesario en el momento en que se redactó el proyecto. (¿CHacer?)
Yo (@zwagoth) aprendí algo aquí: siempre haz comentarios al momento de escribir. Para compensar esto , documentaré la estructura básica del programa para ayudar a comprenderlo.
Disculpas por el desorden de una base de código. Este fue también uno de mis primeros proyectos en C++ de cualquier escala y complejidad.
Contiene casi toda la lógica central para el manejo de mensajes de protocolo, mensajes RTB y varias devoluciones de llamadas de eventos básicos, como conexión y desconexión.
El archivo contiene tablas de funciones anónimas que llevan el nombre de los eventos que manejan. Este archivo no debe almacenar ningún estado y se considera únicamente un área de almacenamiento lógico. El diseño básico de este archivo es permitir la lógica adhesiva sin obligar a Lua a realizar ningún trabajo pesado y minimizar la cantidad de datos que se transfieren dentro y fuera de Lua a costa de más llamadas a funciones en C++.
Hay cuatro tablas insertadas en el espacio de nombres de archivos global con nombres cortos para facilitar la escritura:
u
: funciones de conexión/carácter (usuario)
s
: funciones de servidor/estado global
c
: funciones de estado del canal
const
: identificadores de error y valores constantes
Las funciones de devolución de llamada del protocolo aceptan dos parámetros, la conexión a la que está asociado el comando y una copia de la tabla de los argumentos json proporcionados.
Las conexiones son números opacos y NO deben modificarse de ninguna manera. Cambiar el valor de una conexión no es seguro. Has sido advertido.
Un archivo lua de variables de configuración que se utilizan durante el inicio y el funcionamiento del demonio de chat.
Un servidor de políticas flash minimalista. Si necesita admitir Flash, esto es lo que debe ejecutar. Personalice la política incorporada según sus necesidades.
Punto de entrada al programa. Se ocupa de la inicialización de hilos de fondo y rizos.
Hace demasiadas cosas. El flujo de código comienza en Server::run()
.
El flujo de conexión es el siguiente:
listenCallback handshakeCallback connectionWriteCallback connectionReadCallback connectionwriteCallback
listenCallback
configura controladores de eventos por conexión y pasa el flujo a...
handshakeCallback
, que maneja las lecturas de los apretones de manos de websocket.
connectionWriteCallback
controla cuándo una conexión está lista para escribirse y se habilita cuando hay elementos en la cola para escribirse en la conexión. Maneja el almacenamiento en búfer.
connectionReadCallback
maneja todos los eventos de lectura cuando finaliza la fase de protocolo de enlace. Todo el análisis del protocolo ocurre aquí y los comandos se envían y ejecutan desde dentro de esta función.
pingCallback
maneja el envío de eventos de ping a los clientes. Si se cambia el protocolo, esta debería ser una de las primeras cosas en cambiar.
connectionTimerCallback
se activa periódicamente y comprueba si la conexión está muerta y la limpia.
runLuaEvent
es magia de tritón. También donde ocurre la magia para cada comando. Aquí es donde cada comando pasa del código C++ al código Lua. Maneja todos los estados de Lua y la devolución de llamada para asegurarse de que Lua no quede atrapado en un bucle infinito. Maneja el error de impresión desde el interior de Lua.
Este archivo almacena todos los datos estatales relacionados con canales, conexiones, prohibiciones y moderación.
Las conexiones se dividen entre identificadas y no identificadas, ya que los nombres de los personajes no se conocen hasta que el servidor de inicio de sesión valida que existen y los mantiene fuera del grupo de caracteres que se pueden buscar por nombre.
Maneja guardar y restaurar el estado en el disco.
Se ejecuta como un hilo en segundo plano que procesa las solicitudes de inicio de sesión y las responde y las devuelve al hilo principal.
Sistema de inicio de sesión serializado, utiliza una cola de inicio de sesión global. Esto podría mejorarse bastante.
La conversión a curl_multi sería buena, pero implica una interacción divertida con libev o muchas encuestas a ciegas. Las colas deben bloquearse antes de acceder a ellas, debido a los subprocesos.
Clase de canal básica, mantiene datos de estado sobre un canal durante su vida útil. Todas las acciones de bajo nivel relacionadas con los canales ocurren en este archivo a través de ganchos en Lua. Generalmente envuelto en un puntero intrusivo para administrar la vida útil de la instancia.
Aquí es donde ocurre la serialización y deserialización de canales desde json.
Maneja todas las conexiones de red y depura los estados de Lua.
Mantiene listas de problemas, estado, mensajes de estado y género.
Mantiene la lista de amigos e ignorados.
Mangos por aceleradores de conexión.
Aquí es donde ocurre el almacenamiento en búfer de los datos de salida.
Generalmente se transmite mediante punteros intrusivos para gestionar la vida útil de la instancia.
Mantiene una lista interna de los canales que se han unido. Esto debe mantenerse sincronizado con la lista de usuarios del canal real.
Este archivo está reservado para las pocas funciones que requerían velocidad bruta para poder personalizarse. Maneja el comando de inicio de sesión ( IDN
). Maneja el comando de depuración ( ZZZ
). Maneja el comando de búsqueda ( FKS
).
Todos los comandos de contenedor de Lua que se incluyen en la categoría s
en archivos Lua.
Todos los comandos de contenedor de Lua que se incluyen en la categoría u
en archivos Lua.
Todos los comandos de contenedor de Lua que se incluyen en la categoría c
en archivos Lua.
Todos los valores del contenedor Lua que se incluyen en la categoría const
en archivos Lua.
Mensajes de error y definiciones.
Utilice las macros definidas para comprobar errores y tipos. Esta es la única forma de evitar fallas si se pasa inesperadamente el tipo incorrecto como datos livianos a una función.
Asegúrate de que tu pila de Lua esté equilibrada. Me he esforzado mucho para asegurarme de que esto sea cierto, pero es fácil cometer errores.
Evite devolver tablas al código Lua, son costosas de construir y, a menudo, tienen un período de uso corto. Utilice el mejor criterio para equilibrar el costo de las llamadas a funciones y el costo de crear tablas.
Evite pasar hilos a Lua innecesariamente. Puede resultar costoso debido a las copias de memoria.
Abuso de que lua acepta múltiples valores de retorno como característica nativa. Vea las dos notas anteriores.
Maneja el hilo redis de solo inserción. Es un pequeño resumen de los comandos de Redis y sus valores de retorno.
Utiliza una cola de entrada para recibir comandos. Los comandos se ejecutan de forma periódica y no tienen garantía de confiabilidad. Se puede deshabilitar e ignora la entrada cuando está deshabilitado.
gdb funciona bien para depurar esta aplicación. Deshabilitar tcmalloc y la sección JIT de LuaJIT debería ayudar enormemente a depurar fallos (tcmalloc puede ocultar algunos daños menores en el montón).
Las herramientas de creación de perfiles de memoria en tcmalloc son bastante buenas. Consulte la documentación de tcmalloc para obtener más información sobre cómo usarlo.