springboot( 三)redis demo

zhangtianshun 2020-05-03

redis介绍

Redis是目前业界使用最广泛的内存数据存储。相比memcached,Redis支持更丰富的数据结构,例如hashes, lists, sets等,同时支持数据持久化。除此之外,Redis还提供一些类数据库的特性,比如事务,HA,主从库。可以说Redis兼具了缓存系统和数据库的一些特性,因此有着丰富的应用场景。本文介绍Redis在Spring Boot中两个典型的应用场景。

如何使用

1、引入 spring-boot-starter-data-redis

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-redis</artifactId> 
</dependency>

2、添加配置文件

#redis配置
# REDIS (RedisProperties)
# Redis服务器地址
spring.redis.host=192.168.31.151
# Redis服务器连接端口
spring.redis.port=6379  
# Redis服务器连接密码(默认为空)
spring.redis.password=  
#Redis数据库索引(默认为0)
spring.redis.database=0  
#连接池最大连接数(使用负值表示没有限制)
spring.redis.jedis.pool.max-active=50
#连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.jedis.pool.max-wait=3000
#连接池中的最大空闲连接
spring.redis.jedis.pool.max-idle=20
#连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=2
#连接超时时间(毫秒)
spring.redis.timeout=5000

至于redis的安装配置,可见分布式进阶redis,一种安装传统的redis,一种用docker ,建议先安装docker,直接用docker pull redis 比较方便

redisConfig->根据StringRedisTemplate对象命名我们可以知道该对象支持String类型,但是在实际的应用中,我们可能需要存入Object对象。那该怎么存储呢。聪明的你,肯定立刻想到了,直接把对象转成json格式字符串,不就可以存储了嘛。这里我使用jackson依赖转换成json数据

@Configuration
public class RedisConfig {

//    /**
//     * 连接 redis 需要 RedisConnection 和 RedisConnectionFactory,
//     * RedisConnection 是通过 RedisConnectionFactory 进行创建
//     * RedisConnection 提供较低级的数据操作 (byte arrays)
//     */
//    @Bean
//    RedisConnectionFactory initJedisConnectionFactory(){
//        //在这里设置redis连接对象配置
//        return new JedisConnectionFactory();
//    }
    
    /**
     * 配置RedisTemplate实例
     * @param factory
     * @return
     */
    @Bean
    public RedisTemplate<Serializable, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate<Serializable, Object> template = new RedisTemplate<Serializable, Object>();
        template.setConnectionFactory(connectionFactory);
        template.afterPropertiesSet();
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new ObjectRedisSerializer());//自定义
        //template.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));//redis提供现有的
        return template;
    }
}
ObjectRedisSerializer类
@Slf4j
public class ObjectRedisSerializer implements RedisSerializer<Object>{

    
    
    /**
     * 定义序列化和发序列化转化类
     */
    private Converter<Object, byte[]> serializer = new SerializingConverter();
    private Converter<byte[], Object> deserializer = new DeserializingConverter();
    
    /**
     * 定义转换空字节数组
     */
    private static final byte[] EMPTY_ARRAY = new byte[0]; 
    
    @Override
    public byte[] serialize(Object obj) throws SerializationException {
        byte[] byteArray = null;
        if (null == obj) {
            log.warn("Redis待序列化的对象为空.");
            byteArray = EMPTY_ARRAY;
        } else {
            try {
                byteArray = serializer.convert(obj);
            } catch (Exception e) {
                log.error("Redis序列化对象失败,异常:"+e.getMessage());
                byteArray = EMPTY_ARRAY;
            }
        }
        return byteArray;
    }

    @Override
    public Object deserialize(byte[] datas) throws SerializationException {
        Object obj = null;
        if(isNullOrEmpty(datas)){
            log.warn("Redis待反序列化的对象为空.");
        }else{
            try {
                obj = deserializer.convert(datas);
            } catch (Exception e) {
                log.error("Redis反序列化对象失败,异常:"+e.getMessage());
            }
        }
        return obj;
    }
    
    private boolean isNullOrEmpty(byte[] datas){
      return (null == datas)|| (datas.length == 0);
    }
}

RedisController

@RestController
@Slf4j
public class RedisController {
    
    @Autowired
    private IUserService userService;
    
    @Autowired
    private RedisUtils redisUtils;
    
    @Autowired
    private RedisTemplate<Serializable, Object> redisTemplate;
     
    @RequestMapping("redis/getUser")
    public User getUser() throws Exception {
        String username = "lisi";
        User user  = (User) redisTemplate.opsForValue().get("user"+username);
        if(user ==null){
            log.info("未命中缓存!");
            user=userService.findByUsername(username);
            redisTemplate.opsForValue().set("user"+username, user);
        }
        return user;
    }
    
    @RequestMapping("redis/getUser2")
    public User getUser2() throws Exception {
        String username = "zhangsan";
        User user  = (User) redisUtils.get("user"+username);
        if(user ==null){
            log.info("未命中缓存!");
            user=userService.findByUsername(username);
            redisUtils.set("user"+username, user);
        }
        return user;
    }
    
}

也可以将常用操作提取出来,封装成RedisUtils,直接使用即可

RedisUtils

@Component
public class RedisUtils {
 
    @Autowired
    private RedisTemplate<Serializable, Object> redisTemplate;
 
    /**
     * 读取缓存
     * 
     * @param key
     * @return
     */
    public Object get(final String key) {
        return redisTemplate.opsForValue().get(key);
    }
 
    /**
     * 写入缓存
     */
    public boolean set(final String key, Object value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().set(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
 
    /**
     * 更新缓存
     */
    public boolean getAndSet(final String key, Object value) {
        boolean result = false;
        try {
            redisTemplate.opsForValue().getAndSet(key, value);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
 
    /**
     * 删除缓存
     */
    public boolean delete(final String key) {
        boolean result = false;
        try {
            redisTemplate.delete(key);
            result = true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

测试:

springboot( 三)redis demo

 发现第一次未走缓存,第二次走了缓存

如果redis获取的User不能强转,即xxx不能转换为xxx,按常理说不通,A怎么不能cast成A呢,debug仔细检查了是否有拼错类名及不一致情况,确实xxx就是xxx,
怎么就不能cast呢?

google一通得知和热部署有关,和用到的spring-boot-devtools有关,和classloader有关,这样就说的通了。看了许多帖子,都是比较直接的解决方案,把spring-boot-devtools注释掉,不要热部署了

共享Session-spring-session-data-redis

分布式系统中,sessiong共享有很多的解决方案,其中托管到缓存中应该是最常用的方案之一,

Spring Session官方说明

Spring Session provides an API and implementations for managing a user’s session information.

如何使用

1、引入依赖

<dependency>
    <groupId>org.springframework.session</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
2、Session配置:

@Configuration@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)//失效时间一个月
public class SessionConfig {
}

注意:maxInactiveIntervalInSeconds: 设置Session失效时间,使用Redis Session之后,原Boot的server.session.timeout属性不再生效

redis 客户端 输入 keys ‘*sessions*‘

1) "spring:session:expirations:1591092540000"
2) "spring:session:sessions:expires:88fda400-dc85-4a22-8126-e1858febdc60"
3) "userzhangsan"
4) "spring:session:sessions:88fda400-dc85-4a22-8126-e1858febdc60"
5) "user"
6) "userlisi"
127.0.0.1:6379>

其中 1591092540000为失效时间,意思是这个时间后session失效,88fda400-dc85-4a22-8126-e1858febdc60 为sessionId,登录http://localhost:8080/uid 发现会一致,就说明session 已经在redis里面进行有效的管理了。

如何在两台或者多台中共享session

其实就是按照上面的步骤在另一个项目中再次配置一次,启动后自动就进行了session共享。

代码路径:

 

相关推荐

DiamondTao / 0评论 2020-08-30