Довольно типичная настройка — приложение Spring / Hibernate, которое требует распределенного кэша. Но это оказывается не так тривиально для настройки.
Вам явно нужен кеш. Есть варианты сделать это с EhCache, Hazelcast, Infinispan, memcached, Redis, эластичной болью AWS и некоторыми другими. Однако EhCache поддерживает только реплицированный и не распределенный кеш, а Hazelcast еще не работает с последней версией Hibernate. Infinispan и Hazelcast поддерживают согласованное хеширование, поэтому записи хранятся только в определенных экземплярах, а не имеют полную копию всего кэша в куче каждого экземпляра. Elasticache специфичен для AWS, поэтому Infinispann кажется наиболее сбалансированным вариантом с настройкой весны / гибернации.
Итак, давайте сначала настроим спящий 2-й уровень кеша. Официальная документация для infinispan не является лучшим результатом Google — обычно это либо очень старая документация, либо просто старая версия документа. Тебе лучше открыть последнюю версию с домашней страницы.
Некоторые из приведенных ниже вариантов довольно «скрыты», и я не мог легко найти их в документации или в существующих инструкциях.
Сначала добавьте соответствующие зависимости в конфигурацию вашего менеджера зависимостей. Вам понадобится infinispan-core
, infinispan-spring
и hibernate-infinispan
. Затем в вашем файле configuratoin (в зависимости от того, что это такое — в моем случае это jpa.xml, весенний файл, который определяет свойства JPA) настройте следующее:
1
2
3
4
5
6
|
< prop key = "hibernate.cache.use_second_level_cache" >true</ prop > < prop key = "hibernate.cache.use_query_cache" >true</ prop > < prop key = "hibernate.cache.region.factory_class" >org.hibernate.cache.infinispan.InfinispanRegionFactory</ prop > < prop key = "hibernate.cache.inifinispan.statistics" >true</ prop > < prop key = "hibernate.cache.infinispan.cfg" >infinispan.xml</ prop > < prop key = "hibernate.cache.infinispan.query.cfg" >distributed-query</ prop > |
Эти параметры включают кэш 2-го уровня и кэш запросов, используя фабрику региона по умолчанию (позже мы увидим, почему это может потребоваться изменить на пользовательский), включить статистику, указать файл конфигурации infinispan.xml и изменить имя по умолчанию для кеша запросов, чтобы можно было использовать распределенный (по умолчанию это «локальный кеш»). Конечно, вы можете перенести все это в файл .properties.
Затем в корне вашего пути к классам (src / main / resources) создайте infinispan.xml:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
<? xml version = "1.0" encoding = "UTF-8" ?> xsi:schemaLocation="urn:infinispan:config:8.1 http://www.infinispan.org/schemas/infinispan-config-8.1.xsd urn:infinispan:config:store:jdbc:8.0 http://www.infinispan.org/schemas/infinispan-cachestore-jpa-config-8.0.xsd" xmlns = "urn:infinispan:config:8.1" > < jgroups > < stack-file name = "external-file" path = "${jgroups.config.path:jgroups-defaults.xml}" /> </ jgroups > < cache-container default-cache = "default" statistics = "true" > < transport stack = "external-file" /> < distributed-cache-configuration name = "entity" statistics = "true" /> < distributed-cache-configuration name = "distributed-query" statistics = "true" /> </ cache-container > </ infinispan > |
-Djgroups.config.path
что -Djgroups.config.path
будет передан в JVM для указания конфигурации jgroups. В зависимости от того, используете ли вы свои собственные настройки или AWS, существует несколько вариантов. Здесь вы можете найти файлы конфигурации для EC2, облака Google и базового механизма UDP и TCP. Их следует размещать вне самого проекта, потому что локально вы, скорее всего, не хотите использовать S3_PING (механизм обнаружения узлов на основе S3), и значения могут различаться в зависимости от среды.
Если вам нужна статистика (и хорошо иметь ее), вы должны включить ее как на уровне кеш-контейнера, так и на уровне кеша. На самом деле я понятия не имею, что делает опция статистики в свойствах hibernate — она ничего не изменила для меня.
Затем вы определяете каждый из ваших кешей. Ваши сущности должны быть помечены чем-то вроде
1
2
|
@Cache (usage = CacheConcurrencyStrategy.READ_WRITE, region = "user" ) public class User { .. } |
И тогда Infinispan создает кеши автоматически. Все они могут иметь общие настройки по умолчанию, и эти значения по умолчанию определены для кеша с именем «entity». Мне понадобилось время, чтобы это выяснить, и, наконец, я получил ответ на stackoverflow . Последнее — это кеш запросов (используя имя, которое мы определили в свойствах гибернации). Обратите внимание на элементы «распределенной конфигурации кеша» — так вы явно скажете «этот (или все) кэш (ы) должен быть распределен» (они будут использовать механизм транспорта, указанный в файле jgroups). Вы можете настроить значения по умолчанию в jgroups-defaults.xml и указать на него, как показано в примере выше, если вы не хотите, чтобы разработчики указывали аргументы jvm.
Вы можете определить специфичные для сущности свойства, используя, например, <distributed-cache-configuration name="user" />
(проверьте автозаполнение из XSD, чтобы увидеть, какие у вас есть варианты конфигурации ( а XML — довольно удобный конфигурационный DSL, это ?)
Все идет нормально. Теперь наш кеш будет работать как локально, так и в AWS (EC2, S3), при условии, что мы настроим правильные ключи доступа, и локально. Технически, это может быть хорошей идеей иметь разные файлы infinispan.xml для локальных и производственных и определять по умолчанию <local-cache>
, а не распределенный, потому что с настройками TCP или UDP вы можете оказаться в кластер с другими товарищами по команде в той же сети (хотя я не уверен в этом, это может вызвать некоторые неожиданные проблемы).
Теперь весна. Если бы вы только настраивали SpringEmbeddedCacheManagerFactoryBean
, вы бы создали bean-компонент с SpringEmbeddedCacheManagerFactoryBean
, SpringEmbeddedCacheManagerFactoryBean
classpath:infinispan.xml
качестве местоположения ресурса, и это работало бы. И вы все еще можете сделать это, если вы хотите полностью разделить менеджеры кеша. Но менеджеры кэша хитры. Я кратко описал проблемы с EhCache , и здесь мы должны сделать несколько обходных путей, чтобы диспетчер кэша распределялся между hibernate и spring. Хорошая ли это идея — зависит. Но даже если вам нужны отдельные менеджеры кеша, вам может понадобиться ссылка на спящий менеджер кеша, поэтому часть шагов, описанных ниже, все еще необходима. Проблема с использованием отдельных кэшей — это имя JMX, под которым они зарегистрированы, но я думаю, что его можно также настроить.
Итак, если нам нужен менеджер общего кэша, мы должны создать подклассы двух фабричных классов:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
/** * A region factory that exposes the created cache manager as a static variable, so that * it can be reused in other places (e.g. as spring cache) * * @author bozho * */ public class SharedInfinispanRegionFactory extends InfinispanRegionFactory { private static final long serialVersionUID = 1126940233087656551L; private static EmbeddedCacheManager cacheManager; public static EmbeddedCacheManager getSharedCacheManager() { return cacheManager; } @Override protected EmbeddedCacheManager createCacheManager(ConfigurationBuilderHolder holder) { EmbeddedCacheManager manager = super .createCacheManager(holder); cacheManager = manager; return manager; } @Override protected EmbeddedCacheManager createCacheManager(Properties properties, ServiceRegistry serviceRegistry) throws CacheException { EmbeddedCacheManager manager = super .createCacheManager(properties, serviceRegistry); cacheManager = manager; return manager; } } |
Да, статическая переменная. Хитрый, я знаю, так что будь осторожен.
Затем мы повторно используем это для весны:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
/** * A spring cache factory bean that reuses a previously instantiated infinispan embedded cache manager * @author bozho * */ public class SharedInfinispanCacheManagerFactoryBean extends SpringEmbeddedCacheManagerFactoryBean { private static final Logger logger = ...; @Override protected EmbeddedCacheManager createBackingEmbeddedCacheManager() throws IOException { EmbeddedCacheManager sharedManager = SharedInfinispanRegionFactory.getSharedCacheManager(); if (sharedManager == null ) { logger.warn( "No shared EmbeddedCacheManager found. Make sure the hibernate 2nd level " + "cache provider is configured and instantiated." ); return super .createBackingEmbeddedCacheManager(); } return sharedManager; } } |
Затем мы изменяем свойство hibernate.cache.region.factory_class в конфигурации hibernate на наш новый пользовательский класс, а в нашем файле конфигурации spring мы делаем:
1
2
|
< bean id = "cacheManager" class = "com.yourcompany.util.SharedInfinispanCacheManagerFactoryBean" /> < cache:annotation-driven /> |
Кэш-память пружины используется с аннотацией @Cacheable уровня @Cacheable
которая позволяет нам кэшировать вызовы методов, и мы также можем получить доступ к CacheManager
помощью простого внедрения.
Затем «последняя» часть должна проверить, работает ли она. Даже если ваше приложение запускается нормально и выглядит нормально работающим, вы должны запустить свой тестовый пакет интеграции или селена и проверить статистику через JMX. У вас могут даже быть тесты, которые используют MBean для получения определенных статистических данных о кешах, чтобы убедиться, что они используются.
Ссылка: | Настройка распределенного кэша Infinispan с помощью Hibernate и Spring от нашего партнера по JCG Божидара Божанова в техническом блоге Bozho. |