Parchear c贸digo Java en producci贸n sin anestesia

imagen

Aqu铆 hablar茅 sobre el dispositivo de una de las muchas herramientas que ayudan en el desarrollo de varios servicios para el proyecto Odnoklassniki. Dentro de la empresa, lo llamamos "Reemplazo de c贸digo activo" (HCR), y esta herramienta est谩 dise帽ada para corregir errores cr铆ticos y sin complicaciones al ejecutar servicios de producci贸n sin detenerlos. Esta es una caracter铆stica extremadamente importante, ya que le permite evitar el proceso bastante aburrido y lento de dise帽ar una nueva versi贸n corregida del servicio basura, para evitar la pausa suficiente de la operadora en la disponibilidad de cada host y evitar el vaciado de cach茅s.

En general, ahorra mucho tiempo y reduce el intervalo desde el momento en que se detecta el error hasta la correcci贸n de horas a minutos. En la mayor铆a de los casos, como se plane贸, se corrigen errores menores en el c贸digo, por ejemplo, el programador olvid贸 verificar si es nulo y para algunos usuarios ciertas acciones en el sitio conducen a un error. Es decir, cuando la correcci贸n se lleva a cabo cambiando varias l铆neas dentro del m茅todo. Y en aras de tales cambios menores, ya no necesita distraer a sus colegas y esperar horas disponibles para la producci贸n.

Por ejemplo:

imagen
Puedes arreglarlo f谩cilmente en:

imagen
Por supuesto, puede hacer muchos m谩s cambios, al mismo tiempo agregar nuevas clases, hacer r谩pidamente los cambios que el administrador solicita al mismo tiempo, sin esperar la pr贸xima actualizaci贸n. Pero esto ya es as铆, si Monsieur sabe mucho sobre perversiones.

Adem谩s, es posible poner "parches" entre s铆 y hasta el infinito.

Pero esta herramienta no es omnipotente y se basa en la funcionalidad est谩ndar que ofrece la clase Java: java.lang.instrument.Instrumentation y su m茅todo void redefineClasses (ClassDefinition ... definiciones) .

Instrumentation.redefineClasses reemplaza las clases cargadas previamente con un nuevo c贸digo de bytes. Puede sobrecargar varias clases con diferentes dependencias al mismo tiempo. La sobrecarga no cambia las instancias de clase existentes, no cambia la herencia y no toca los campos de instancia o clase. Solo puede cambiar el cuerpo del m茅todo, el conjunto de constantes y atributos. Puede agregar nuevas clases o subclases. Las firmas de m茅todos, los campos de instancia y los campos de clase no se pueden cambiar. Si intenta realizar cambios incompatibles, redefineClasses en principio no funcionar谩 y arrojar谩 un error. Debe recordarse que cuando las clases se sobrecargan, la ejecuci贸n de la secci贸n de c贸digo sobrecargada no se interrumpe, el nuevo bytecode se usar谩 la pr贸xima vez que se llame al mismo m茅todo. Y, por lo tanto, si intenta corregir el c贸digo de un m茅todo que tiene un ciclo infinitamente largo en su interior, el reemplazo real ocurrir谩 solo despu茅s de que termine este ciclo.

Si simplemente: puede cambiar el c贸digo solo dentro de los m茅todos y el punto.

Y aqu铆 hay un ejemplo de un ciclo while, que hasta que se complete el m茅todo, no se solucionar谩.

imagen

La principal dificultad fue hacer una herramienta que funcione en el ecosistema Odnoklassniki, una herramienta que se ajuste a todos los procesos de trabajo establecidos. Que interactuar谩 de manera consistente y transparente con todos los servicios en cientos de hosts, adem谩s de ser flexible y f谩cil de usar. Esta herramienta deber铆a hacer frente a docenas de experimentos, trabajos y actualizaciones que ocurren continuamente en la producci贸n.

驴C贸mo se ve el proceso de instalaci贸n de un parche desde el punto de vista del desarrollador / administrador que trata de corregir un error en la producci贸n, pero para que pueda hacerse usando alg煤n procedimiento est谩ndar y confiable en docenas de servidores? Omitimos el proceso de encontrar y corregir errores en el c贸digo.

1. Se crea un brunch por separado en GIT para las correcciones de c贸digo. El uso de versiones es muy importante no solo por conveniencia, sino tambi茅n para posibles investigaciones posteriores.

2. TeamCity lanza el proceso de compilaci贸n del parche. Primero, se crea un ensamblaje de proyecto a partir del brunch especificado, y luego se compara el nuevo ensamblaje con el instalado en la producci贸n. Para hacer esto, escrib铆 un complemento para la herramienta de compilaci贸n, que extrae todos los archivos de los archivos, compara las discrepancias y selecciona solo aquellos archivos que han cambiado o agregado. En este caso, la versi贸n del compilador de Java en ambos ensamblados deber铆a ser la misma, porque otra versi贸n del compilador crear谩 diferentes archivos y casi todos los archivos del proyecto se incluir谩n en el parche. Es muy importante crear solo un peque帽o archivo, en el que solo se obtendr谩n los archivos necesarios, porque Esto acelerar谩 significativamente el proceso de entrega del parche a docenas de servidores. El proceso de compilaci贸n es adecuado no solo para el parche del c贸digo del proyecto, sino que tambi茅n puede reemplazar la biblioteca parcheada en el proyecto. Al comparar el contenido de dos ensamblajes, se encontrar谩n diferencias en las bibliotecas (archivos jar).

3. En caso de un ensamblaje exitoso, el parche se env铆a a un repositorio especial, y en la ventana de resultados se emite una clave (o hash), que es necesaria para identificar de forma 煤nica el parche y garantizar que este c贸digo llegue a la producci贸n.

imagen

Bueno, y de nuevo: puede parchear un n煤mero ilimitado de veces y las compilaciones con el mismo n煤mero de versi贸n diferir谩n en un hash.

4. Luego, toda la actividad se transfiere al servicio de configuraci贸n, donde en la IU habitual puede especificar para qu茅 servicio, en qu茅 hosts y qu茅 versiones de aplicaciones necesita parchear.

imagen

Tal abundancia de par谩metros proporciona el nivel necesario de flexibilidad de configuraci贸n, que es muy importante en un gran zool贸gico desde muchos servidores. Digamos que en alguna parte de los servidores el n煤mero de versi贸n de la aplicaci贸n es diferente y no necesita parchear este c贸digo en absoluto. O, para la verificaci贸n, Hot Code Rreplace se inicia primero en un servidor o en un grupo de servidores, y luego se distribuye en todas las instancias de la aplicaci贸n.

5. A trav茅s de un cambio de configuraci贸n, los servicios seleccionados reciben informaci贸n sobre lo que necesita instalar el parche, su versi贸n y el hash de verificaci贸n. La idea es que todos los servicios reciban el comando "instalar el parche" y luego actuar de forma independiente. Comparan independientemente su propia versi贸n y solo si la versi贸n coincide y falta el hash del parche o es diferente, descargan independientemente el ensamblaje del parche desde el repositorio. El proceso de descarga en s铆 se realiza a trav茅s de HTTP, y puede cambiar r谩pidamente la direcci贸n del repositorio, el n煤mero de intentos de descarga y el per铆odo de espera entre reintentos.

6. Cada aplicaci贸n comprueba localmente el hash del ensamblado y lo desempaqueta. En este caso, se comprueba la presencia de cada archivo en la matriz entre los devueltos por Instrumentation.getAllLoadedClasses (), todas las clases y archivos nuevos se escriben en uno nuevo: una ruta de clase temporal, y esta ruta de clase se agrega a trav茅s de Instrumentation.appendToSystemClassLoaderSearch (), y las clases existentes se leen en la memoria y pasar por el m茅todo redefineClasses.

7. Todo el proceso: la llegada de una se帽al sobre la necesidad de parchear la aplicaci贸n, su descarga, verificaci贸n, desempaquetado y aplicaci贸n se registra en detalle, tanto en el registro general con la aplicaci贸n como en el propio, para que pueda monitorear el proceso r谩pidamente y sin gestos innecesarios.

8. Despu茅s de que el parche se aplica con 茅xito, el proceso finaliza cambiando la versi贸n de la aplicaci贸n a la parcheada agregando una l铆nea especialmente compuesta que incluye el hash del parche. En el caso de que para alg煤n host la versi贸n no haya cambiado a la esperada, vamos al registro de Reemplazo de c贸digo activo para ese host y veremos qu茅 sucedi贸 all铆. Si se tratara de problemas de comunicaci贸n, puede repetir con seguridad el comando de revisi贸n y el host deseado lo intentar谩 nuevamente.

驴Qu茅 posibles problemas pueden evitar que la aplicaci贸n parchee? Hay bastantes de ellos, y entre ellos la funcionalidad de la clase Instrumentation que pondr铆a en 煤ltimo lugar. Hasta ahora, el c贸digo torcido que no cumple con las estrictas condiciones de redefineClasses siempre ha sido actualizado por la JVM sin ninguna consecuencia para la aplicaci贸n. Al aplicar el m茅todo redefineClasses, la JVM detiene completamente la aplicaci贸n, pero este proceso lleva una fracci贸n de segundo. Porque no da nada de miedo.

El momento m谩s arriesgado es la entrega del parche al servidor, que se decidi贸 mediante reentrenamientos adicionales. Pero si las retransmisiones no ayudan, puede repetir el comando para llamar al parche y cada uno de los hosts intentar谩 repetir el proceso, pero instale el parche solo si es necesario, es decir. el parche no se ha instalado previamente o si la clave hash ha cambiado.

Otro problema potencial es cuando la soluci贸n corrige un error y agrega uno nuevo. Para minimizar este riesgo, primero cargamos el parche en un n煤mero limitado de servidores, miramos los registros, gr谩ficos y monitoreamos el resultado. Y solo entonces implementamos correcciones a otros hosts.

驴Qu茅 hacer al reiniciar una aplicaci贸n o servidor? Esto ya est谩 incrustado en la l贸gica de todas las aplicaciones classmate: una de las primeras en cualquier aplicaci贸n es el m贸dulo HCR. Y si durante la inicializaci贸n se nota informaci贸n sobre la necesidad de parchear la aplicaci贸n, primero se aplicar谩 el parche.

Y ahora un poco sobre en qu茅 consiste Hot Code Replace.

  1. Nuestro JavaAgent. JavaAgent, si alguien lo ha olvidado , este es un archivo * .jar separado, especialmente formado, que JVM recoge cuando la aplicaci贸n comienza a usar un par谩metro adicional, por ejemplo: -javaagent: /path/to/lib/my-agent.jar Gracias a las caracter铆sticas adicionales de Javaagent- y es posible usar magia de reemplazo de c贸digo. Es en el agente donde est谩 disponible la clase java.lang.instrument.Instrumentation. Pero, no lo obstru铆 (el agente) con c贸digo extra, porque La actualizaci贸n del agente es una tarea no trivial, pero simplemente movi贸 la instancia de la clase Instrumentation al campo est谩tico de la clase de utilidad. Por lo tanto, todas las manipulaciones pueden iniciarse desde cualquier lugar de la aplicaci贸n.
  2. Servicio de configuraci贸n: es responsable de la configuraci贸n de cualquiera de nuestras aplicaciones y, por lo tanto, se inicializa primero en cada aplicaci贸n. Es all铆 donde se oculta la funcionalidad principal de Hot Code Replace. Al iniciar la aplicaci贸n o al cambiar la configuraci贸n de HCR para una aplicaci贸n en particular, se verifica la compatibilidad de la versi贸n y se realizan todas las manipulaciones anteriores.
  3. TeamCity y scripts de creaci贸n: para crear convenientemente "parches" y guardar solo clases y recursos modificados o agregados en ellos.

驴Cu谩les son las ventajas que tenemos de esta herramienta? El primero es la velocidad de corregir errores cr铆ticos en el producto. Desde los registros, veo que los colegas gradualmente comenzaron a usar HCR cada vez m谩s a menudo, en lugar de esperar a que se publicaran. Lo siguiente es la velocidad de aplicaci贸n. No es necesario detener la aplicaci贸n, la JVM solo se congela por una fracci贸n de segundo y todos sus objetos permanecen en sus lugares y contin煤an funcionando.

Y nuestros desarrolladores se curaron libre y felizmente y corrigieron sus errores de forma inmediata e independiente directamente en producci贸n sin tener en cuenta la cantidad de servidores y la carga.

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


All Articles