Nueva interfaz de Odnoklassniki: lanzamiento de React en Java. Parte 1



Muchos han escuchado el nombre GraalVM, pero hasta ahora no todos han tenido la oportunidad de probar esta tecnolog铆a en producci贸n. Para Odnoklassniki, esta tecnolog铆a ya se ha convertido en el front-end cambiante del "Santo Grial".

En este art铆culo quiero hablar sobre c贸mo logramos hacer amigos con Java y JavaScript, y comenzar a migrar a un gran sistema con mucho c贸digo heredado, y c贸mo GraalVM ayuda en el camino.

Al momento de escribir el art铆culo, result贸 que todo el volumen del material no se ajusta al tama帽o tradicional de la HABR y si publica la publicaci贸n completa, tomar谩 varias horas leerlo. Por lo tanto, decidimos dividir el art铆culo en 2 partes.

Desde la primera parte aprender谩 sobre la historia del front-end en Odnoklassniki y se familiarizar谩 con sus caracter铆sticas hist贸ricas, seguir谩 el camino para encontrar una soluci贸n a los problemas que se han acumulado en nosotros durante los 11 a帽os del proyecto, y al final se sumergir谩 en las caracter铆sticas t茅cnicas de la implementaci贸n del servidor de la decisi贸n que tomamos.

Antecedentes


La primera versi贸n de Odnoklassniki apareci贸 hace 13 a帽os, en 2006. El sitio se hizo en .NET, luego no hab铆a JavaScript en el sitio, todo estaba en la representaci贸n del servidor.



Un a帽o despu茅s, Odnoklassniki ten铆a m谩s de un mill贸n de usuarios. En 2007, estos fueron n煤meros incre铆bles, y el sitio, incapaz de soportar la carga, comenz贸 a caer. Los desarrolladores resolvieron el problema con la ayuda del proyecto One.lv, creado por la compa帽铆a letona Forticom, cuyas competencias centrales estaban en el desarrollo de Java. Por lo tanto, Odnoklassniki, se decidi贸 reescribir de .NET a Java.

Con el tiempo, el proyecto se desarroll贸 y surgi贸 la necesidad de nuevas soluciones para el cliente. Por ejemplo, cuando se navega por un sitio, no se actualiza por completo, sino solo ciertas partes. O para que, al enviar, solo se actualice el formulario y no toda la p谩gina. Al mismo tiempo, el sitio funcionaba solo en Java.

Para hacer esto, se les ocurri贸 un sistema en el que el sitio estaba marcado con bloques con nombre. Al navegar, se realiz贸 una solicitud al servidor, que tom贸 una decisi贸n sobre lo que deber铆a cambiarse usando este enlace, y el cliente recibi贸 las piezas necesarias. El motor del cliente simplemente reemplaz贸 las partes necesarias, por lo que se implement贸 la din谩mica del cliente. Muy conveniente, porque toda la l贸gica de negocios est谩 en el servidor. El peque帽o motor permiti贸 a la compa帽铆a seguir escribiendo c贸digo Java para administrar el cliente.

Por supuesto, sin JavaScript m铆nimo no era suficiente. Para hacer una ventana emergente, se necesitan manipulaciones: por ejemplo, al pasar el cursor sobre una pantalla div: el bloque se colg贸 o se ocult贸 con la pantalla: ninguno.

Pero al mismo tiempo, el contenido de la ventana emergente se solicit贸 al servidor, toda la l贸gica de negocios estaba all铆 y estaba en Java.



2018


Despu茅s de 12 a帽os, Odnoklassniki se convirti贸 en un servicio gigante con m谩s de 70 millones de usuarios. Tenemos m谩s de 7,000 m谩quinas en 4 centros de datos, y solo 600 mil solicitudes por segundo llegan al frente de OK.RU.

El servidor frontal de Odnoklassniki contin煤a funcionando en Java, y la base de c贸digo de los frentes solo supera los dos millones de l铆neas.



Las tecnolog铆as implementadas en el lado del cliente tampoco se detuvieron: aparecieron muchas soluciones usando diferentes bibliotecas: GWT, jQuery, DotJs, RequireJS y muchas otras.

En ese momento, est谩ndares como React, Angular y Vue no eran comunes, cada desarrollador trat贸 de encontrar la soluci贸n 贸ptima utilizando todas las herramientas disponibles.

Qued贸 claro que vivir con esto es muy dif铆cil, porque se ha acumulado una gran cantidad de problemas:

  • Muchas bibliotecas antiguas
  • No hay un marco 煤nico
  • Sin isomorfismo (ya que el backend est谩 en Java, el cliente est谩 en JS)
  • No hay una sola aplicaci贸n estructurada en el cliente
  • Mala capacidad de respuesta
  • Herramientas insuficientes
  • Umbral de entrada alto

El mundo ya estaba en 2018 y era necesario cambiar.

Usando todo el poder del pensamiento t茅cnico, pensamos y formulamos cuatro requisitos b谩sicos para resolver problemas:

  1. Los compa帽eros de clase deben tener un c贸digo isomorfo para la interfaz de usuario. Debido a que es imposible escribir constantemente el servidor en Java, y luego, si necesita agregar alg煤n tipo de din谩mica, juegue lo mismo en el cliente.
  2. Se necesita una transici贸n suave. Porque es imposible hacer r谩pidamente la segunda versi贸n de Odnoklassniki y cambiar
  3. Necesariamente se necesita la representaci贸n del servidor (m谩s sobre eso a continuaci贸n)
  4. La nueva soluci贸n, que funciona con la misma cantidad de hierro, no deber铆a afectar el rendimiento y la tolerancia a fallas bajo nuestras cargas.

驴Por qu茅 la representaci贸n del lado del servidor?


Odnoklassniki tiene muchos usuarios que viven lejos de Mosc煤 y no siempre tienen una buena conexi贸n a Internet.



La representaci贸n del servidor ayudar谩 a estos usuarios a obtener contenido m谩s r谩pido. Mientras se cargan las im谩genes, podr谩n comenzar a leer algo:



Llevamos a cabo una serie de experimentos, tratando de entender qu茅 suceder铆a si algunos datos (por ejemplo, cinta) ya fueran entregados al cliente, con una expectativa. Como resultado, result贸 que esto afect贸 negativamente la actividad del usuario.

C贸mo funciona el servidor ahora


El navegador realiza una solicitud al sitio OK y accede a la aplicaci贸n OK-WEB, que est谩 completamente escrita en Java. La aplicaci贸n sigue los datos en la API. Entre WEB y API, se implementa el transporte binario r谩pido one-nio desarrollado en Odnoklassniki. Las solicitudes se completan en menos de un milisegundo. Puedes ver qu茅 es por separado . One-nio le permite hacer muchas consultas a bajo costo sin preocuparse por demoras.

La API extrae los datos y los entrega a la web. La web genera p谩ginas HTML con un motor Java y se lo entrega al navegador.

Todo esto lleva ahora menos de 200 ms.



Busca una soluci贸n


Al principio, se desarroll贸 el concepto de migraci贸n basado en widgets.

Las solicitudes ser谩n entregadas al sitio en peque帽os trozos. En el interior se escribir谩n en una nueva pila. Y para el resto del sitio ser谩 solo un elemento DOM con alg煤n tipo de comportamiento personalizado.



Esto ser谩 similar a la etiqueta <video>: un elemento DOM personalizado con atributos, m茅todos y eventos. Como resultado, la API DOM se encuentra fuera, mientras que la funcionalidad del widget se implementa dentro de la nueva pila.

驴Qu茅 pila elegir?


Ahora que era necesario implementar el concepto, comenzaron a clasificar las opciones.

Kotlin


El primer prototipo se hizo en Kotlin. La idea era la siguiente: para nuevos componentes, escribir l贸gica en Kotlin y describir el marcado de componentes en XML. Todo se puede ejecutar en el servidor en la JVM utilizando el motor de plantillas existente, y para el cliente se puede transponer en JavaScript.



Adem谩s de introducir un nuevo lenguaje con un umbral de entrada alto, Kotlin result贸 tener herramientas insuficientemente desarrolladas para trabajar con JavaScript, y mucho m谩s tendr铆a que desarrollarse de forma independiente.

Por lo tanto, desafortunadamente, este concepto tuvo que ser abandonado.

Node.js


Otra opci贸n es poner Node.js u otro tiempo de ejecuci贸n, por ejemplo, Dart. Pero que pasa?

Hay dos formas de usar Node.js.
La primera forma es delegar la representaci贸n del componente al servidor en Node.js que se ejecuta en el mismo servidor que la aplicaci贸n Java. Por lo tanto, guardamos la aplicaci贸n en Java y solo en el proceso de renderizar HTML hacemos una llamada al servicio que se ejecuta localmente en Node.js.

Sin embargo, hay varios problemas con este enfoque:

  1. Una llamada remota a Node.js implica serializar / deserializar la entrada. Estos datos pueden ser muy voluminosos, por ejemplo, en el caso de que un nuevo componente en JS sea un contenedor alrededor de un componente antiguo implementado en Java.
  2. Una llamada remota, incluso en una m谩quina local, est谩 lejos de ser gratuita y tambi茅n presenta un retraso adicional. Si hay docenas o cientos de tales componentes en la p谩gina, incluso los m谩s simples, aumentaremos significativamente la sobrecarga y el retraso en el procesamiento de la solicitud del usuario.
  3. Adem谩s, la operaci贸n de dicho sistema es significativamente complicada, ya que en lugar de un solo proceso necesitar铆amos tener un proceso en Java y varios procesos en Node.js. En consecuencia, todas las operaciones se vuelven mucho m谩s complicadas, por ejemplo: implementaci贸n, recopilaci贸n de indicadores operativos, an谩lisis de registros, monitoreo de errores, etc.

La segunda forma de usar Node.js es colocarlo frente al servidor web en Java y usarlo para el procesamiento posterior de HTML. En otras palabras, es un proxy que analiza HTML, encuentra componentes en JS, los dibuja y devuelve el HTML terminado al usuario. Una opci贸n interesante, parece ser universal y bastante funcional. Las desventajas de este enfoque son que requiere un cambio completo en toda la infraestructura, aumenta significativamente los costos generales y conlleva graves riesgos: cualquier solicitud debe pasar por Node.js, es decir, comenzaremos a depender completamente de ella. Parece una soluci贸n demasiado cara para resolver nuestro problema.





Resulta que Node.js no se puede usar por los siguientes motivos:

  • La serializaci贸n / deserializaci贸n es una carga de trabajo adicional y demoras
  • Node.js es otro componente en el enorme sistema distribuido de Odnoklassniki

Ya tenemos muchos especialistas que saben c贸mo "cocinar" Java, y ahora tendremos que contratar a un personal que operar谩 Node.js y crear谩 otra infraestructura adem谩s de la existente.

JavaScript en la JVM


Pero, 驴qu茅 pasa si intentas ejecutar JavaScript dentro de la JVM? Resulta que el c贸digo Java y JavaScript se ejecutar谩 en un proceso e interactuar谩 con un m铆nimo de sobrecarga.

Esto reemplazar谩 sin problemas las piezas de Java con JavaScript dentro de la WEB actual.
Los componentes JS recibir谩n datos de Java y generar谩n HTML. Podr谩n trabajar isom贸rficamente en el cliente y el servidor.

Pero, 驴c贸mo ejecutar JS en la JVM?
Puede usar el V8 como lo hace Cloudflare . Pero este es un c贸digo binario de terceros para Java. Por lo tanto, en la JVM no ser谩 posible detectar errores dentro del V8. Cualquier ca铆da de V8 destruir谩 todo el proceso. Como resultado, el uso de V8 aumentar谩 los riesgos operativos, y esto no deber铆a permitirse.

Hay varios tiempos de ejecuci贸n JS para la JVM: dos rinocerontes, Nashorn y Rhino (uno de Oracle, el otro de Mozilla) y GraalVM fresco.



Ventajas de JS Runtimes para JVM:

  • Todo funciona en la JVM, y tenemos mucha experiencia en esto.
  • Interacci贸n gratuita de Java y JavaScript
  • Tiempo de ejecuci贸n seguro
  • Compilador de Java en caso de GraalVM

Adem谩s, fue suficiente para comparar estos tiempos de ejecuci贸n en velocidad. Result贸 que GraalVM est谩 por delante de todos por un amplio margen:



驴Qu茅 es GraalVM?


GraalVM es un tiempo de ejecuci贸n de alto rendimiento que admite programas en diferentes idiomas. Tiene un marco para escribir compiladores de lenguaje para la JVM. Gracias a esto, se admite la ejecuci贸n de programas en Java, Kotlin, JS, Python y otros lenguajes dentro de la misma JVM.

Puede obtener m谩s informaci贸n sobre las caracter铆sticas de GraalVM en un informe de Oleg Shelaev , que trabaja en Oracle Labs, donde se est谩 desarrollando GraalVM. Recomendado para ver back-end y front-end.

GraalVM nos permite ejecutar JS para representar la interfaz de usuario en el servidor. Como biblioteca usamos React .

Las ventajas de tal paquete:

  • No se agregaron nuevos idiomas: todav铆a Java y JavaScript
  • Gran comunidad: todos saben Reaccionar
  • Umbral de entrada bajo
  • Busque f谩cilmente colegas en un equipo
  • La operaci贸n no es complicada.

Ejecutando React en GraalVM


Dentro de GraalVM, puede crear un Contexto, un contenedor aislado en el que el programa se ejecutar谩 en el idioma invitado. En nuestro caso, el idioma del invitado es JS:

Context context = Context.create("js"); //  global   Value js = context.getBindings("js"); 

Para interactuar con el contexto, se utiliza su objeto global:

 //    global js.putMember("serverProxy", serverProxy); //    global Value app = js.getMember("app"); 

Puede cargar el c贸digo del m贸dulo en el contexto:

 //     Value load = js.getMember("load"); //     load.execute(pathToModule); 

O "zap-eval-it" es cualquier c贸digo all铆:

 context.eval("js", someCode); 



Representaci贸n del servidor JS: concepto


Cree un contexto de JavaScript en la JVM y cargue el c贸digo del m贸dulo de aplicaci贸n React en 茅l. Lanzamos de Java a JS las funciones y m茅todos necesarios. Luego, desde este contexto, extraemos el enlace a la funci贸n JS render () de este m贸dulo, para que luego podamos llamarlo desde Java.



Cuando el usuario solicita la p谩gina, se inicia el motor de plantilla del servidor, llama a la funci贸n render () de los componentes necesarios con los datos necesarios, recibe el c贸digo HTML de ellos y se lo entrega junto con el HTML de toda la p谩gina al usuario.



Representaci贸n del servidor JS: implementaci贸n


En el motor de plantillas de servidor de Odnoklassniki, el dise帽o se escribe en forma de marcado HTML. Para distinguir las aplicaciones JS del marcado habitual, utilizamos etiquetas personalizadas.
Cuando el motor de plantillas encuentra una etiqueta personalizada, se crea una tarea para representar el m贸dulo correspondiente. Se env铆a al grupo de subprocesos, cada uno de los cuales tiene su propio contexto JS, se ejecuta en un subproceso libre, representa el componente en 茅l y se lo entrega al cliente.



驴Por qu茅 necesito un grupo de contexto?


El componente se representa sincr贸nicamente en un hilo. En este momento, el contexto de representaci贸n JS est谩 ocupado. Por lo tanto, al crear varios contextos independientes, puede paralelizar la representaci贸n de componentes utilizando capacidades de subprocesamiento m煤ltiple de Java.



Las funciones de adquisici贸n de datos Java se pasan por referencia a cada contexto. El resultado es un excelente JavaScript multiproceso en un solo proceso.

En el pr贸ximo art铆culo describiremos c贸mo la implementaci贸n de la parte del cliente de la nueva interfaz se basa en este concepto.

Para continuar

Source: https://habr.com/ru/post/480808/


All Articles