Spring Boot集成Redis 从Docker安装到分布式
Spring从3.1开始,Spring引入了对Cache的支持,其使用方法和原理都类似于Spring对事务管理的支持,Spring Cache是作用在方法上。核心思想,调用一个缓存方法时会把该方法的参数和返回值作为一组键值对存放在缓存中,下次使用相同的参数来调用该方法时将直接读取缓存中的内容。
SpringBoot提供了对Redis集成的组件包spring-boot-starter-data-redis(不要依赖spring-boot-starter-redis,他是旧版本),该组件包同时依赖于spring-data-redis和lettuce,SpringBoot1.0默认使用Jedis客户端,2.0替换成了Lettuce。
Jedis在实现上是直连Redis服务,多线程环境下非线程安全,除非使用连接池,为每个RedisConnection实例增加物理连接。
Lettuce是一个可伸缩非阻塞且线程安全的Redis客户端,多个线程可以共享同一个RedisConnection,它利用netty NIO框架来高效地管理多个连接。
本文主要介绍Docker下安装Redis、SpringBoot集成Redis以及具体的应用分布式Session
Docker安装Redis
镜像选取
可通过DockerHub或者命令行选取镜像,Docker的安装可参考Docker安装
DockerHub选取
访问https://hub.docker.com搜索redis即可,https://hub.docker.com/search?q=redis&type=image
命令行选取
一般选取Starts最多的官方镜像
docker search redis
可以查看到如下内容(只截取了前三个)
kk@kk demo $ docker search redis
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
redis Redis is an open source key-value store that… 7926 [OK]
bitnami/redis Bitnami Redis Docker Image 138 [OK]
sameersbn/redis 79 [OK]
拉取镜像
默认拉取最新版本的镜像
docker pull redis
启动容器
查看镜像
docker images
可以查看到IMAGE ID(镜像ID)
创建容器
docker run --name myredis -p 6379:6379 -d redis redis-server --appendonly yes
- --name 设置命名(myredis)
- -p 映射宿主机端口到容器端口
- -d 后台以守护进程方式运行
- redis 镜像名(或者替换成镜像ID均可)
- redis-server --appendonly yes 在容器启动执行redis-server命令,打开redis持久化
查看容器
执行命令,查看正在运行的容器,可以观察到已正常启动
docker ps
启动成功后显示如下
kk@kk demo $ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
038d4475783a redis "docker-entrypoint.s…" 7 seconds ago Up 5 seconds 0.0.0.0:6379->6379/tcp myredis
查看容器日志
docker logs -f 038d4475783a
注意 038d4475783a 需替换成自己的redis容器ID
可以查看到以绑定6379端口
kk@kk demo $ docker logs -f 038d4475783a
1:C 18 Mar 2020 03:08:29.408 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
1:C 18 Mar 2020 03:08:29.408 # Redis version=5.0.8, bits=64, commit=00000000, modified=0, pid=1, just started
1:C 18 Mar 2020 03:08:29.408 # Configuration loaded
1:M 18 Mar 2020 03:08:29.409 * Running mode=standalone, port=6379.
1:M 18 Mar 2020 03:08:29.409 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
1:M 18 Mar 2020 03:08:29.409 # Server initialized
1:M 18 Mar 2020 03:08:29.410 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
1:M 18 Mar 2020 03:08:29.410 * Ready to accept connections
SpringBoot集成Redis
简易项目实战
pom添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置文件application
server:
port: 8078
spring:
application:
name: redis-session
cache:
type: redis
# redis 配置
redis:
# 服务器地址
host: 127.0.0.1
# 服务器端口
port: 6379
# 服务器连接密码(默认为空)
password:
# Redis分片(默认有16个分片,默认为0),在大型项目中建议使用0号分片存储,select分片耗时较大
database: 0
# 连接超时时间
timeout: 1000ms
redis简易使用
private final String cacheKey = "redis:cache:key:userid";
@Autowired
private StringRedisTemplate stringRedisTemplate;
@RequestMapping("/stringset")
public String stringSetAction(@RequestParam String key, @RequestParam String value) {
stringRedisTemplate.opsForValue().set(key, value);
return stringRedisTemplate.opsForValue().get(key);
}
基于注解的Redis
使用Spring Cache主要包含两个步骤,
- 声明某些方法使用缓存
- 配置Spring对Cache的支持
和Spring对事务管理的支持一样,Spring对Cache的支持也有基于注解和基于XML配置两种方式,以下将以注解方式进行验证
常用注解
@EnableCaching
开启缓存功能,一般放在启动类或者Redis的配置类上
@Cacheable
@Cacheable可以标记在方法上,也可以标记在类上。当标记在方法上时表示该方法是支持缓存的,当标记在类上时表示该类所有的方法都支持缓存。
对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次使用相同的参数调用该方法时,直接返回缓存中的结果。支持缓存的方法在对象内部被调用时不会触发缓存功能
Spring缓存方法的返回值是以键值对进行缓存的,值就是方法的返回结果,对于键,Spring支持两种策略,默认策略和自定义策略。
@Cacheable可以设置如下属性
- value:缓存名称(必填),指定缓存的命名空间
- key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义
- unless:条件符合则不缓存
- condition:条件符合则缓存
@CachePut
@CachePut也可以声明一个方法支持缓存功能,与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中
@CachePut可以设置如下属性
- value:缓存名称(必填),指定缓存的命名空间
- key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义
- unless:条件符合则不缓存
- condition:条件符合则缓存
@CacheEvict
@CacheEvict可以标记在方法上,也可以标记在类上,当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作,
@CacheEvict可以设置如下属性
- value:缓存名称(必填),指定缓存的命名空间
- key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义
- condition:条件符合则缓存
基于注解的项目实战
pom添加依赖
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
@Cacheable使用
注意:需要使用浏览器或postman进行验证,使用IDEA插件RestService调用缓存不会生效
private final String cacheKey = "redis:cache:key:userid";
@RequestMapping("/cache")
@Cacheable(value = cacheKey)
public String cacheIndex() {
System.out.println("set cache");
return "set cache";
}
只有首次访问的时候再控制台打印“set cache”,之后直接返回Redis结果,不会在控制台打印信息了
@CachePut使用
private final String cacheKey = "redis:cache:key:userid";
@RequestMapping("/put")
@CachePut(value = cacheKey)
public String putAction() {
System.out.println("update cache");
return "update cache";
}
每次访问会更新为本次的返回值
@CacheEvict使用
private final String cacheKey = "redis:cache:key:userid";
@RequestMapping("/del")
@CacheEvict(value = cacheKey)
public String delAction() {
System.out.println("delete cache");
return "delete cache";
}
删除缓存的内容
共享Session
分布式系统中,Session共享有很多解决方案,存储到缓存中是最常用的方案之一
Spring Session提供了一套创建和管理Servlet HttpSession的方案。Spring Session提供了集群Session(Clustered Sessions)功能,默认采用外置的Redis来存储Session数据,以此来解决Session共享问题
Session项目实战
pom依赖
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>
Session配置文件
@Configuration
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 86400*30)
public class SessionConfig {
}
maxInactiveIntervalInSeconds:设置Session失效时间,使用Redis Session之后,Spring Boot配置文件中的server.session.timeout属性将不再生效
测试-获取sessionID
注意:需要使用浏览器或postman进行验证,使用IDEA插件RestService调用缓存不会生效
@RestController
@RequestMapping("/session")
public class RedisSessionController {
@RequestMapping("/uid")
public String sessionAction(HttpSession session) {
UUID uid = (UUID) session.getAttribute("uid");
System.out.println("get " + uid);
if (uid == null) {
System.out.println("start set " + uid);
uid = UUID.randomUUID();
}
System.out.println("set " + uid);
session.setAttribute("uid", uid);
return session.getId();
}
}
可以多次调用观察控制台的打印信息