Многопоточное кэширование Java с одним потоком обновлений - PullRequest
4 голосов
/ 30 сентября 2010

У меня есть веб-сервис, в котором одновременно выполняется в среднем ~ 1 000 потоков запросов. Эти потоки обращаются к данным из кэша (в настоящее время в ehcache.) Когда срок действия записей в кэше истекает, поток, попавший в просроченную запись, пытается получить новое значение из БД, в то время как другие потоки также пытаются получить доступ к этому блоку ввода, т.е. Я использую декоратор BlockingEhCache. Вместо того, чтобы другие потоки ожидали «извлечения потока», я бы хотел, чтобы другие потоки использовали «устаревшее» значение, соответствующее «пропущенному» ключу. Есть ли сторонние разработчики ehcache декораторов для этой цели? Знаете ли вы какие-либо другие решения для кэширования, которые имеют такое поведение? Другие предложения?

Ответы [ 2 ]

1 голос
/ 02 октября 2010

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

Давайте предположим, что все потоки обращаются к этому кешу с использованием интерфейса Service, называемого FooService, и компонента EJB, называемого SimpleFooService. Служба будет иметь методы, необходимые для получения необходимых данных (которые также кэшируются). Таким образом, вы скрываете тот факт, что он кэшируется из внешнего интерфейса (http запрашивает объекты).

Вместо того, чтобы просто хранить данные для кэширования в свойстве сервиса, мы сделаем для него специальный объект. Давайте назовем это FooCacheManager. Он будет хранить кэш в свойстве в FooCacheManger (скажем, его типа Map). У него будут геттеры, чтобы получить кеш. Он также будет иметь специальный метод reload (), который будет загружать данные из БД (путем вызова служебных методов для получения данных или через DAO) и заменять содержимое кэша (сохраненного в свойстве). .

Хитрость в следующем:

  1. Объявите свойство кэша в FooCacheManger как AtomicReference (новый объект объявлен в Java 1.5). Это гарантирует безопасность потока, когда вы читаете, а также присваиваете ему. Ваши действия по чтению / записи никогда не будут конфликтовать или считывать половину записанного значения.
  2. Функция reload () сначала загрузит данные во временную карту, а затем по окончании назначит новую карту свойству, сохраненному в FooCacheManager. Поскольку свойство AtomicReference, присваивание является атомарным, таким образом, оно в основном перелистывает карту в одно мгновение без какой-либо блокировки.
  3. Внедрение TTL - пусть FooCacheManager реализует интерфейс QuartzJob и делает его эффективно работой с кварцем. В методе execute задания запустите reload (). В Spring XML определите это задание для запуска каждые хх минут (ваш TTL), которое также можно определить в файле свойств, если вы используете PropertyPlaceHolderConfigurer.

Этот метод эффективен, так как читает темы:

  1. Не блокировать для чтения
  2. Не вызывается isExpired () при каждом чтении, что составляет 1 Кбит / с.

Также поток записи не блокируется при записи данных.

Если это не понятно, я могу добавить пример кода.

0 голосов
/ 12 октября 2010

Поскольку ehcache удаляет устаревшие данные, другой подход может заключаться в обновлении данных с вероятностью, которая увеличивается по мере приближения времени истечения и равна 0, если время истечения «достаточно» далеко.

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

Если вы работаете со ссылками (поток обновления загружает объект, а затем просто изменяет ссылку в кеше), то для операций get и set в кэше не требуется отдельной синхронизации.

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