Patch de code Java sur la production sans anesthésie

image

Ici, je vais parler du dispositif de l'un des nombreux outils qui aident au développement de divers services pour le projet Odnoklassniki. Au sein de l'entreprise, nous l'appelons «Hot Code Replace» (HCR), et cet outil est conçu pour corriger les bogues critiques et simples dans l'exécution des services de production sans les arrêter. C'est une fonctionnalité extrêmement importante, car elle vous permet d'éviter le processus plutôt ennuyeux et long de disposer d'une nouvelle version corrigée du service indésirable, d'éviter la pause suffisamment longue de l'accompagnateur dans la disponibilité de chaque hôte et d'éviter de vider les caches.

En général, cela fait gagner beaucoup de temps et réduit l'intervalle entre le moment où l'erreur est détectée et la correction d'heures en minutes. Le plus souvent, comme prévu, des erreurs mineures dans le code sont corrigées, par exemple, le programmeur a oublié de vérifier la nullité et pour certains utilisateurs certaines actions sur le site entraînent une erreur. C'est-à-dire lorsque la correction est effectuée en changeant plusieurs lignes dans la méthode. Et pour des changements aussi mineurs, vous n'avez plus besoin de distraire vos collègues et d'attendre des heures pour la production.

Par exemple:

image
Vous pouvez facilement le réparer sur:

image
Bien sûr, vous pouvez apporter beaucoup plus de modifications, en même temps ajouter de nouvelles classes, apporter rapidement des modifications que le gestionnaire demande en même temps, sans attendre la prochaine mise à jour. Mais c'est déjà le cas, si Monsieur en sait beaucoup sur les perversions.

De plus, il est possible de mettre des "patchs" les uns sur les autres et à l'infini.

Mais cet outil n'est pas omnipotent et est basé sur la fonctionnalité standard offerte par la classe Java: java.lang.instrument.Instrumentation et sa méthode void redefineClasses (ClassDefinition ... définitions) .

Instrumentation.redefineClasses remplace les classes précédemment chargées par un nouveau code d'octet. Vous pouvez surcharger plusieurs classes avec différentes dépendances en même temps. La surcharge ne modifie pas les instances de classe existantes, ne modifie pas l'héritage et ne touche pas les champs d'instance ou de classe. Vous ne pouvez modifier que le corps de la méthode, le pool de constantes et d'attributs. Vous pouvez ajouter de nouvelles classes ou sous-classes. Les signatures de méthode, les champs d'instance et les champs de classe ne peuvent pas être modifiés. Si vous essayez d'apporter des modifications incompatibles, redefineClasses ne fonctionnera en principe pas et générera une erreur. Il faut se rappeler que lorsque les classes sont surchargées, l'exécution de la section de code surchargée n'est pas interrompue, le nouveau bytecode sera utilisé la prochaine fois que la même méthode sera appelée. Et par conséquent, si vous essayez de corriger le code d'une méthode qui contient un cycle infiniment long, le remplacement réel n'aura lieu qu'après la fin de ce cycle.

Si tout simplement: vous ne pouvez changer le code qu'à l'intérieur des méthodes et du point.

Et voici un exemple de boucle while, qui jusqu'à la fin de la méthode, ne sera pas corrigée.

image

La principale difficulté a été de créer un outil qui fonctionne dans l'écosystème Odnoklassniki, un outil qui s'intègre dans tous les processus de travail établis. Qui interagira de manière cohérente et transparente avec tous les services sur des centaines d'hôtes, tout en étant flexible et facile à utiliser. Cet outil devrait faire face à des dizaines d'expériences, de travaux et de mises à jour qui se produisent en continu sur la production.

À quoi ressemble le processus d'installation d'un correctif du point de vue développeur / administrateur essayant de corriger un bogue en production, mais pour qu'il puisse être fait en utilisant une procédure standard et fiable sur des dizaines de serveurs. Nous omettons le processus de recherche et de correction des erreurs dans le code.

1. Un brunch séparé est créé dans GIT pour les corrections de code. L'utilisation de la gestion des versions est très importante non seulement pour des raisons de commodité, mais aussi pour d'éventuelles investigations ultérieures.

2. TeamCity lance le processus de construction du patch. Tout d'abord, un assemblage de projet est créé à partir du brunch spécifié, puis le nouvel assemblage est comparé à celui installé sur la production. Pour ce faire, j'ai écrit un plug-in pour l'outil de construction, qui extrait tous les fichiers des archives, compare les écarts et sélectionne uniquement les fichiers qui ont été modifiés ou ajoutés. Dans ce cas, la version du compilateur Java dans les deux assemblys doit être la même, car une autre version du compilateur créera des fichiers différents et presque tous les fichiers de projet seront inclus dans le patch. Il est très important de créer juste une petite archive, dans laquelle seuls les fichiers nécessaires seront récupérés, car Cela accélérera considérablement le processus de livraison du correctif à des dizaines de serveurs. Le processus de génération convient non seulement au correctif du code du projet, mais vous pouvez également remplacer la bibliothèque corrigée dans le projet. Lors de la comparaison du contenu de deux assemblys, des différences seront trouvées dans les bibliothèques (fichiers jar).

3. En cas d'assemblage réussi, le correctif est envoyé vers un référentiel spécial et dans la fenêtre de résultat, une clé (ou un hachage) est émise, ce qui est nécessaire pour identifier le correctif de manière unique et garantir que ce code parviendra à la production.

image

Eh bien, et encore - vous pouvez patcher un nombre illimité de fois et les builds avec le même numéro de version différeront d'un hachage.

4. Ensuite, toutes les activités sont transférées vers le service de configuration, où dans l'interface utilisateur habituelle, vous pouvez spécifier pour quel service, sur quels hôtes et quelles versions d'applications vous devez patcher.

image

Une telle abondance de paramètres donne le niveau nécessaire de flexibilité des paramètres, ce qui est très important dans un grand zoo à partir de nombreux serveurs. Disons que sur une partie des serveurs, le numéro de version de l'application est différent, et vous n'avez pas du tout besoin de patcher ce code. Ou, pour vérification, Hot Code Rreplace est d'abord lancé sur un serveur ou sur un groupe de serveurs, puis distribué sur toutes les instances de l'application.

5. Grâce à une modification de configuration, les services sélectionnés reçoivent des informations sur ce que le correctif doit être installé, sa version et le hachage de vérification. L'idée est que tous les services reçoivent la commande «installer le patch» et agissent ensuite indépendamment. Ils comparent indépendamment leur propre version et uniquement si la version correspond et que le hachage du correctif est manquant ou différent, ils téléchargent indépendamment l'assembly du correctif à partir du référentiel. Le processus de téléchargement lui-même a lieu via HTTP et vous pouvez modifier rapidement l'adresse du référentiel, le nombre de tentatives de téléchargement et la période d'attente entre les tentatives.

6. Chaque application vérifie localement le hachage de l'assembly et le décompresse. Dans ce cas, chaque fichier est vérifié pour sa présence dans le tableau parmi ceux renvoyés par Instrumentation.getAllLoadedClasses (), toutes les nouvelles classes et tous les nouveaux fichiers sont écrits dans un nouveau - un chemin de classe temporaire, et ce chemin de classe est ajouté via Instrumentation.appendToSystemClassLoaderSearch (), et les classes existantes sont lues en mémoire et passer par la méthode redefineClasses.

7. L'ensemble du processus: l'arrivée d'un signal sur la nécessité de patcher l'application, son téléchargement, sa vérification, son déballage et son application sont enregistrés en détail, à la fois dans le journal général avec l'application et dans le sien, afin que vous puissiez rapidement et sans gestes inutiles surveiller le processus.

8. Une fois le correctif appliqué, le processus se termine en remplaçant la version de l'application par celle corrigée en ajoutant une ligne spécialement composée comprenant le hachage du correctif. Dans le cas où pour certains hôtes la version n'a pas changé pour la version attendue, nous allons dans le journal de remplacement de code à chaud pour cet hôte et voyons ce qui s'y est passé. S'il s'agissait de problèmes de communication, vous pouvez répéter la commande de patch en toute sécurité et l'hôte souhaité réessaiera.

Quels problèmes possibles peuvent empêcher l'application de patcher? Il y en a pas mal, et parmi eux la fonctionnalité de la classe Instrumentation que je mettrais en dernier lieu. Jusqu'à présent, le code tordu qui ne remplit pas les conditions strictes de redefineClasses a toujours été flashé par la JVM sans aucune conséquence pour l'application. Lors de l'application de la méthode redefineClasses, la JVM arrête complètement l'application, mais ce processus prend une fraction de seconde. Parce que ce n'est pas du tout effrayant.

Le moment le plus risqué est la livraison du correctif au serveur, qui a été décidée par des recyclages supplémentaires. Mais si les retrays ne vous aident pas, vous pouvez répéter la commande pour appeler le correctif et chacun des hôtes essaiera de répéter le processus, mais installez le correctif uniquement si nécessaire, c'est-à-dire le correctif n'a pas été installé précédemment ou si la clé de hachage a changé.

Un autre problème potentiel est lorsque le correctif corrige une erreur et en ajoute une nouvelle. Pour minimiser ce risque, nous téléchargeons d'abord le correctif sur un nombre limité de serveurs, examinons les journaux, les graphiques et surveillons le résultat. Et seulement alors, nous déployons des corrections vers d'autres hôtes.

Que faire avec le redémarrage d'une application ou d'un serveur? Ceci est déjà intégré dans la logique de toutes les applications de camarade de classe: l'un des premiers dans n'importe quelle application est le module HCR. Et si lors de l'initialisation des informations sur la nécessité de patcher l'application sont remarquées, le patch sera appliqué en premier.

Et maintenant, en quoi consiste le remplacement de code à chaud.

  1. Notre JavaAgent. JavaAgent, si quelqu'un l'a oublié , il s'agit d'une archive * .jar distincte et spécialement formée qui est récupérée par la JVM lorsque l'application commence à utiliser un paramètre supplémentaire, par exemple: -javaagent: /path/to/lib/my-agent.jar Grâce aux fonctionnalités supplémentaires de Javaagent- et il est possible d'utiliser la magie de remplacement de code. C'est dans l'agent que la classe java.lang.instrument.Instrumentation est disponible. Mais, je ne l'ai pas obstrué (l'agent) avec du code supplémentaire, car La mise à jour de l'agent est une tâche non triviale, mais a simplement déplacé l'instance de la classe Instrumentation dans le champ statique de la classe utilitaire. Ainsi, toutes les manipulations peuvent être lancées depuis n'importe où dans l'application.
  2. Service de configuration - est responsable de la configuration de l'une de nos applications et est donc initialisé dans chaque application en premier. C'est là que la fonctionnalité principale de Hot Code Replace est cachée. Au démarrage de l'application ou lors de la modification de la configuration HCR pour une application particulière, la compatibilité des versions est vérifiée et toutes les manipulations ci-dessus sont effectuées.
  3. TeamCity et créer des scripts - pour créer facilement des «correctifs» et n'y enregistrer que les classes et ressources modifiées ou ajoutées.

Quels sont les avantages que nous avons de cet outil? Le premier est la vitesse de correction des erreurs critiques dans le prod. D'après les journaux, je constate que les collègues ont progressivement commencé à utiliser HCR de plus en plus souvent, au lieu d'attendre les versions. Vient ensuite la vitesse d'application. Il n'est pas nécessaire d'arrêter l'application, la JVM ne se fige qu'une fraction de seconde et tous vos objets restent à leur place et continuent de fonctionner.

Et nos développeurs ont guéri librement et joyeusement et ont corrigé leurs erreurs immédiatement et indépendamment directement en production sans tenir compte du nombre de serveurs et de la charge.

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


All Articles