Довольно типичная настройка — приложение 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. |