springboot之项目集群生成不同的hashcode导致缓存不一致。

场景:

集群项目使用访问策略使用的随机,缓存使用的redis,自定义key当传入两个参数以上时候,使用Arrays.deepHashCode来计算缓存的key。但是由于被缓存的方法有个固定的User.class参数,不过在deepHashCode方法中这个class不同的机器生成不同的值,导致产生的hashcode不一样

发现问题:

然后出现在机器1刚访问过这个接口,redis也有缓存,访问到机器2的时候仍然走service方法,也就是没有从redis取缓存。

(从日志看是否走的缓存,在自定义key生成里面打上日志,和接口调用的service打上日志,如果走了自定义key方法两遍就是在生成缓存,如果走一遍自定义key这个方法就是取的缓存)

然后找日志,同一个接口,在访问到不同机器的时候,生成的缓存key不一样,所以导致每个机器都需要缓存一遍。

原因:生成hash key的方法为Arrays.deepHashCode看方法,没有对class类型的判断

public static int deepHashCode(Object a[]) {
        if (a == null)
            return 0;

        int result = 1;

        for (Object element : a) {
            int elementHash = 0;
            if (element instanceof Object[])
                elementHash = deepHashCode((Object[]) element);
            else if (element instanceof byte[])
                elementHash = hashCode((byte[]) element);
            else if (element instanceof short[])
                elementHash = hashCode((short[]) element);
            else if (element instanceof int[])
                elementHash = hashCode((int[]) element);
            else if (element instanceof long[])
                elementHash = hashCode((long[]) element);
            else if (element instanceof char[])
                elementHash = hashCode((char[]) element);
            else if (element instanceof float[])
                elementHash = hashCode((float[]) element);
            else if (element instanceof double[])
                elementHash = hashCode((double[]) element);
            else if (element instanceof boolean[])
                elementHash = hashCode((boolean[]) element);
            else if (element != null)
                elementHash = element.hashCode();

            result = 31 * result + elementHash;
        }

        return result;
    }

查看User.class参数生成缓存key的时候,每台机器生成的代理class内容不一样,然后导致使用这个class内容生成的缓存不一致。所以集群情况下就会导致同一接口,不同机器生成各自的缓存key。

重写一下deepHashCode,这是改好的结果

可以在集群情况下。每个机器上,相同参数的情况下,保持生成的hash key一致。

这样就可以保证,在一个机器上生成缓存,下次请求到另外一个机器,仍然可以使用这个缓存。

public static int deepHashCode(Object a[]) {
        if (a == null)
            return 0;

        int result = 1;

        for (Object element : a) {
            int elementHash = 0;
            if (element instanceof Object[])
                elementHash = deepHashCode((Object[]) element);
            else if (element instanceof byte[])
                elementHash = hashCode((byte[]) element);
            else if (element instanceof short[])
                elementHash = hashCode((short[]) element);
            else if (element instanceof int[])
                elementHash = hashCode((int[]) element);
            else if (element instanceof long[])
                elementHash = hashCode((long[]) element);
            else if (element instanceof char[])
                elementHash = hashCode((char[]) element);
            else if (element instanceof float[])
                elementHash = hashCode((float[]) element);
            else if (element instanceof double[])
                elementHash = hashCode((double[]) element);
            else if (element instanceof boolean[])
                elementHash = hashCode((boolean[]) element);
            else if (element instanceof class)
                elementHash = hashCode((element.toString());
            else if (element != null)
                elementHash = element.hashCode();

            result = 31 * result + elementHash;
        }

        return result;
    }

这一句, else if (element instanceof class)
                elementHash = hashCode((element.toString());

加上对class的判断,对class做toString就会在不同机器产生一样的calss了,所以生成的hashcode一样。缓存就不变了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot集成Redis可以使用Spring Data Redis。Spring Data Redis是Spring Data的一部分,它提供了对Redis的支持,包括对Redis的连接、操作、数据序列化等。 以下是使用Spring Boot集成Redis缓存的步骤: 1. 添加Spring Data RedisRedis客户端依赖 在Maven中添加以下依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> ``` 2. 配置Redis连接信息 在application.properties中添加以下配置: ```properties spring.redis.host=localhost spring.redis.port=6379 ``` 3. 配置Redis缓存管理器 在Java配置类中添加以下代码: ```java @Configuration @EnableCaching public class CacheConfig { @Bean public RedisConnectionFactory redisConnectionFactory() { return new JedisConnectionFactory(); } @Bean public RedisTemplate<String, Object> redisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory()); redisTemplate.setKeySerializer(new StringRedisSerializer()); redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); return redisTemplate; } @Bean public CacheManager cacheManager() { RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate()); cacheManager.setDefaultExpiration(600); return cacheManager; } } ``` 4. 在需要缓存的方法上添加@Cacheable注解 例如: ```java @Service public class UserService { @Autowired private UserRepository userRepository; @Cacheable(value = "userCache", key = "#id") public User getUserById(Long id) { Optional<User> optionalUser = userRepository.findById(id); return optionalUser.orElse(null); } } ``` @Cacheable注解会将方法的返回值缓存Redis中,value属性指定缓存的名称,key属性指定缓存的键。 以上就是使用Spring Boot集成Redis缓存的步骤。注意,这里使用了默认的Redis连接工厂和Redis模板,如果需要更多的定制化配置,可以参考Spring Data Redis文档进行设置。 ### 回答2: Spring Boot 是一个用于快速构建 Java 应用程序的开源框架。它简化了基于 Spring 框架的应用程序的创建和配置过程。而 Redis 是一种快速且高效的内存数据库,用于存储和检索数据。 在 Spring Boot 中使用 Redis 缓存可以提高应用程序的性能和响应速度。要使用 Redis 缓存,首先需要在项目的依赖中添加 Redis 相关的依赖项。然后,在应用程序的配置文件中配置 Redis 的连接信息,包括主机名、端口号、密码等。 一旦配置完成,就可以在应用程序中使用 @Cacheable 注解将方法标记为可缓存的。当调用被标记为缓存的方法时,Spring Boot 会首先检查缓存中是否已经存在该数据,如果存在则直接返回缓存中的数据,否则执行方法并将结果存入缓存。可以使用 @CacheEvict 注解来清除缓存中的数据,以便在数据发生变化时及时更新缓存。 使用 Redis 缓存还可以有其他一些高级特性,例如设置缓存的过期时间、使用不同缓存生成策略等。还可以通过配置 Redis 集群实现高可用和负载均衡。 总而言之,Spring Boot 提供了简单而强大的工具来集成 Redis 缓存,通过使用 Redis 缓存可以提高应用程序的性能和可伸缩性,减轻后端数据库的负载,从而提供更好的用户体验。 ### 回答3: Spring Boot使用Redis作为缓存的步骤如下: 1. 导入Redis依赖:在pom.xml文件中添加Spring Boot对Redis的依赖。 2. 配置Redis连接信息:在application.properties或application.yml文件中配置Redis的连接信息,包括主机名、端口号、密码等。 3. 创建RedisTemplate Bean:在Spring Boot的配置类中创建RedisTemplate Bean,用于操作Redis数据库。 4. 使用RedisTemplate进行缓存操作:在需要使用缓存的方法上添加注解@EnableCaching,然后在方法执行时,使用RedisTemplate进行缓存的读取和写入操作。 5. 添加缓存注解:在需要进行缓存的方法上添加注解@Cacheable,用于标记此方法的结果需要被缓存。可以通过设置缓存key,来定制不同参数下的缓存策略。 6. 清除缓存:在需要清除缓存的方法上添加注解@CacheEvict,用于标记此方法执行后需要清除缓存。 通过以上步骤,Spring Boot就可以和Redis进行连接,并使用Redis作为缓存来提高应用程序的性能。在缓存读取时,先从Redis中获取数据,如果Redis中不存在,则从数据库中读取,然后将读取到的数据写入Redis中;在缓存写入时,先将数据写入Redis中,再同步写入数据库。这样可以大大提高读取数据的速度,并减轻数据库的压力。同时,Spring Boot提供了灵活的缓存策略配置,可以根据业务需求来定制缓存的命中规则和过期时间。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值