文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python Spring缓存同步的最佳实践是什么?

2023-06-07 00:38

关注

在开发过程中,缓存是一个非常重要的概念。缓存可以提高应用程序的性能,减轻数据库的负担。Spring框架提供了缓存支持,可以很方便地实现缓存功能。但是,当多个应用程序同时访问同一个缓存时,就需要考虑缓存同步的问题。本文将介绍Python Spring缓存同步的最佳实践,并附带演示代码。

一、使用Spring缓存

Spring框架提供了一个非常方便的缓存支持。通过在方法上添加@Cacheable注解,可以将方法的返回值缓存起来,下次调用该方法时,直接返回缓存中的结果。例如:

@Cacheable("users")
public User getUserById(int id) {
    // ...
}

这段代码表示将getUserById方法的返回值缓存到名为“users”的缓存中。Spring框架会自动根据方法参数生成缓存的key,以保证缓存的唯一性。

二、缓存同步的问题

当多个应用程序同时访问同一个缓存时,就需要考虑缓存同步的问题。如果一个应用程序修改了缓存中的数据,其他应用程序也需要能够及时获取到修改后的数据。否则,就会出现数据不一致的情况。

三、解决方案

Python Spring缓存同步的最佳实践是使用Redis作为缓存中间件,并使用Redis的发布订阅功能实现缓存同步。

Redis是一个开源的内存数据结构存储系统,支持多种数据结构,例如字符串、哈希、列表、集合、有序集合等。Redis的发布订阅功能可以让多个客户端通过一个频道来实现消息的发布和订阅。当某个客户端向频道发布消息时,所有订阅该频道的客户端都能够收到该消息。因此,可以使用Redis的发布订阅功能来实现缓存同步。

具体实现方式如下:

  1. 在Spring配置文件中配置Redis缓存和Redis消息监听器:
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
    <constructor-arg ref="redisTemplate"/>
    <property name="defaultExpiration" value="3600"/>
</bean>

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory"/>
    <property name="keySerializer">
        <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
    </property>
    <property name="valueSerializer">
        <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
    </property>
</bean>

<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="localhost"/>
    <property name="port" value="6379"/>
    <property name="password" value=""/>
    <property name="database" value="0"/>
</bean>

<bean id="messageListener" class="com.example.MyMessageListener"/>

<bean id="redisMessageListenerContainer" class="org.springframework.data.redis.listener.RedisMessageListenerContainer">
    <property name="connectionFactory" ref="jedisConnectionFactory"/>
    <property name="messageListeners">
        <map>
            <entry key-ref="messageListener">
                <list>
                    <bean class="org.springframework.data.redis.listener.PatternTopic">
                        <constructor-arg value="__keyevent@0__:expired"/>
                    </bean>
                </list>
            </entry>
        </map>
    </property>
</bean>
  1. 在需要同步的缓存方法上添加@CachePut注解,并在方法中向Redis发布消息:
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
    // 更新用户信息
    // ...

    // 向Redis发布消息
    redisTemplate.convertAndSend("__keyevent@0__:expired", "users:" + user.getId());
    return user;
}

这段代码表示将updateUser方法的返回值更新到名为“users”的缓存中,并向Redis发布消息“users:xxx”,其中xxx为用户id。当其他应用程序访问“users:xxx”缓存时,Redis会自动触发消息监听器,从而更新缓存中的数据。

  1. 在消息监听器中处理缓存同步:
public class MyMessageListener implements MessageListener {
    @Autowired
    private CacheManager cacheManager;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String key = new String(message.getBody());
        if (key.startsWith("users:")) {
            int userId = Integer.parseInt(key.substring(6));
            cacheManager.getCache("users").evict(userId);
        }
    }
}

这段代码表示当Redis接收到消息“users:xxx”时,从名为“users”的缓存中删除缓存key为xxx的缓存项。这样,其他应用程序再次访问该缓存时,就会重新从数据库中获取最新的数据。

四、演示代码

完整的演示代码如下:

@Configuration
@EnableCaching
public class CacheConfig extends CachingConfigurerSupport {
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofMinutes(60))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new JdkSerializationRedisSerializer()));
        RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .transactionAware()
                .build();
        return redisCacheManager;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        return redisTemplate;
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.setHostName("localhost");
        jedisConnectionFactory.setPort(6379);
        jedisConnectionFactory.setPassword("");
        jedisConnectionFactory.setDatabase(0);
        return jedisConnectionFactory;
    }

    @Bean
    public MessageListenerAdapter messageListenerAdapter() {
        return new MessageListenerAdapter(new MyMessageListener());
    }

    @Bean
    public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory, MessageListenerAdapter messageListenerAdapter) {
        RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer();
        redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory);
        redisMessageListenerContainer.addMessageListener(messageListenerAdapter, new PatternTopic("__keyevent@0__:expired"));
        return redisMessageListenerContainer;
    }
}

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserDao userDao;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    @Cacheable(value = "users", key = "#id")
    public User getUserById(int id) {
        return userDao.getUserById(id);
    }

    @Override
    @CachePut(value = "users", key = "#user.id")
    public User updateUser(User user) {
        userDao.updateUser(user);
        redisTemplate.convertAndSend("__keyevent@0__:expired", "users:" + user.getId());
        return user;
    }
}

public class MyMessageListener implements MessageListener {
    @Autowired
    private CacheManager cacheManager;

    @Override
    public void onMessage(Message message, byte[] pattern) {
        String key = new String(message.getBody());
        if (key.startsWith("users:")) {
            int userId = Integer.parseInt(key.substring(6));
            cacheManager.getCache("users").evict(userId);
        }
    }
}

以上就是Python Spring缓存同步的最佳实践,希望对大家有所帮助。

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯