2012-10-05 13 views
5

Mein Anwendungsfall besteht darin, einen In-Memory-Cache über die Daten zu führen, die in einer persistenten Datenbank gespeichert sind.Implementieren eines periodisch aktualisierenden Cache in Java

Ich verwende die Daten zum Auffüllen einer Liste/Karte von Einträgen auf der Benutzeroberfläche. Zu jedem Zeitpunkt sollten die auf der Benutzeroberfläche angezeigten Daten so aktualisiert sein, wie es möglich ist (dies kann auch durch die Aktualisierungsfrequenz des Cache geschehen).

Der Hauptunterschied zwischen einer regulären Cache-Implementierung und diesem bestimmten Cache besteht darin, dass eine regelmäßige Aktualisierung aller Elemente in regelmäßigen Intervallen erforderlich ist und sich daher von einem LRU-Cache unterscheidet.

Ich muss diese Implementierung in Java tun und es wird großartig sein, wenn es irgendwelche vorhandenen Frameworks gibt, die verwendet werden können, um diese um sie herum zu bauen.

Ich habe die Google Guava-Cache-Bibliothek erkundet, aber sie ist eher für eine Aktualisierung pro Eintrag als für eine Massenaktualisierung geeignet. Es gibt keine einfachen APIs, die eine Aktualisierung für den gesamten Cache durchführen.

Jede Hilfe wird sehr geschätzt.

Auch wenn es möglich ist, inkrementell die Aktualisierung durchzuführen, sollte es groß sein, da die einzige Einschränkung beim Aktualisieren des gesamten Cache besteht, wenn der Cache sehr groß ist, sollte der Speicher-Heap mindestens zweimal sein die Größe des Caches, um die neuen Einträge zu laden und die alte durch die neue zu ersetzen. Wenn der Cache inkrementell ist oder eine Chunked-Aktualisierung (in gleichen Größen) durchgeführt wird, wird es großartig.

Antwort

3

EHCache ist erhalten mögen. Ich könnte mir vorstellen, dass sie etwas haben, was für dich funktionieren würde.

Um ein inkrementelles Neuladen eines Caches durchzuführen (was bei fast allen Caches funktionieren würde), durchlaufen Sie einfach die aktuell geladenen Einträge und erzwingen Sie deren Aktualisierung. (Sie könnten diese Aufgabe auf einem Hintergrundplaner ausführen).

Als Alternative zum Erzwingen des Neuladens des gesamten Caches kann EHCache eine "Time-to-Live" für einen Eintrag angeben, sodass Einträge automatisch neu geladen werden, wenn sie zu alt sind.

+1

@ jtahlborn- Die BulkLoader-API (http://ehcache.org/documentation/apis/bulk-loading) von EhCache ist hilfreich, aber es wäre großartig gewesen, wenn sie eine refreshTime- oder periodische Intervallzeit-Option bereitgestellt hätte, die sie selbst verwalten würde die Planung für die Cache-Aktualisierung. Wie auch immer, es kann immer durch einen externen Scheduler erreicht werden und die Bulk Loading API wird periodisch aufgerufen. Danke für die Antwort. –

+0

Für EHCache werfen Sie einen Blick auf: http://www.ehcache.org/documentation/3.3/thread-pools.html und http://terracotta.org/documentation/4.1/bigmemorymax/api/bulk-loading – Aliuk

+0

Aber. .ist time-to-live nicht einfach Element aus dem Cache entfernt? Es ist NICHT das gleiche wie das, was Sie hier geschrieben haben - "automatisches Neuladen" – javagirl

0

einfach diese Klasse erben und implementieren loadDataFromDB und update, wie Sie ein ziemlich voll funktionsfähige Java-Caching-Bibliothek, das inkrementelle Updates

import org.apache.log4j.Logger; 
import java.util.List; 
import java.util.concurrent.Semaphore; 


public abstract class Updatable<T> 
{ 
    protected volatile long lastRefreshed = 0; 
    private final int REFRESH_FREQUENCY_MILLISECONDS = 300000; // 5 minutes 
    private Thread updateThread; 
    private final Semaphore updateInProgress = new Semaphore(1); 

    protected static final Logger log = Logger.getLogger(Updatable.class); 

    public void forceRefresh() 
    { 
     try 
     { 
      updateInProgress.acquire(); 
     } 
     catch (InterruptedException e) 
     { 
      log.warn("forceRefresh Interrupted"); 
     } 

     try 
     { 
      loadAllData(); 
     } 
     catch (Exception e) 
     { 
      log.error("Exception while updating data from DB", e); 
     } 
     finally 
      { 
      updateInProgress.release(); 
     } 

    } 

    protected void checkRefresh() 
    { 
     if (lastRefreshed + REFRESH_FREQUENCY_MILLISECONDS <  System.currentTimeMillis()) 
      startUpdateThread(); 
    } 

    private void startUpdateThread() 
    { 
     if (updateInProgress.tryAcquire()) 
     { 
      updateThread = new Thread(new Runnable() 
      { 
       public void run() 
       { 
        try 
        { 
         loadAllData(); 
        } 
        catch (Exception e) 
        { 
         log.error("Exception while updating data from DB", e); 
        } 
        finally 
        { 
         updateInProgress.release(); 
        } 
       } 
      }); 

      updateThread.start(); 
     } 
    } 

    /** 
    * implement this function to load the data from DB 
    * 
    * @return 
    */ 
    protected abstract List<T> loadFromDB(); 

    /** 
    * Implement this function to hotswap the data in memory after it was loaded from DB 
    * 
    * @param data 
    */ 
    protected abstract void updateData(List<T> data); 

    private void loadAllData() 
    { 
     List<T> l = loadFromDB(); 
     updateData(l); 
     lastRefreshed = System.currentTimeMillis(); 
    } 

    public void invalidateCache() 
    { 
     lastRefreshed = 0; 
    } 

} 
+0

Danke für die Antwort RA. Wann wird die checkRefresh() Funktion aufgerufen? Wenn ich es richtig verstehe, würde dies einen kontinuierlichen Ablaufprozess erfordern, um in regelmäßigen Abständen mit checkRefresh abzufragen. Ich freute mich auf eine sauberere Implementierung, bei der ich einfach einen neuen Cache mit einem Cache Loader einbauen konnte. –

+0

CheckRefresh sollte in jeder Get-Operation aufgerufen werden, die Sie in Ihrer Klasse implementieren. ICH.E: öffentliche Daten get() {checkRefresh(); // Daten zurückgeben; } –

+0

Aber das wird die Latenz des Datenabrufs in Fällen beeinflussen, wenn der Update-Thread ausgelöst wird, als wenn er wie ein Cron vorausgegangen wäre und die Daten bereits vorab geholt hätte, wäre ein solcher Fall nicht entstanden. –

0

Eine Sache, die überprüft werden muss, ist das regelmäßige Auffrischen erforderlich? Sie können Ihre Aktualisierungslogik anwenden, sobald Sie Daten aus dem Cache abrufen. Dadurch entfällt die Notwendigkeit einer asynchronen Aktualisierung, und die Notwendigkeit, alte Kopien des Caches zu verwalten, entfällt. Diese IMO ist die einfachste und beste Methode zum Aktualisieren von Cachedaten, da sie keine zusätzlichen Gemeinkosten erfordert.

Dadurch wird sichergestellt, dass die Daten basierend auf dem Aktualisierungsintervall aktualisiert werden und keine asynchrone Aktualisierung erforderlich ist.

Verwandte Themen