¿Por qué el lenguaje Go es adecuado para desarrollar servidores de juegos en línea?
Leí esta publicación en golang-China hace algún tiempo:
Personalmente creo que golang es muy adecuado para el desarrollo de juegos en línea del lado del servidor, así que escribí este artículo para resumir él.
Desde la perspectiva de los juegos online:
El funcionamiento exitoso de un juego online depende en gran medida de la comunidad formada espontáneamente por los jugadores. Sólo cuando los jugadores formen espontáneamente un ecosistema estable podrá continuar el juego y evitar la aparición de pueblos fantasmas. Esto requiere importar una gran cantidad de usuarios varias veces y solo se puede completar cuando la cantidad de usuarios en línea simultáneos alcanza un cierto punto crítico. Por lo tanto, es muy necesario que varias personas estén conectadas al mismo tiempo.
En cuanto a la jugabilidad común de los juegos en línea, a excepción de las estadísticas y las funciones de resumen de datos, como las clasificaciones, básicamente no hay aplicaciones que requieran mucho tiempo de CPU. En proyectos anteriores, varios cálculos de daños causados por el combate en tiempo real no consumían mucha CPU. Para completar una operación, el jugador necesita ir y venir de un cliente a otro. Para obtener una alta velocidad de respuesta y satisfacer la experiencia del jugador, el procesamiento del lado del servidor no puede llevar demasiado tiempo. Por tanto, el uso de CPU correspondiente a cada solicitud es relativamente pequeño.
La IO de los juegos en línea se divide principalmente en dos aspectos, uno es IO de red y el otro es IO de disco. En términos de IO de red, se puede dividir en IO de recursos artísticos y IO de instrucciones de lógica del juego. Aquí analizamos principalmente el IO de la lógica del juego. La IO de la lógica del juego es similar al uso de la CPU. La cantidad de bytes por solicitud es muy pequeña, pero debido a que varias personas están en línea al mismo tiempo, la cantidad de concurrencia es bastante alta. Además, la transmisión de información cartográfica también traerá comunicaciones de red más frecuentes. En términos de IO del disco, se trata principalmente de guardar datos del juego. El uso de diferentes bases de datos marcará grandes diferencias. En proyectos anteriores, hemos pasado por el proceso de cambiar de MySQL a bases de datos en memoria como MongoDB, y la E/S del disco ya no es un cuello de botella. En términos generales, sigue siendo una solución utilizar la memoria como búfer de primer nivel para evitar leer y escribir una gran cantidad de pequeños bloques de datos.
Basándonos en estas características de los juegos en línea, las características del lenguaje de golang son muy adecuadas para desarrollar juegos en el lado del servidor.
En primer lugar, el lenguaje go proporciona el mecanismo goroutine como mecanismo de concurrencia nativo. Cada gorutina requiere muy poca memoria y, en aplicaciones reales, se puede iniciar una gran cantidad de gorutinas para responder a conexiones simultáneas. Goroutine es muy similar a greenlet en gevent. Cuando encuentra un bloqueo de IO, el programador cambiará automáticamente a otra goroutine para su ejecución para garantizar que la CPU no espere debido a IO. En comparación con gevent, goroutine no tiene las restricciones GIL subyacentes de Python, por lo que no es necesario utilizar múltiples procesos para exprimir el rendimiento de las máquinas de múltiples núcleos. Al establecer el número máximo de subprocesos, puede controlar los subprocesos iniciados por go. Cada subproceso ejecuta una rutina para permitir que la CPU se ejecute a plena carga.
Al mismo tiempo, el lenguaje go proporciona un canal de mecanismo de comunicación único para goroutine. Cuando se lee o escribe el canal, la gorutina que actualmente opera el canal también se suspenderá, lo cual es una especie de comunicación de bloqueo sincrónico. Esto no solo logra el propósito de la comunicación, sino que también logra la sincronización. Desde la perspectiva del modelo CSP, el modelo de concurrencia resuelve tareas a través de un conjunto de procesos y activadores de eventos entre procesos. Aunque, entre los lenguajes de programación convencionales, siempre que sean Turing completos, todos pueden lograr las mismas funciones. Sin embargo, el mecanismo de comunicación entre rutinas proporcionado por el lenguaje go revela elegantemente la naturaleza de la comunicación entre rutinas y evita la carga psicológica que supuso para los programadores el uso explícito de bloqueos en el pasado, lo que de hecho es una gran ventaja. Los programadores que desarrollan juegos en línea pueden escribir la lógica del juego en un estilo de bloqueo de un solo subproceso sin consideraciones adicionales sobre la programación de subprocesos y las dependencias de datos entre subprocesos. Porque la comunicación del canal entre subprocesos ya ha expresado la dependencia de datos entre subprocesos y el programador go lo manejará correctamente.
Además, el mecanismo gc proporcionado por el lenguaje go y el uso protegido de punteros pueden reducir en gran medida la presión de desarrollo de los programadores y mejorar la eficiencia del desarrollo.
De cara al futuro, espero que la comunidad de lenguajes Go proporcione más mecanismos de aislamiento entre gorutinas.
Personalmente, recomiendo ampliamente la filosofía de falla frágil de la comunidad Erlang, que alienta a las aplicaciones a fallar lo antes posible cuando ocurre un comportamiento inesperado y luego bifurcar un nuevo proceso para manejar nuevas solicitudes. Para el mecanismo de rutina, el programador debe asegurarse de que la función ejecutada no se repita sin cesar, lo que provocará que el hilo se atasque. Si puede personalizar el tiempo máximo de ejecución de CPU de la función ejecutada por goroutine y el espacio máximo de memoria que se puede utilizar, será de gran beneficio para mejorar la solidez del sistema.