WhatsApp, Discord y cómo organizar la comunicación simultánea para millones de usuarios

Soy un desarrollador full stack, emprendedor individual. En mi experiencia, una de las clases de proyectos más populares que la gente acude a nosotros para desarrollar es una aplicación en tiempo real. Por supuesto, conoces este tipo de aplicaciones: WhatsApp, Discord, Slack, etc. Al desarrollar aplicaciones en tiempo real, se deben tener en cuenta varios factores, como la escalabilidad, la tolerancia a fallos, la capacidad de respuesta y la distribución. Esta no es una tarea fácil, especialmente para un equipo pequeño o un desarrollador en solitario.

Pero ¿y si te dijera… que puedes crear aplicaciones en tiempo real que Se puede escalar a más de un millón de usuarios con solo unos pocos desarrolladores.? Además, dichas aplicaciones podrían implementarse casi sin demoras y con un costo mínimo. Lo que quiero decir con esto es que para hacer esto necesitas dominar un arma secreta llamada “Máquina Virtual”. erlang» o BEAM (Bogdan/Björn Abstract Machine para Erlang).

Este artículo explicará cómo Erlang y su contraparte más moderna, Elixir, evolucionaron hasta convertirse en arma secreta para el desarrollo de aplicaciones en tiempo real, como WhatsApp y Discord. Estoy especialmente emocionado de compartir estas ideas porque llevo más de 3 años usando Elixir y puedo decir que es una de las mejores decisiones que he tomado a lo largo de mi carrera.

Bueno, vayamos al grano.

Origen de Erlang y Elixir

Idioma erlang Fue desarrollado a finales de los años 1980 por la empresa sueca de telecomunicaciones Ericsson. La empresa buscó crear un lenguaje de programación que cumpliera con los requisitos. concurrencia a gran escala y alta disponibilidad, que operan en sistemas de telecomunicaciones. Erlang está diseñado con énfasis en procesos livianos, mensajería y tolerancia a fallas. Por tanto, Erlang es la forma más conveniente de crear sistemas diseñados para un funcionamiento continuo y que requieran alta disponibilidad.

Con ayuda procesos ligeros Erlang puede ejecutar miles o incluso millones de acciones paralelas en el mismo sistema al mismo tiempo y, como resultado, hay poca penalización en el rendimiento. El modelo de paso de mensajes de Erlang permite una comunicación eficiente entre procesos sin compartir memoria. Esto también reduce la complejidad y aumenta la tolerancia a fallos. Filosofía “Déjalo estrellarse” (Déjalo romperse) Alienta a los desarrolladores a escribir código que pueda recuperarse de fallas con facilidad, lo que contribuye a la confiabilidad general de las aplicaciones escritas en Erlang.

Idioma Elixir, creado en 2011 por José Valim, se basa en la base sólida de la máquina virtual Erlang, pero presenta una sintaxis más moderna centrada en la comodidad del desarrollador. Elixir conserva la poderosa concurrencia y tolerancia a fallas de Erlang, pero agrega mejoras como metaprogramación y programación funcional. Sintaxis del elixir inspirado en rubípor lo que es especialmente fácil para los desarrolladores familiarizados con Ruby acostumbrarse a trabajar con Elixir.

Juntos, Erlang y Elixir forman una potente pila sobre la que se pueden crear aplicaciones fiables que sean fáciles de escalar y mantener. Todas estas características hacen que este par de idiomas sea ideal para empresas como WhatsApp y Discord, ya que estas aplicaciones tienen que manejar una gran cantidad de actividad competitiva de muchos usuarios con poco o ningún tiempo de inactividad.

Cinco características clave que hacen de BEAM el arma secreta para el desarrollo de aplicaciones en tiempo real

Cuando WhatsApp y Discord decidieron crear aplicaciones en tiempo real utilizando la pila BEAM, se basó en varios factores críticos: tolerancia a fallas, escalabilidad, distribución, capacidad de respuesta y la capacidad de actualizar el código en vivo.

Exploremos cómo estas características se alinean con los requisitos de las plataformas que permiten la comunicación en tiempo real.

Tolerancia a fallos

La confiabilidad es una característica crítica para aplicaciones como WhatsApp y Discord, que están diseñadas para ejecutarse continuamente con poco o ningún tiempo de inactividad. La filosofía de Erlang es “deja que se rompa» garantiza que los fallos de procesos individuales no conduzcan al fallo de todo el sistema. Por el contrario, los procesos están aislados y sus supervisores pueden reiniciar los procesos fallidos. Si el sistema está diseñado de esta manera, el impacto de las paradas de emergencia se minimiza y la estabilidad de toda la aplicación solo aumenta.

Escalabilidad

Para escalar el sistema, es necesario no sólo garantizar que se pueda expandir horizontalmente utilizando servidores físicos, sino también garantizar la comunicación entre estos servidores. Pero la comunicación en sistemas distribuidos siempre está plagada de dificultades: condiciones de carrera, descubrimiento de nodos, violaciones de orquestación, etc. Estos problemas surgen por diversas razones, principalmente derivadas de una mala gestión de la complejidad.

A su vez, BEAM proporciona escalabilidad sin otras herramientas exóticas pero peligrosas que existen para estos fines. BEAM maneja la comunicación entre nodos a la perfección. Veamos un ejemplo sencillo de cómo se intercambia información entre dos nodos en Elixir:

En primer lugar, necesitas iniciar dos nodos:

# Запускаем первый узел, на котором нет функции `Hello.world/0`
$ iex --sname node1@127.0.0.1 --cookie secret -S mix

# Запускаем второй узел, на котором есть функция `Hello.world/0`
$ iex --sname node2@127.0.0.1 --cookie secret -S mix

Después de esto, puedes conectar los dos nodos. Conecte el `nodo1` al `nodo2`:

# В node1
iex(node1@127.0.0.1)1> Node.connect(:"node2@127.0.0.1")
true

# Теперь можно вызвать функцию `Hello.world/0` из `node2` в `node1`
iex(node1@127.0.0.1)2> Node.spawn(:"node2@127.0.0.1", fn -> Hello.world() end)
Hello, world!

Este fue un ejemplo simple, pero también muestra lo fácil que es proporcionar comunicación entre nodos en Elixir. Sin la poderosa escalabilidad que proporciona BEAM, para lograr un resultado similar, sería necesario utilizar sistemas distribuidos complejos, como Apache Kafka, RabbitMQ, etc.

Distribución

Las aplicaciones “globales” en tiempo real como WhatsApp y Discord requieren una arquitectura distribuida para dar cabida a usuarios en diferentes regiones. Estamos hablando de un sistema que puede distribuir la carga de trabajo entre muchos nodos y garantizar una comunicación perfecta entre ellos. Planificador de haz no sólo puede distribuir procesos a través de múltiples núcleos en una máquina, sino también para muchos autos en línea. Además, trata todos los procesos por igual, independientemente de si se ejecutan en la misma máquina que BEAM o en otro lugar. Por eso resulta tan fácil construir sistemas distribuidos, especialmente sin preocuparse por la infraestructura en la que se implementan.

Cuando un sistema se distribuye tan fácilmente, también escala bien. Si necesita ampliar su aplicación, puede agregar fácilmente nuevos nodos al clúster y distribuir la carga de trabajo entre ellos. No hay necesidad de preocuparse por programadores de cargas de trabajo complejos o estrategias de fragmentación: BEAM lo hace todo por usted.

Sensibilidad

Si la plataforma está diseñada para comunicación en tiempo real, entonces entregar mensajes a los usuarios y aplicar actualizaciones debe funcionar de manera rápida y confiable. En Erlang, el problema de ejecutar múltiples procesos se resuelve a nivel del lenguaje. Para ello se utilizan programadores dedicados, que alternativamente se hacen cargo de los procesos de Erlang según sea necesario. El planificador trabaja en desplazando modo: le da a cada proceso una pequeña ventana para ejecutar, luego pausa ese proceso y pasa a trabajar en otro.

Dado que esta ventana de ejecución es pequeña, no el proceso de ejecución prolongada no puede bloquearse el resto del sistema. Además, a nivel intrasistema, las operaciones de E/S se delegan a subprocesos individuales. Se puede utilizar un servicio de sondeo si se proporciona en el núcleo del sistema operativo subyacente. De esta manera, cualquier proceso que espere a que se complete una operación de E/S no bloqueará la ejecución de otros procesos.

Actualización de código en un sistema en ejecución

Cuando necesita admitir aplicaciones en tiempo real, debe poder implementar actualizaciones sin interrumpir la actividad del usuario. Normalmente, en entornos de nube en contenedores, se utilizan opciones de implementación azul-verde, versiones canary, etc. para resolver dichos problemas. Pero con BEAM, puedes actualizar el código directamente en el sistema en vivo sin reiniciar la aplicación. Esta oportunidad se llama “intercambio de código activo“, es fundamental cuando se trata de aplicaciones en tiempo real que requieren un funcionamiento ininterrumpido. Gracias a este enfoque, el desarrollador puede corregir errores, agregar funciones u optimizar el rendimiento sin interrumpir el servicio.

Estudio de caso n.º 1: Cómo WhatsApp utiliza Erlang para escalar la mensajería en tiempo real, dado que la aplicación atiende a más de mil millones de usuarios activos diarios

En 2009, Brian Acton y Jan Koum fundaron WhatsApp, concebida como una aplicación de mensajería para multiplataforma, rápido, confiable y seguro trabajar en tiempo real. Eligieron Erlang como lenguaje base debido a su tolerancia a fallas, escalabilidad y capacidades de trabajo distribuido. La arquitectura y el código base de los sistemas de mensajería de WhatsApp están inspirados en ejabberd, un proyecto de código abierto, un servidor XMPP escrito en Erlang.

La aplicación WhatsApp se desarrolló rápidamente en 2018; mil millones de usuarios diariamente. Gracias a la eficiencia de Erlang y al cuidadoso refinamiento tanto de ejabberd como de XMPP, los autores pudieron procesar millones de mensajes diariamente casi sin tiempo de inactividad del sistema, mientras que al mismo tiempo se las arreglaban con un equipo pequeño y bajos costos de hardware e ingeniería.

Según Anton Lavrik, ingeniero del servidor de WhatsApp, Una de las mayores fortalezas de Erlang es su enfoque de la concurrencia.. Según él, “se puede intentar alcanzar un nivel similar de competencia en otras plataformas utilizando otros lenguajes, pero esto a menudo requiere demasiado esfuerzo y recursos”. Dado que Erlang se creó originalmente con miras a resolver problemas relacionados con la concurrencia, hace que sea mucho más fácil llevar la concurrencia a un alto nivel”.

Estudio de caso n.° 2: Cómo Discord usó Elixir y escribió una aplicación de chat para que funcionara en tiempo real, y luego pudo escalarla para atender a 11 millones de usuarios simultáneos.

Discord fue fundada en 2015 por Jason Citron y Stan Wisniewski. Se trata de una sala de chat para correspondencia en tiempo real, inicialmente dirigida a jugadores. Luego, los autores decidieron construir una infraestructura basada en dos lenguajes clave: Python y Elixir. Hasta ahora, se utiliza una base de código Python integrada con Elixir para controlar los elementos de la API, pero la mayoría de los aspectos clave, en particular el chat en tiempo real, están vinculados a Elixir.

Los servidores populares de Discord, como los que sirven a los juegos Fortnite y Minecraft, pueden admitir cientos de miles de usuarios simultáneos. Por lo tanto, deberían manejarse muy bien (sin demoras) transmisión transmitiendo mensajes y actualizaciones del estado de los usuarios en tiempo real. Elixir tiene un modelo de concurrencia y mecanismos de tolerancia a fallos tan convenientes que se consideró el lenguaje ideal para crear un sistema de chat en tiempo real en Discord. Gracias a la capacidad de Discord para manejar una competencia tan masiva, así como para distribuir cargas en múltiples nodos, la plataforma de chat de Discord escala bien manteniendo un alto rendimiento y confiabilidad.

Luego, en 2019, Discord alcanzó un nuevo hito: necesitaba admitir más usuarios simultáneos que nunca. En ese momento, la plataforma ya no solo contaba con BEAM, sino también con las capacidades de nuevos lenguajes de “alto rendimiento”. Entonces, se escribió una nueva lista de participantes en Rust, en la que puede actualizar muy rápidamente los estados de hasta 11 millones de usuarios simultáneos. Además, lo más interesante es que logramos adaptar un módulo Rust precompilado para que funcione con el código base de Elixir, esto se hizo usando una característica llamada NIF (Función implementada de forma nativa). Este enfoque nos permite confiar en Rendimiento de óxido sin sacrificar la productividad de Elixir.

Análisis del caso #3: (del autor) Cómo escribí una aplicación de red para que funcione en tiempo real usando Elixir y Phoenix

El año pasado tuve la oportunidad de desarrollar un interesante proyecto “FoodFocus”: en esta aplicación, una transmisión transmite cómo un usuario prepara un plato, y otros usuarios pueden conectarse a esta transmisión y comunicarse directamente con el chef. Además, la aplicación podría conectarse a través de un reloj inteligente, en cuyo caso las recetas podrían sincronizarse con el temporizador del reloj.

Al comenzar a desarrollar este proyecto, seleccioné el stack tecnológico más económico y eficiente para el mismo. Estas fueron las características más importantes de esta pila:

– Funciones fáciles de desarrollar para trabajar en tiempo real

– Proporcionar herramientas listas para usar para que no sea necesario importarlas desde numerosas bibliotecas de terceros.

– Debe ser agradable trabajar con la aplicación; me importa la comodidad del desarrollador

Después de investigar un poco el problema, decidí escribir la parte del servidor en Elixir y Fénix, y para el lado del cliente use Flutter. Aunque este fue mi primer intento de escribir una aplicación “lista para la venta” en Elixir, me sorprendió lo fácil que era crear funciones en tiempo real usando Phoenix.

Enumeraré algunas de mis funciones y herramientas favoritas que me resultaron muy útiles al desarrollar un proyecto en Elixir:

  • Canal Fénix: gran desarrollo. Están construidos sobre WebSockets y proporcionan una API simple y fácil de usar para crear aplicaciones en tiempo real.

  • ETS (Tienda de términos de Erlang): ETS es un almacén clave-valor integrado de Erlang que es muy rápido y eficiente. No es necesario recurrir a herramientas similares a Redis para almacenar en caché o almacenar datos temporales.

  • Herramientas respaldadas por Postgres: El ecosistema de Elixir tiene muchas herramientas respaldadas por una base de datos de Postgres. ecto destinado a operaciones de bases de datos, Oban – para realizar tareas en segundo plano, y ElectricSQL – ejecutar solicitudes principalmente en el dispositivo, y no en la nube (primero local) – para mejorar la UX.

Gracias a todas estas oportunidades, pude entregar el proyecto a tiempo y cumplir
al presupuesto. Si hubiera elegido una pila de tecnología diferente, podría
Tomaría más tiempo configurar bibliotecas de terceros y sus
integración, así como centrarse rápidamente en el desarrollo de funciones específicas
aplicaciones.

Conclusión

Los ejemplos de WhatsApp y Discord muestran claramente cómo utilizando la máquina virtual Erlang y Elixir se pueden crear aplicaciones fácilmente escalables que funcionan en tiempo real. Pero no son sólo estas empresas las que han apreciado el poder de las tecnologías aquí descritas. Otros jugadores notables – Spotify, Pinterest, Pepsico, Financial Times y Heroku – también adoptaron lenguajes similares a Erlang y con su ayuda pudieron manejar una alta concurrencia, lograron tolerancia a fallas y escalabilidad en sus áreas temáticas.

En conclusión, si está creando una nueva plataforma de mensajería, un servicio de juegos en tiempo real o cualquier otra aplicación que necesite una conectividad sólida, le recomiendo que considere Erlang VM y Elixir como su salvavidas. Las capacidades únicas de estos lenguajes lo ayudan a construir una base sólida para su aplicación y escalarla tanto como desee sin sacrificar el rendimiento o la confiabilidad.

Publicaciones Similares

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *