У меня есть несколько вопросов, связанных с ядром HazelCast, а также с API-интерфейсом кэша Spring Boot.
Позвольте мне сначала изложить сценарий.
У нас есть система мониторинга для мониторинга нескольких сетевых инфраструктур.
У меня есть приложение Spring Boot, которое можно развернуть как несколько узлов за балансировщиком нагрузки. В дополнение к этому, это же приложение может работать для нескольких инфраструктур, просто запустив его с другим профилем, таким как infra-1-prod, infra-2-prod и c.
. Его горизонтально масштабируемое, а также универсальны. Эта природа достигается за счет запуска приложения с различными профилями.
Наряду с другими изменениями этого профиля, изменяется подключение базовых БД к реляционной базе данных, которая содержит данные конфигурации для конкретной инфраструктуры.
Посмотрите на соответствующую архитектуру для приложения
![Relevant Architecture of Application](https://i.stack.imgur.com/CP8HY.png)
Одно и то же приложение с загрузочной пружиной может быть запущено как узел для различных инфраструктур, порождая свою собственную Узел экземпляра HazelCast. Если у нас есть 6 узлов для приложения, будет 6 узлов для кластера HazelCast. Все они будут синхронизированы c.
Теперь у меня есть Repository
с именем RuleRepository
, который возвращает данные Rule
для определенного Псевдонима правила .
@Repository
public interface RuleRepository extends JpaRepository<Rule, Long> {
@Cacheable(value = Constants.CACHE_ALIAS)
Optional<Rule> findByAlias(String ruleAlias);
//some other functions
}
Теперь проблема в том, что псевдонимы правил автоматически генерируются последовательностями БД, а псевдоним R_123 указывает на разные данные для узлов Infra-1 и Infra-2 но поскольку все узлы HazelCast синхронизированы c, неверные данные переопределяются.
Для этого я подумал о том, чтобы присвоить разные имена кешу для каждой инфраструктуры, чтобы кэшированные данные не перемешивались.
Это просто , а не , поскольку мы не можем вставлять свойства в имена кеша. Для этого нам нужно реализовать наши собственные пользовательские CacheResolver
и CacheManager
.
. Прежде чем задать первый вопрос, я изложу свое понимание HazelCast.
Каждый экземпляр HazelCast может иметь несколько Конфигурации карты, которые в основном просто разные кэши. Каждый CacheManager может быть связан с единичным экземпляром HazelCast, который будет внутренне содержать несколько кэшей.
Вопрос 1: Если связь между CacheManager и HazelCastInstance является однозначной, то как мне определить, какие данные метода будет кешироваться в какой кеш (Map Config).
Вот неполная реализация, которая у меня сейчас есть
public class CacheableOperations {
private final CacheManager cacheManager;
private final CacheManager noOpCacheManager;
public CacheableOperations(CacheManager cacheManager, CacheManager noOpCacheManager) {
this.cacheManager = cacheManager;
this.noOpCacheManager = noOpCacheManager;
}
private Map<String, CacheableOperation<?>> opMap;
public void init() {
List<CacheableOperation<? extends Class>> ops = new ArrayList<>();
ops.add(new CacheableOperation.Builder(RuleRepository.class)
.method("findByAlias")
.cacheManager(cacheManager)
.build());
postProcessOperations(ops);
}
public CacheableOperation<?> get(CacheOperationInvocationContext<?> context) {
final String queryKey = getOperationKey(context.getTarget().getClass().getName(),
context.getMethod().getName());
return opMap.get(queryKey);
}
private void postProcessOperations(List<CacheableOperation<? extends Class>> ops) {
Map<String, CacheableOperation<?>> tempMap = new HashMap<>();
for (CacheableOperation<?> op : ops) {
for (String methodName : op.getMethodNames()) {
tempMap.put(getOperationKey(op.getTargetClass().getName(), methodName), op);
}
}
opMap = ImmutableMap.copyOf(tempMap);
}
private String getOperationKey(String first, String second) {
return String.format("%s-%s", first, second);
}
Вот класс для CacheConfiguration
@Configuration
@AllArgsConstructor
public class CacheConfiguration extends CachingConfigurerSupport {
private final CacheProperties cacheProperties;
private SysdiagProperties sysdiagProperties;
@Bean
@Override
public CacheManager cacheManager() {
return new HazelcastCacheManager(hazelcastInstance());
}
@Bean
@Profile("client")
HazelcastInstance hazelcastInstance() {
Config config = new Config();
config.getNetworkConfig().getJoin().getTcpIpConfig().addMember(sysdiagProperties.getCache().getMemberIps()).setEnabled(true);
config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
config.setInstanceName("restapi-master-cache-" + sysdiagProperties.getServiceName());
return Hazelcast.newHazelcastInstance(config);
}
@Bean
@Override
public CacheResolver cacheResolver() {
return new CustomCacheResolver(cacheProperties, operations(), noOpCacheManager());
}
@Bean
public CacheManager noOpCacheManager() {
return new NoOpCacheManager();
}
@Bean
public CacheableOperations operations() {
CacheableOperations operations = new CacheableOperations(cacheManager(), noOpCacheManager());
operations.init();
return operations;
}
И вот CacheableOperation
класс
public class CacheableOperation<T> {
private final Class<T> targetClass;
private final String[] methodNames;
private final CacheManager cacheManager;
private CacheableOperation(Class<T> targetClass, String[] methodNames, CacheManager cacheManager) {
this.targetClass = targetClass;
this.methodNames = methodNames;
this.cacheManager = cacheManager;
}
public Class<T> getTargetClass() {
return targetClass;
}
public String[] getMethodNames() {
return methodNames;
}
public CacheManager getCacheManager() {
return cacheManager;
}
public static class Builder<T> {
private final Class<T> targetClass;
private String[] methodNames;
private CacheManager cacheManager;
private Map<String, Method> methods = new HashMap<>();
public Builder(Class<T> targetClass) {
this.targetClass = targetClass;
Arrays.stream(targetClass.getDeclaredMethods())
.forEachOrdered(method -> methods.put(method.getName(), method));
}
public Builder<T> method(String... methodNames) {
this.methodNames = methodNames;
return this;
}
public Builder<T> cacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
return this;
}
public CacheableOperation<T> build() {
checkArgument(targetClass != null);
checkArgument(ArrayUtils.isNotEmpty(methodNames));
checkArgument(Arrays.stream(methodNames).allMatch(name -> methods.get(name) != null));
return new CacheableOperation<T>(targetClass, methodNames, cacheManager);
}
}
}
И, наконец, CacheResolver
public class CustomCacheResolver implements CacheResolver {
private final CacheableOperations operations;
private final CacheProperties cacheProperties;
private final CacheManager noOpCacheManager;
public CustomCacheResolver(CacheProperties cacheProperties, CacheableOperations operations, CacheManager noOpCacheManager) {
this.cacheProperties = cacheProperties;
this.operations = operations;
this.noOpCacheManager = noOpCacheManager;
}
@Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
if (!cacheProperties.isEnabled()) {
return getCaches(noOpCacheManager, context);
}
Collection<Cache> caches = new ArrayList<>();
CacheableOperation operation = operations.get(context);
if (operation != null) {
CacheManager cacheManager = operation.getCacheManager();
if (cacheManager != null) {
caches = getCaches(cacheManager, context);
}
}
return caches;
}
private Collection<Cache> getCaches(CacheManager cacheManager, CacheOperationInvocationContext<?> context) {
return context.getOperation().getCacheNames().stream()
.map(cacheName -> cacheManager.getCache(cacheName))
.filter(cache -> cache != null)
.collect(Collectors.toList());
}
}
Вопрос 2: Во всей этой кодовой базе, Я не могу найти связь между именем кэша и именем метода, которое я сделал в первом фрагменте. Все, что я мог видеть, - это связь между именем метода и экземпляром cacheManager
. Где я могу это определить?
Все вопросы и документация, которые я читаю о Spring Boot и HazelCast, в данном случае не очень глубоки go.
Вопрос 3: Может ли кто-нибудь определить для меня роль CacheResolver
и CacheManager
прямым способом.
Спасибо за терпение. Ответ на хотя бы один вопрос может мне сильно помочь. :)