前言
最近都在聊Spring的缓存抽象Spring Cache,上篇文章深入介绍了Spring Cache集成进程缓存的第三方组件如Caffeine、Ehcache,若对此篇文章感兴趣,可移步观看:【小家Spring】玩转Spring Cache — 整合进程缓存之王Caffeine Cache和Ehcache3.x
我们知道现在的应用大都以微服务的方式进行分布式部署,因此如果仅仅使用本地缓存是 满足不了/难以满足 我们需求的(Infinispan这种基于本地内存实现的分布式缓存不在本文讨论范围~)。针对分布式场景下的缓存应用,我们急需要一个高可用的、快速的、中心化的、分布式的缓存产品。然后在众多此场景的实现产品中,Redis以它众多优秀的特性脱颖而出。
so,本文就以大伙最关心、最熟悉的Redis这个缓存产品为例,让它和Spring Cache集成,达到分布式中心缓存的效果(支持缓存直接直接操作)。
Redis:一个基于键值对存储的NoSQL内存数据库,可存储复杂的数据结构,如List, Set, Hashes。
关于Redis的单线程、数据结构、序列化、网络IO、高吞吐等高级特性,不是本文讨论的范围。本文只讨论集成注解缓存的k-v结构
Spring Data Redis简介和使用示例
Spring构建了自己庞大的生态,它对很多优秀的、流行的产品提供了一整套的整合、解决方案。Redis在缓存界这么广受欢迎,Spring Data工程中自然少不了它,它就是Spring Data Redis。
Spring Data Redis对Redis底层开发包(Jedis、Lettuce、JRedis、RJC)进行了高度封装。RedisTemplate封装提供了redis各种操作、异常处理及序列化,完全屏蔽里底层实现(使用者面向Spring Data编程即可,可完全不用关心底层到底使用的是Jedis or Lettuce)。
Spring Data Redis这个Jar的依赖包如下:
另外有个使用的Tips需要注意:在Spring Data Redis的使用方面上,我们还需要重视版本的差异:
其实我一直在强调版本意识,不管是在Spring上、JDK上,还是MyBatis,版本意识对你做架构都非常的重要
Spring Data Redis1.x截图:
Spring Data Redis2.x截图:
版本的差异从包结构上就能一目了然。具体到源码处,从RedisConnectionFactory
接口的继承图也能看出差异:
由图可知,从2.x版本开始,Spring就只为我们保留了jedis和lettuce。
lettuce是redis连接池未来的发展趋势,2.x开始已经推荐使用lettuce作为访问redis的client客户端。
不管用什么客户端,对使用者应该都是透明的,因为在开发过程中,没有极其特殊的情况,应该规定只允许使用RedisTemplate来操作Redis。
使用Jedis作为Client操作Redis示例
虽然说Jedis有多线程安全问题,并且它的性能也堪忧,大有被淘汰的趋势。但是,但是,但是毕竟它还仍旧还是当下的主流的Java访问Redis的客户端,所以本文也有必要把它的使用说一下,供以参考:
第一步:导包(本文以2.x为例)
<!-- 使用Spring Data Redis 操作Redis缓存 --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.1.9.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/redis.clients/jedis --> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>2.10.2</version> </dependency>
第二步:准备Config配置文件
//@EnableCaching // 因为此处我没准备CacheManager,暂时关闭缓存注解 @Configuration public class CacheConfig extends CachingConfigurerSupport { @Bean public RedisConnectionFactory redisConnectionFactory() { RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(); // 2.0后的写法 configuration.setHostName("10.102.132.150"); //configuration.setPassword(RedisPassword.of("123456")); configuration.setPort(6379); configuration.setDatabase(0); JedisConnectionFactory factory = new JedisConnectionFactory(configuration); // Spring Data Redis1.x这么来设置 2.0后建议使用RedisStandaloneConfiguration来取代 //factory.setHostName("10.102.132.150"); //factory.setPassword("123456"); //factory.setPort(6379); //factory.setDatabase(0); return factory; } @Bean public RedisTemplate<String, String> stringRedisTemplate() { RedisTemplate<String, String> redisTemplate = new StringRedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory()); return redisTemplate; } }
运行单元测试:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = {RootConfig.class, CacheConfig.class}) public class TestSpringBean { @Autowired private RedisConnectionFactory redisConnectionFactory; @Autowired private RedisTemplate<String, String> redisTemplate; @Test public void test1() { System.out.println(redisConnectionFactory); System.out.println(redisConnectionFactory.getConnection()); //System.out.println(redisConnectionFactory.getClusterConnection()); //InvalidDataAccessApiUsageException: Cluster is not configured! //System.out.println(redisConnectionFactory.getSentinelConnection()); // InvalidDataAccessResourceUsageException: No Sentinels configured System.out.println(redisTemplate); redisTemplate.opsForValue().set("name", "fsx"); System.out.println(redisTemplate.opsForValue().get("name")); } }
打印输出:
org.springframework.data.redis.connection.jedis.JedisConnectionFactory@238b521e org.springframework.data.redis.connection.jedis.JedisConnection@1cefc4b3 org.springframework.data.redis.core.StringRedisTemplate@2b27cc70 fsx
并且Redis Server端也能查看到key为“name”的值:
由此证明我们的缓存配置都能正常work了,能够使用RedisTempate操作Redis了。部分很简单有木有,只需要简单的两步即可达到目的~
关于Jedis或者说RedisTemplate的详细使用,显然也不是本文的重点,有兴趣的可自行研究,或者出门左拐~
使用Lettuce作为Client操作Redis示例
Lettuce作为新时代的Redis客户端,它势必成为将来的主流(其实现在也很主流了,比如SpringBoot2.0后默认就使用它作为Redis的Client访问)。
第一步:同样的导包(此处导入lettuce作为客户端)
<!-- 使用Spring Data Redis 操作Redis缓存 --> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-redis</artifactId> <version>2.1.9.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/io.lettuce/lettuce-core --> <dependency> <groupId>io.lettuce</groupId> <artifactId>lettuce-core</artifactId> <version>5.1.7.RELEASE</version> </dependency>
第二步:准备Config配置文件
//@EnableCaching @Configuration public class CacheConfig extends CachingConfigurerSupport { @Bean public RedisConnectionFactory redisConnectionFactory() { // RedisStandaloneConfiguration这个配置类是Spring Data Redis2.0后才有的~~~ RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(); // 2.0后的写法 configuration.setHostName("10.102.132.150"); //configuration.setPassword(RedisPassword.of("123456")); configuration.setPort(6379); configuration.setDatabase(0); LettuceConnectionFactory factory = new LettuceConnectionFactory(configuration); // Spring Data Redis1.x这么来设置 2.0后建议使用RedisStandaloneConfiguration来取代 //factory.setHostName("10.102.132.150"); //factory.setPassword("123456"); //factory.setPort(6379); //factory.setDatabase(0); return factory; } @Bean public RedisTemplate<String, String> stringRedisTemplate() { RedisTemplate<String, String> redisTemplate = new StringRedisTemplate(); redisTemplate.setConnectionFactory(redisConnectionFactory()); return redisTemplate; } }
用上了Spring Data Redis2.0提供的RedisStandaloneConfiguration配置类后,配置步骤和Jedis的一毛一样
运行如上的单元测试,打印结果如下:
org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory@59942b48 14:35:00.798 [main] INFO io.lettuce.core.EpollProvider - Starting without optional epoll library 14:35:00.800 [main] INFO io.lettuce.core.KqueueProvider - Starting without optional kqueue library org.springframework.data.redis.connection.lettuce.LettuceConnection@3cf7298d org.springframework.data.redis.core.StringRedisTemplate@1ff55ff fsx
从日志中会发现多输出了两句info日志,结果都是ok的,能正常work。
说明:SpringBoot1.x最终依赖的是Spring Data Redis 1.8.xx,默认导入使用的是Jedis客户端,版本号为2.9.x(非最新的3.x,不兼容,慎用)
SpringBoot2.0开始,依赖的是Spring Data Redis 2.x/x,并且默认导入使用的是Lettuce客户端,版本号是从5.x.x开始
当然,不管是Boot1.x或者2.x,都是允许你手动切换的(只是完全没有必要而已)。