Как мы можем хранить данные в области приложения для целей кэширования? - PullRequest
4 голосов
/ 06 апреля 2011

Я храню данные в области приложения.Я хочу очистить эти данные после каждого часа.Infact использует его как кеш на один час.Каков наилучший способ реализовать это?Ранее мы использовали область видимости сеанса для хранения этих данных, и срок действия истек после истечения сеанса.Поскольку эти данные уникальны для всего приложения, мы хотим сохранить их в области приложения.

Ответы [ 4 ]

5 голосов
/ 06 апреля 2011

Для очень простого кэша вы можете использовать в Google guava MapMaker : Вот пример, взятый из Javadoc:

   ConcurrentMap<Key, Graph> graphs = new MapMaker()
       .concurrencyLevel(4)
       .softKeys()
       .weakValues()
       .maximumSize(10000)
       .expireAfterWrite(10, TimeUnit.MINUTES)
       .makeComputingMap(
           new Function<Key, Graph>() {
             public Graph apply(Key key) {
               return createExpensiveGraph(key);
             }
           });

есть эти два метода expireAfterWrite и expireAfterRead , чтобы сделать то, что вы хотите. И бесплатно у вас есть многопоточная карта, со слабым значением, программными клавишами и отложенной оценкой, если вы хотите / нуждаетесь в этом:)

1 голос
/ 06 апреля 2011

В зависимости от того, хотите ли вы агрессивно очистить данные с истекшим сроком (для восстановления памяти) или вы просто хотите пересчитать после истечения срока действия, подход будет совсем другим.

Если вы просто хотите пересчитать, я бы расширил SoftReference, например:

public class ExpiringSoftReference<T> extends SoftReference<T> implements Serializable {
  private final long _expirationMoment;

  public ExpiringSoftReference(Object referent, long duration, TimeUnit unit) {
    this(referent, System.currentTimeMillis() + unit.toMillis(duration);
  }

  private ExpiringSoftReference(Object referent, long expirationMoment) {
    super(referent);
    _expirationMoment = expirationMoment;
  }

  public T get() {
    if (System.currentTimeMillis() >= _expirationMoment) {
      clear();
    }
    return super.get();
  }

  private Object writeReplace() throws ObjectStreamException {
    return new SerializedForm<T>(get(), _expirationMoment);
  }

  private static class SerializedForm<T> implements Serializable {
    private final T _referent;
    private final T _expirationMoment;

    SerializedForm(T referent, long expirationMoment) {
      _referent = referent;
      _expirationMoment = expirationMoment;
    }

    private Object readResolve() throws ObjectStreamException {
      return new ExpiringSoftReference<T>(_referent, _expirationMoment);
    }
  }
}

Если вы хотите активно восстанавливать память, вам нужно реализовать своего рода сборщик мусора. Подход заключается в том, чтобы сначала поместить все ссылки в очередь приоритетных потоков, а затем время от времени заглядывать в первый элемент, чтобы узнать, не истек ли хотя бы один из ссылок:

public class ExpiringSoftReference<T> 
extends SoftReference<T> 
implements Comparable<ExpiringSoftReference>, Serializable {
  // same as above, plus
  public int compareTo(ExpiringSoftReference other) {
    if (this._expirationMoment < other._expirationMoment) {
      return -1;
    } else if (this._expirationMoment > other._expirationMoment) {
      return 1;
    } else {
      return 0;
    }
  }

  final long expiration() {
    return _expirationMoment;
  }
}

public class ExpirationEnforcer {
  private final PriorityBlockingQueue<ExpiringSoftReference> _byExpiration = new ...();

  public void add(ExpiringSoftReference reference) {
    _byExpiration.put(reference);
  }

  public synchronized void tick() {
    long now = System.currentTimeMillis();
    while (true) {
      ExpiringSoftReference candidate = _byExpiration.peek();
      if (candidate == null || candidate.expiration() > now) {
        return;
      }
      ExpirationSoftReference toClear = _byExpiration.peek();
      toClear.clear();
    }
  }
}

Вам нужно будет вызывать tick () в очереди каждые пару секунд или что-то еще. Для этого в Java EE вам потребуется использовать службу таймера или что-то в этом роде.

0 голосов
/ 15 апреля 2011

Мы нашли способ очистить переменные области приложения, используя ServletContextListener и Timer.

public class SampleListener implements ServletContextListener {

public static final long _startTimerDelayMin = 10; // In minutes
public static final long _startTimerPeriodMin = 60; // In minutes

Timer timer;
ServletContext context =null;       

public void contextInitialized(ServletContextEvent contextEvent) {      
    context = contextEvent.getServletContext();     
    scheduleTimer();        
}   

public void scheduleTimer() {
    long delay = _startTimerDelayMin * 60000;   //initial delay set to _startTimerDelayMin minutes as per msecs
    long period = _startTimerPeriodMin * 60000;   //subsequent rate set to _startTimerPeriodMin minutes as per msecs
    timer = new Timer();
    timer.scheduleAtFixedRate(new RemindTask(), delay, period); 
}

public void contextDestroyed(ServletContextEvent arg0) {
    //Stopping Timer Thread once context destroyed
    timer.cancel();
}

private static String getPropertyValue(String key){
    String value = "";
    try{
        value = Util.getPropertyValueOfKey(key).trim();
    }catch(IOException e){

    }
    return value;
}

/**
 * This class is invoked at given interval to clear the application scope variable for browse call which have not been used for given time
 *
 */
class RemindTask extends TimerTask {

    public void run() {
        clearScopeVariables();
    } 

    /**
     * This function has logic to clear the application scope variable for browse call which have not been used for given time
     */
    public void clearScopeVariables() {
        Date dt = new Date();
        Enumeration<String> applicationScopeVarNames = (Enumeration<String>)context.getAttributeNames();
                    // TODO: clear the scope variables
    }       
}   

Добавить прослушиватель в web.xml

0 голосов
/ 06 апреля 2011

Откуда поступают эти данные?Если вы используете БД, вам следует рассмотреть возможность использования ORM с поддержкой кэшей первого и второго уровня, таких как Hibernate / JPA.Таким образом, вам не нужно менять свой код, чтобы он зависел от кэша (что сделало бы его довольно сложным в обслуживании / повторном использовании).Вы просто запускаете SQL-запросы обычным способом, и Hibernate / JPA будет возвращать объекты из кэша в памяти Java, когда это применимо.

См. Также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...