在学习了Redis Sentinel和Redis Cluster之后,通过几个例子来了解下SpringBoot2.0版本整合Redis Sentinel和Redis CLuster的Demo
Redis Sentinel github地址:https://github.com/swallretu/RedisSentinel
Redis Cluster github地址:https://github.com/swallretu/RedisCluster
Redis Sentinel和Redis Cluster分别采用了两种方式来实现redisConnectionFactory,Redis Sentinel使用,Redis Cluster使用LettuceConnectionFactory,具体细节可以从两个demo中看到.
Cachable: 可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,这个稍后会进行说明。需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的(换句话说就是如果在程序里面手动call这个方法,是不会调用缓存的)。@Cacheable可以指定三个属性,value、key和condition。
CachePut: 当我们使用CachePut的时候不需要我们自己在调用valueOperations.set(key,value)方法手动设置数据进入热地说,CachePut会自动setup value进入Redis.
value属性: value属性是必须指定的,其表示当前方法的返回值是会被缓存在哪个Cache上的,对应Cache的名称。其可以是一个Cache也可以是多个Cache,当需要指定多个Cache时其是一个数组。
@Cacheable(value="stu",key="#id")
public String getStudent(String id) {
System.out.println("进入getStudent()方法获取对象。。。");
String str = (String)valueOperations.get(STUDENT_REDIS_KEY_PREFIX+id);
/**
* 如果在项目中这里添加访问dao的操作; dao.getStuById(id)
*/
return str;
}
@Cacheable(value={"stu1","stu2"},key="#id")
public String getStudent(String id) {
System.out.println("进入getStudent()方法获取对象。。。");
String str = (String)valueOperations.get(STUDENT_REDIS_KEY_PREFIX+id);
/**
* 如果在项目中这里添加访问dao的操作; dao.getStuById(id)
*/
return str;
}
key属性:
key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。我们这里先来看看自定义策略,至于默认策略会在后文单独介绍。
自定义策略是指我们可以通过Spring的EL表达式来指定我们的key。这里的EL表达式可以使用方法参数及它们对应的属性。使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。下面是几个使用参数作为key的示例。
示例中的key保存到redis中是通过value::+key的方式做为Redis去存储数据,例如我们存储id=1的记录,最终redis里面的key=stu::1
/**
* 因为已经在redistemplate配置了jackson,所以直接用string来接受返回值
* @param id
* @return
*/
@Cacheable(value="stu",key="#id")
public String getStudent(String id) {
System.out.println("进入getStudent()方法获取对象。。。");
String str = (String)valueOperations.get(STUDENT_REDIS_KEY_PREFIX+id);
/**
* 如果在项目中这里添加访问dao的操作; dao.getStuById(id)
*/
return str;
}
/**
* 方法体里面不需要在进行任何添加数据进入redis的操作,因为CachePut标记的注解已经自动将数据添加到redis中,key=stu::#stu.id
* @param stu
* @return
*/
@CachePut(value="stu",key="#stu.id")
public String addStudent(Student stu) {
System.out.println("进入addStudent()方法添加对象。。。");
/**
* 如果在项目中这里添加访问dao的操作; dao.saveStu(stu)
*/
return stu.toString();
}
除了上述使用方法参数作为key之外,Spring还为我们提供了一个root对象可以用来生成key。通过该root对象我们可以获取到以下信息。
属性名称 | 描述 | 示例
---|---|---
methodName | 当前方法名 | #root.methodName
method | 当前方法 | #root.method.name
target | 当前被调用的对象 | #root.target
targetClass | 当前被调用的对象的class | #root.targetClass
args | 当前方法参数组成的数组 | #root.args[0]
caches | 当前被调用的方法使用的Cache | #root.caches[0].name
讲明白Cacheable注解后,我们来关注Springboot整合Redis Cluster的例子,示例里面代码的详细解释参考github里面的注释
application.yml
里面包含了对于Redis的配置以及通过lettuce来实现RedisConnectionFactory的配置
spring:
redis:
cluster:
nodes: 127.0.0.1:6004,127.0.0.1:6005,127.0.0.1:6006,127.0.0.1:6007,127.0.0.1:6008,127.0.0.1:6009
timeout: 5000ms
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
database: 0
RedisConfig
采用ObjectMapper和Jackson来实现key-value序列化,所以当我们在后面从Reids取出我们保存进去的对象的时候,Spring会返回我们已经按照Jackson处理的Json类型的对象信息。
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
public class RedisConfig {
/**
* 使用Lettuce做为ConnectionFactory,并且设置redisTemplate的key-value序列化
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<String,Object> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory){
//redis序列化器
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
template.setKeySerializer(jackson2JsonRedisSerializer);
template.setValueSerializer(jackson2JsonRedisSerializer);
template.setHashKeySerializer(jackson2JsonRedisSerializer);
template.setHashValueSerializer(jackson2JsonRedisSerializer);
return template;
}
}
StudentController
@RestController
@RequestMapping("/stu")
public class StudentController {
@Autowired
public IStudentService iStudentService;
@RequestMapping("/get/{id}")
public String getStu(@PathVariable("id") String id){
System.out.println();
String str = iStudentService.getStudent(id);
return "get执行成功==="+str;
}
@RequestMapping("/add")
public String addStu(@RequestParam(value="id") int id,
@RequestParam(value="name") String name,
@RequestParam(value="age") String age){
Student stu = new Student(id,name,age);
String str = iStudentService.addStudent(stu);
return "add执行成功===" + str;
}
@RequestMapping("/update")
public String updateStu(@RequestParam(value="id") int id,
@RequestParam(value="name") String name,
@RequestParam(value="age") String age){
Student stu = new Student(id,name,age);
System.out.println(stu.toString());
String idStr = String.valueOf(id);
String strOld = (String)iStudentService.getStudent(idStr);
iStudentService.updateStudent(stu);
String strNew = (String)iStudentService.getStudent(idStr);
StringBuilder build = new StringBuilder();
build.append(strOld).append("\t");
build.append(strNew).append("\t");
System.out.println(build.toString());
return "update执行成功===" + build.toString();
}
}
StudentService接口和实现类
实现类中添加了对于缓存的判断
public interface IStudentService {
public String getStudent(String key);
public String addStudent(Student stu);
public String updateStudent(Student stu);
public String getStus(String key);
public String addStus(List<Student> stus);
}
@Service
public class StudentServiceImpl implements IStudentService{
private final static String STUDENT_REDIS_KEY_PREFIX = "stu::";
@Autowired
private RedisTemplate<String,Object> redisTemplate;
private ValueOperations<String,Object> valueOperations;
private ListOperations<String,Object> listOperations;
@PostConstruct
public void setUpOperations(){
valueOperations = redisTemplate.opsForValue();
listOperations = redisTemplate.opsForList();
}
/**
* 因为已经在redistemplate配置了jackson,所以直接用string来接受返回值
* @param id
* @return
*/
@Cacheable(value="stu",key="#id")
public String getStudent(String id) {
System.out.println("进入getStudent()方法获取对象。。。");
String str = (String)valueOperations.get(STUDENT_REDIS_KEY_PREFIX+id);
/**
* 如果在项目中这里添加访问dao的操作; dao.getStuById(id)
*/
return str;
}
/**
* 方法体里面不需要在进行任何添加数据进入redis的操作,因为CachePut标记的注解已经自动将数据添加到redis中,key=stu::#stu.id
* @param stu
* @return
*/
@CachePut(value="stu",key="#stu.id")
public String addStudent(Student stu) {
System.out.println("进入addStudent()方法添加对象。。。");
/**
* 如果在项目中这里添加访问dao的操作; dao.saveStu(stu)
*/
return stu.toString();
}
/**
* 方法体里面不需要在进行任何添加数据进入redis的操作,因为CachePut标记的注解已经自动将数据添加到redis中,key=stu::#stu.id
* @param stu
* @return
*/
@CachePut(value="stu",key="#stu.id" )
public String updateStudent(Student stu) {
System.out.println("进入updateStudent()方法更新对象。。。");
return stu.toString();
}
@Override
public String getStus(String key) {
return null;
}
@Override
public String addStus(List<Student> stus) {
return null;
}
}
Student
public class Student implements Serializable {
int id;
String name;
String age;
public Student() {
}
public Student(int id,String name, String age) {
this.id = id;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"id='" + id + '\'' +
"name='" + name + '\'' +
", age='" + age + '\'' +
'}';
}
public int getId() {
return id;
}
...
...
...
public void setAge(String age) {
this.age = age;
}
}
RedisClusterApplication
添加@EnableCaChing启动缓存
@EnableCaching
@SpringBootApplication
public class RedisClusterApplication {
public static void main(String[] args) {
SpringApplication.run(RedisClusterApplication.class, args);
}
}
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.troy</groupId>
<artifactId>rediscluster</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>RedisCluster</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.5.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>