Si usa logback como componente de sesión de sesión en su aplicación, la mayoría de ellos configurará el archivo `logback.xml`. Además, en el entorno de producción, puede modificar directamente el nivel de registro en el archivo logback.xml, y puede entrar en vigencia sin reiniciar la aplicación. Entonces, ¿cómo se implementa esta función?
Si usa logback como un componente de sesión de sesión en su aplicación, la mayoría de ellos configurará el archivo logback.xml. Además, en el entorno de producción, puede modificar directamente el nivel de registro en el archivo logback.xml, y puede entrar en vigencia sin reiniciar la aplicación.
Entonces, ¿cómo se implementa esta función?
I. Descripción y análisis del problema
En respuesta al problema anterior, primero lanza un caso real. En mi sitio web personal Z+, todos los gadgets se agregan dinámicamente y ocultan a través de archivos de configuración. Debido a que solo hay un servidor, los archivos de configuración se simplifican y se colocan directamente en un directorio en el servidor.
Ahora, cuando tengo un problema, necesito darme cuenta del cambio cuando cambia el contenido de este archivo, la aplicación puede detectar el cambio, recargar el contenido del archivo y actualizar el caché interno de la aplicación
Una de las formas más fáciles de pensar es en las encuestas para determinar si el archivo ha sido modificado. Si se modifica, se recargará y se actualizará. Por lo tanto, los principales problemas de los que deben preocuparse son los siguientes:
II. Diseño e implementación
Después de abstraer el problema, la solución correspondiente es más clara
Entonces una implementación muy simple es más fácil:
clase pública FileUptest {Private Long LastTime; @Test public void testFileUpdate () {archivo file = nuevo archivo ("/tmp/alarmConfig"); // Primero, la última marca de tiempo modificada del archivo LastTime = file.lastModified (); // Tarea cronometrada, determina si el archivo ha cambiado cada segundo, es decir, determina si los cambios modificados ProgredEDExecutorService ProchuledExeCutorService = Ejecutors.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(); } } } },0, 1, TimeUnit.seconds); intente {Thread.sleep (1000 * 60); } catch (InterruptedException e) {E.PrintStackTrace (); }}}Lo anterior es una implementación muy simple y muy básica, que básicamente puede satisfacer nuestras necesidades. Entonces, ¿cuál es el problema con esta implementación?
¿Qué sucede si se produce una excepción durante la ejecución de una tarea de tiempo?
Hacer algunas modificaciones al código anterior
clase pública FileUptest {Private Long LastTime; private void tt () {tire new nullPointerException (); } @Test public void testFileUpdate () {archivo file = nuevo archivo ("/tmp/alarmConfig"); lasttime = file.lastModified (); ProgramedExecutorService ProchuleDExecutorService = Ejecutors.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(); } } } }, 0, 1, TimeUnit.seconds); intente {thread.sleep (1000 * 60 * 10); } catch (InterruptedException e) {E.PrintStackTrace (); }}}En la prueba real, descubrí que el código anterior se activó solo cuando se modificó la primera modificación, pero la modificación nuevamente sería inútil. Es decir, cuando se lanzó una excepción, la tarea de tiempo ya no se ejecutaría. La razón principal de este problema es que el ProgramedExecutorService es
Consulte las instrucciones de comentarios del código fuente de ProchuledExecutorService directamente
Si alguna ejecución de la tarea encuentra una excepción, se suprimen las ejecuciones posteriores. De lo contrario, la tarea solo terminará mediante cancelación o terminación del ejecutor.
Por lo tanto, al usar esta postura, debe asegurarse de que su tarea no arroje excepciones, de lo contrario no podrá jugar más tarde
La solución correspondiente es relativamente simple, solo páblela
Iii. Edición avanzada
El anterior es una versión de implementación básica. Por supuesto, en el círculo Java, hay básicamente muchas necesidades comunes que se puede encontrar para utilizar las herramientas de código abierto correspondientes. Por supuesto, esta no es una excepción, y debería ser la serie Apache que todos tienen más atributos.
En primer lugar, dependencias de Maven
<Spendency> <MoupRoMID> Commons-io </proupId> <artifactId> commons-io </artifactid> <versión> 2.6 </versión> </pendency>
Principalmente, utilizamos FilealterationObServer, FilealterationListener y FilealterationMonitor en esta herramienta para implementar escenarios de requisitos relacionados. Por supuesto, es muy simple de usar, por lo que no sé cómo explicarlo. Solo mire el código copiado de uno de mi proyecto de código abierto de código rápido.
Public Class PropertiesConflistenerHelper {public static Boolean RegisterConfChangeListener (archivo de archivo, function <archivo, map <string, alarmConfig >> func) {try {// intervalo de votación de 5 segundos de largo intervalo = timion.seconds.tomillis (5); // Debido a que la escucha se realiza en directorios, el directorio raíz del archivo se obtiene directamente aquí file dir = file.getParentFile (); // Cree un observador de archivo para filtrar FileAlterationObServer Observer = newAlealterationObServer (dir, fileFilterUtils.and (fileFilterUtils.fileFileFilter (), fileFilterUtils.nameFileFilter (file.getName ())); // Establecer el cambio de archivo Listener Observer.addListener (nuevo myFileListener (func)); FilealterationMonitor Monitor = nuevo FilealterationMonitor (intervalo, observador); monitor.Start (); devolver verdadero; } catch (excepción e) {log.error ("Registro de propiedades Cambiar el error del oyente! E: {}", e); devolver falso; }} Clase final estática myFileListener extiende FilealterationListenerAdaptor {Función privada <archivo, map <string, alarmConfig >> func; public myFileListener (function <archivo, map <string, alarmConfig >> func) {this.func = func; } @Override public void onFilechange (archivo de archivo) {map <string, alarmConfig> ans = func.apply (archivo); // Si la carga falla, imprima un registro log.warn ("PropertiesConfig cambiado! Recargar ANS: {}", ANS); }}}Para la implementación anterior, algunas explicaciones breves:
Una pregunta, ¿qué sucede si se lanza una excepción cuando se ejecuta el método FUNC?
Los resultados de la prueba reales son los mismos que anteriores. Después de lanzar una excepción, aún debe tener cuidado y no ejecutar la excepción.
Entonces, echemos un vistazo a la lógica de implementación anterior y deduzcamos directamente el módulo central.
public void run () {while (true) {if (this.running) {iterator var1 = this.observers.iterator (); while (var1.hasNext ()) {FileAlterationObServer Observer = (FileAlterationObServer) var1.next (); observador.checkandNotify (); } if (this.running) {try {thread.sleep (this.interval); } capt (interruptedException var3) {; } continuar; } } devolver; }}Básicamente está claro de lo anterior que toda la lógica de implementación es diferente de nuestro primer método de tarea cronometrada. Aquí usamos hilos y bucles muertos directamente, y los métodos de sueño se utilizan para detenerse internamente. Por lo tanto, cuando ocurre una excepción, es equivalente a tirarlo directamente, y el hilo se arrodillará.
Versión JDK complementaria
JDK1.7 proporciona un servicio WatchService, que también se puede usar para monitorear los cambios de archivos. No he estado expuesto a eso antes, así que descubrí que existe esto. Luego busqué la información relacionada con el uso y descubrí que es bastante simple. Vi una publicación de blog que explica que está basada en eventos y tiene una mayor eficiencia. Aquí hay una demostración de ejemplo simple
@Testpublic void testFileUpwather () lanza IOException {// Instrucciones, el oyente aquí también debe ser la ruta del directorio = raths.get ("/tmp"); WatchService Watcher = filesystems.getDefault (). NewWatchService (); Path.register (Watcher, Entry_Modify); new Thread (() -> {try {while (true) {watchKey key = watcher.take (); for (watchEvent <?> Event: key.pollevents ()) {if (event.kind () == overflow) {// event se puede perder o descartar continuar;} rath fileName = (raty) event.context (); system.out.println ("File Update (" + fileMeMeMeMeMeMeMe; "FileNeMe); } if (! key.reset ()) {// restablecer WatchKey Break; intente {thread.sleep (1000 * 60 * 10); } catch (InterruptedException e) {E.PrintStackTrace (); }}IV. Resumen
El uso de Java para implementar el monitoreo de los cambios en el archivo de configuración implica principalmente dos puntos
En general, esta implementación es relativamente simple. Ya sea que se trate de una implementación personalizada o que confíe en Commos-IO, no tiene mucho costo técnico, pero una cosa a tener en cuenta es:
Para evitar la situación anterior, una implementación que se puede hacer es usar la notificación de mensajes asíncronos de EventBus. Después de cambiar el archivo, envíe un mensaje y luego agregue una anotación @SubScribe al método específico para recargar el contenido del archivo. Esto no solo realiza el desacoplamiento, sino que también evita las excepciones de servicio causadas por excepciones (si está interesado en esta implementación, puede comentar y explicar)
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.