Si vous utilisez Logback comme composant de connexion dans votre application, la plupart d'entre eux configureront le fichier `Logback.xml`. De plus, dans l'environnement de production, vous pouvez modifier directement le niveau de journal dans le fichier logback.xml, et il peut prendre effet sans redémarrer l'application. Alors, comment cette fonction est-elle mise en œuvre?
Si vous utilisez Logback comme composant de connexion dans votre application, la plupart d'entre eux configureront le fichier logback.xml. De plus, dans l'environnement de production, vous pouvez modifier directement le niveau de journal dans le fichier logback.xml, et il peut prendre effet sans redémarrer l'application.
Alors, comment cette fonction est-elle mise en œuvre?
I. Description et analyse du problème
En réponse au problème ci-dessus, lancez d'abord un cas réel. Dans mon site Web personnel Z +, tous les gadgets sont ajoutés dynamiquement et cachés via des fichiers de configuration. Parce qu'il n'y a qu'un seul serveur, les fichiers de configuration sont simplifiés et placés directement dans un répertoire sur le serveur.
Maintenant, lorsque j'ai un problème, j'ai besoin de réaliser le changement lorsque le contenu de ce fichier change, l'application peut détecter la modification, recharger le contenu du fichier et mettre à jour le cache interne de l'application
L'une des façons les plus simples de penser est le sondage pour déterminer si le fichier a été modifié. S'il est modifié, il sera rechargé et rafraîchi. Par conséquent, les principaux problèmes qui doivent être préoccupés sont les suivants:
Ii Conception et mise en œuvre
Une fois le problème abstrait, la solution correspondante est plus claire
Alors une implémentation très simple est plus facile:
classe publique FileUpTest {privé Long Lasttime; @Test public void testFileUpDate () {file file = new File ("/ tmp / alarmConfig"); // Tout d'abord, le dernier horodatage modifié du fichier lasttime = file.lastModified (); // Tâche chronométrée, détermine si le fichier a changé chaque seconde, c'est-à-dire déterminer si LastModified modifie ScheduleDexeCutorService ScheduleDexeCuTorService = Executor.NewScheduledThreadPool (1); ScheduleDExECUTORService.ScheDuleAtFixeDrate (new Runnable () {@Override public void run () {if (File.lastModified ()> Lasttime) {System.out.println ("File Update! Time:" + File.lastModified ()); Lasttime = File.lastModified ();}}}}, 1, 1, 1, 1, TimeUnit.seconds); essayez {thread.sleep (1000 * 60); } catch (InterruptedException e) {e.printStackTrace (); }}}Ce qui précède est une implémentation très simple et très basique, qui peut essentiellement répondre à nos besoins. Alors, quel est le problème avec cette implémentation?
Que se passe-t-il si une exception se produit lors de l'exécution d'une tâche de synchronisation?
Apporter quelques modifications au code ci-dessus
classe publique FileUpTest {privé Long Lasttime; private void tt () {lance un nouveau nullpointerException (); } @Test public void testFileUpDate () {file file = new File ("/ tmp / alarmConfig"); Lasttime = file.lastModified (); ScheduleDExECUTORService ScheduleDeXeCuTorService = Exécutors.NewScheDuledThreadPool (1); ScheduleDExECUtorService.ScheDuleAtFixeDrate (new Runnable () {@Override public void run () {if (File.lastModified ()> Lasttime) {System.out.println ("File Update! Time:" + File.LastModified ()); LastTime = File.lastModified (); TTT (););); TimeUnit.seconds); essayez {thread.sleep (1000 * 60 * 10); } catch (InterruptedException e) {e.printStackTrace (); }}}Dans le test réel, j'ai constaté que le code ci-dessus n'a été déclenché que lorsque la première modification a été modifiée, mais la modification serait à nouveau inutile. Autrement dit, lorsqu'une exception a été lancée, la tâche de synchronisation ne serait plus exécutée. La raison principale de ce problème est que le SchedulededexEcutorService est
Consultez les instructions de commentaire du code source de ScheduleDexecutorService directement
Si une exécution de la tâche rencontre une exception, les exécutions ultérieures sont supprimées.Le autre, la tâche ne se terminera que par annulation ou résiliation de l'exécuteur testamentaire.
Par conséquent, lorsque vous utilisez cette posture, vous devez vous assurer que votre tâche ne lance pas des exceptions, sinon vous ne pourrez pas jouer plus tard
La solution correspondante est relativement simple, il suffit de l'attraper
Iii. Édition avancée
Le précédent est une version d'implémentation de base. Bien sûr, dans le cercle Java, il existe essentiellement de nombreux besoins communs qui peuvent être utilisés pour utiliser des outils open source correspondants. Bien sûr, cela ne fait pas exception, et ce devrait être la série Apache que tout le monde a plus d'attributs.
Tout d'abord, les dépendances maven
<Dependance> <GroupId> Commons-io </proupId> <ArtefactId> Commons-io </letefactive> <DERNÉRATION> 2.6 </DERNIFRATION> </DENDENCENCE>
Principalement, nous utilisons le fichier FilealterationObserver, FilealterationListener et FilealterationMonitor dans cet outil pour mettre en œuvre des scénarios d'exigences connexes. Bien sûr, il est très simple à utiliser, donc je ne sais pas comment l'expliquer. Regardez simplement le code copié à partir de l'un de mes projets open source Quick-Alarm.
Public Class PropertiesConfListEnerHelper {public static boolean registerConfchangeListener (fichier de fichier, fonction <fichier, map <string, alarmConfig>> func) {try {// intervalle de sondage 5 secondes intervalle de long = timeunit.seconds.toMillis (5); // Parce que l'écoute est effectuée dans les répertoires, le répertoire racine du fichier est directement obtenu ici file dir = file.getParentFile (); // Créer un observateur de fichiers pour filtrer FilealterationObserver Observer = new FilealterationObserver (dir, fileFilterUtils.and (fileFilterUtils.fileFileFilter (), fileFilterUtils.nameFileFilter (file.getName ())))); // Définir le fichier Changement Écouteur Observer.AddListener (New MyFileListener (FUNC)); FilealterationMonitor Monitor = nouveau FilealterationMonitor (intervalle, observateur); Monitor.start (); Retour Vrai; } catch (exception e) {log.Error ("Registre des propriétés Modifier l'erreur de l'écoute! E: {}", e); retourne false; }} classe finale statique MyFileListener étend FilealterationListeDaPtor {Fonction privée <fichier, map <string, alarmConfig >> func; public myFileListener (fonction <fichier, map <string, alarmConfig >> func) {this.func = func; } @Override public void onFileChange (fichier file) {map <string, alarmConfig> ans = func.Apply (fichier); // Si le chargement échoue, imprimez un journal log.warn ("PropertiesConfig modifié! Reload ANS: {}", ANS); }}}Pour la mise en œuvre ci-dessus, quelques brèves explications:
Une question, que se passe-t-il si une exception est lancée lorsque la méthode FUNC est exécutée?
Les résultats réels des tests sont les mêmes que ci-dessus. Après avoir lancé une exception, vous devez toujours être prudent et ne pas exécuter l'exception.
Voyons donc la logique d'implémentation ci-dessus et déduisons directement le module de base.
public void run () {while (true) {if (this.running) {iterator var1 = this.observers.iterator (); while (var1.hasnext ()) {filealterationObserver Observer = (filealterationObserver) var1.next (); Observer.CheckAndNotify (); } if (this.running) {try {thread.sleep (this.interval); } catch (interruptedException var3) {; } continuer; } } retour; }}Il est fondamentalement clair à partir de ce qui précède que la logique de mise en œuvre entière est différente de notre première méthode de tâche chronométrée. Ici, nous utilisons directement des threads et des boucles mortes, et les méthodes de sommeil sont utilisées pour s'arrêter en interne. Par conséquent, lorsqu'une exception se produit, elle équivaut à la lancer directement et le fil s'agenouillera.
Version JDK supplémentaire
JDK1.7 fournit un WatchService, qui peut également être utilisé pour surveiller les modifications des fichiers. Je n'y ai pas été exposé auparavant, alors j'ai découvert qu'il y avait cette chose. Ensuite, j'ai recherché les informations liées à l'utilisation et j'ai constaté que c'était assez simple. J'ai vu un article de blog qui explique qu'il est axé sur les événements et a une efficacité plus élevée. Voici un exemple simple de démo
@TestPublic void testFileUpwather () lève ioException {// Instructions, l'auditeur ici doit également être le chemin de répertoire Path = paths.get ("/ tmp"); WatchService watcher = filesystems.getDefault (). NewWatchService (); path.register (watcher, entry_modify); Nouveau thread (() -> {try {while (true) {watchKey key = watcher.take (); for (watchEvent <?> event: key.pollevents ()) {if (event.kind () == overflow) {// event peut être perdu ou découragé continu;} path filename = (path) event.Context (); System.out.out.PrintLn ("file updated:" + filed); } if (! key.reset ()) {// Réinitialiser WatchKey Break;}}} catch (exception e) {e.printStackTrace (); essayez {thread.sleep (1000 * 60 * 10); } catch (InterruptedException e) {e.printStackTrace (); }}Iv. Résumé
L'utilisation de Java pour implémenter la surveillance des modifications de fichiers de configuration implique principalement deux points
Dans l'ensemble, cette implémentation est relativement simple. Qu'il s'agisse d'une implémentation personnalisée ou de s'appuyer sur Commos-io, il n'a pas beaucoup de coût technique, mais une chose à noter est:
Afin d'éviter la situation ci-dessus, une implémentation qui peut être effectuée consiste à utiliser la notification de message asynchrone de EventBus. Une fois le fichier modifié, envoyez un message, puis ajoutez une annotation @Subscribe à la méthode spécifique pour recharger le contenu du fichier. Cela réalise non seulement le découplage, mais évite également les exceptions de service causées par des exceptions (si vous êtes intéressé par cette implémentation, vous pouvez commenter et expliquer)
Ce qui précède est tout le contenu de cet article. J'espère que cela sera utile à l'apprentissage de tous et j'espère que tout le monde soutiendra davantage Wulin.com.