Используйте функцию подписки на сообщения Redis для обновления локального кеша кофеина в приложении.

1. Зачем обновлять кэш кофеина?

1.1, преимущества и недостатки кофеинового кеша

В производственной среде caffeine cache — это локальный кеш, который мы используем в приложении.
Его преимущество в том, что он существует в приложении и имеет самую высокую скорость доступа. Обычно он отвечает менее чем за 1 мс.
Недостатком является то, что его неудобно управлять, потому что он существует на нескольких веб-серверах с балансировкой нагрузки,
его трудно обновлять и удалять, как при управлении кешем Redis.

1.2, обычно мы устанавливаем время кэширования кофеина на 5 или 10 минут,

Но когда начнется масштабная акция, если срок действия кеша еще не истек,
данные, отображаемые веб-сервисом, не будут обновляться сразу.Как
мы обновляем кеш в приложении на нескольких веб-серверах?
Одним из решений является использование подписки на сообщения redis.Мы
отправляем сообщение в redis из фона, и
веб-служба, подписанная на redis, может обрабатывать кеш после получения сообщения,
чтобы обновить кеш на нескольких веб-серверах.

1.3 многоуровневое кэширование обычно используется в производственной среде,

Когда мы обновляем кеш кофеина,
мы не должны обращаться к базе данных, чтобы избежать одновременного доступа к базе данных.Вместо
этого после обновления Redis
локальный кеш получает данные от Redis,
а одновременный доступ порядка сотен или тысяч очень напряжен для Редис Смолл

2. Информация о демонстрационном проекте

pom.xml добавьте pom по мере необходимости

   <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
    </parent>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
   </dependency>
      
   <dependency>
         <groupId>com.github.ben-manes.caffeine</groupId>
         <artifactId>caffeine</artifactId>
   </dependency>

Инициализировать redisTemple

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.omg.CORBA.portable.UnknownException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    
    

    @Bean(name = "redisTemplate")
    @ConditionalOnClass(RedisOperations.class)
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
    
    
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);

        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>  
        (Object.class);
        ObjectMapper mapper = new ObjectMapper();
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        mapper.activateDefaultTyping(mapper.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(mapper);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 指定 key 的序列化格式
        template.setKeySerializer(stringRedisSerializer);
        // 指定 hash 的 key 的序列化格式
        template.setHashKeySerializer(stringRedisSerializer);
        // 指定 value 的序列化格式
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // 指定 hash 的 value 的序列化格式
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

добавить слушателя

import club.chuige.appmanagement.common.tool.cache.CacheManagerTool;
import club.chuige.appmanagement.common.consts.CacheConstants;
import club.chuige.appmanagement.common.consts.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.listener.PatternTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.listener.adapter.MessageListenerAdapter;

@Configuration
public class RedisListenerConfig {
    
    

    @Autowired
    CacheManagerTool cacheManagerTool;

    @Bean
    RedisMessageListenerContainer container(RedisConnectionFactory redisConnectionFactory,
                                            MessageListenerAdapter listenerAdapter) {
    
    
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        container.addMessageListener(listenerAdapter, new PatternTopic(CacheConstants.USER_CACHE));
        return container;
    }

    @Bean
    MessageListenerAdapter listenerAdapter() {
    
    
        return new MessageListenerAdapter(this, "receiveMessage");
    }

     // 接受到通知,清除本地缓存
    public void receiveMessage(String message) {
    
    
        String[] messages = message.split(Constants.SPLIT_COLON);
        CaffeineCache cache = cacheManagerTool.getCacheByName(messages[0]);
        cache.evict(messages[1]);

    }
}

Aop окружает операции обновления и удаления, отправляет широковещательные сообщения и делает недействительными локальные кэши других серверов.

  @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private void sendRedisTopic(String topic, String message) {
    
    
        redisTemplate.convertAndSend(topic, message);
    }

おすすめ

転載: blog.csdn.net/chuige2013/article/details/131000101