Лучшая стратегия кэширования дорогого веб-сервиса в Grails

У меня есть простое приложение Grails, которое нужно периодически делать для внешнего веб-сервиса во время сеанса пользователя (при использовании интерфейса).

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

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

Пример:

BuildConfig.groovy

plugins{
    compile ':cache:1.0.0'
}

MyController.groovy

def getItems(){
    def items = MyService.getItems()
    [items: items]
}

MyService.groovy

@Cacheable("itemsCache")
class MyService {
    def getItems() {
        def results

        //expensive external web service call

        return results
    }
}

UPDATE

Было много хороших вариантов. Я решил пойти с подходом к плагину, который предложил Берт. Я включил пример ответа с незначительными изменениями в приведенный выше пример кода, чтобы помочь другим, желающим сделать что-то подобное. Эта конфигурация завершает кеш через 24 часа.

BuildConfig.groovy

plugins{
    compile ':cache:1.1.7'
    compile ':cache-ehcache:1.0.1'
}

Config.groovy

grails.cache.config = {
    defaultCache {
        maxElementsInMemory 10000
        eternal false
        timeToIdleSeconds 86400
        timeToLiveSeconds 86400
        overflowToDisk false
        maxElementsOnDisk 0
        diskPersistent false
        diskExpiryThreadIntervalSeconds 120
        memoryStoreEvictionPolicy 'LRU'
     }
 }

Ответ 2

Взаимодействие/обходное решение будет заключаться в использовании комбинации @Cacheable ( "itemsCache" ) и @CacheFlush ( "itemsCache" ).

Сообщите метод getItems() для кэширования результатов.

@Cacheable("itemsCache")
def getItems() {
}

а затем еще один метод обслуживания для очистки кеша, который вы часто можете вызывать из задания.

@CacheFlush("itemsCache")
def flushItemsCache() {}

Ответ 3

Из grails-cache модульных тестов (Ищите timeToLiveSeconds), я вижу, что вы можете настроить кеширование на уровне кеша, а не на метод вызова или аналогичного. Используя этот метод, вы должны настроить параметры для grails.cache.config.

Вы создадите выделенный кеш с настройками времени жизни, а затем ссылаетесь на него в своей службе.

Ответ 4

После нескольких часов неудач в боях со SpEL я выиграл войну в конце! Так как вы знаете, что кэш Grails не имеет TTL из коробки. Вы можете придерживаться ehcache и делать какую-то причудливую конфигурацию. Или, что еще хуже, добавьте логику, очищающую ее при сохранении/обновлении и т.д. Но мое решение:

@Cacheable(value = 'domainInstance', key="#someId.concat((new java.util.GregorianCalendar().getTimeInMillis()/10000))")
def getSomeStuffOfDb(String someId){
         //extract something of db
  }       
}

и еще одно, что нужно указать. Вы можете пропустить конфигурацию в Config.groovy, и она будет создана и автоматически добавлена ​​сама.  Однако, если ваше приложение находится под нагрузкой сразу после запуска, это приведет к некоторым исключениям.

    2017-03-02 14:05:53,159 [http-apr-8080-exec-169] ERROR errors.GrailsExceptionResolver  - CacheException occurred when processing request: [GET] /some/get
Failed to get lock for campaignInstance cache creation. Stacktrace follows:

поэтому, чтобы избежать этого, добавьте конфигурацию, чтобы средства кэширования были готовы заранее.

grails.cache.enabled = true
grails.cache.clearAtStartup = true
grails.cache.config = {
    defaults {
        maxElementsInMemory 10000
        overflowToDisk false
    }
    cache {
        name 'domainInstance'
    }
}

GregorianCalendar(). getTimeInMillis()/10000 сделает TTL ~ 10 сек. /1000 ~ 1 сек. Чистая математика здесь.