springboot( 三)redis demo
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; } }
测试:
发现第一次未走缓存,第二次走了缓存
如果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共享。
代码路径: