1.环境
在Linux服务上实现Redis服务 主从同步
<!-- RedisTemplate -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
spring:
redis:
master:
host: 192.168.8.128
port: 6381
password: 3Q3Q
database: 0
timeout: 4000
pool:
max-wait: -1
max-active: -1
max-idle: 20
min-idle: 10
slave:
host: 192.168.8.128
port: 6382
password: 3Q3Q
database: 0
timeout: 4000
pool:
max-wait: -1
max-active: -1
max-idle: 20
min-idle: 10
slave2:
host: 192.168.8.128
port: 6383
password: 3Q3Q
database: 0
timeout: 4000
pool:
max-wait: -1
max-active: -1
max-idle: 20
min-idle: 10
2.配置
package com.yzm.redis05.config;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class RedisProperties {
private Integer database;
private String host;
private Integer port;
private String password;
private Integer timeout;
private Pool pool;
@Data
public static class Pool {
private Integer maxActive;
private Integer minIdle;
private Integer maxIdle;
private Integer maxWait;
}
}
package com.yzm.redis05.config;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Configuration
public class ObjectMapperConfig {
private static final String PATTERN = "yyyy-MM-dd HH:mm:ss";
@Bean(name = "myObjectMapper")
public ObjectMapper objectMapper() {
JavaTimeModule javaTimeModule = new JavaTimeModule();
javaTimeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer());
javaTimeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer());
return new ObjectMapper()
// 转换为格式化的json(控制台打印时,自动格式化规范)
//.enable(SerializationFeature.INDENT_OUTPUT)
// Include.ALWAYS 是序列化对像所有属性(默认)
// Include.NON_NULL 只有不为null的字段才被序列化,属性为NULL 不序列化
// Include.NON_EMPTY 如果为null或者 空字符串和空集合都不会被序列化
// Include.NON_DEFAULT 属性为默认值不序列化
.setSerializationInclusion(JsonInclude.Include.NON_NULL)
// 如果是空对象的时候,不抛异常
.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
// 反序列化的时候如果多了其他属性,不抛出异常
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
// 取消时间的转化格式,默认是时间戳,可以取消,同时需要设置要表现的时间格式
.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false)
.setDateFormat(new SimpleDateFormat(PATTERN))
// 对LocalDateTime序列化跟反序列化
.registerModule(javaTimeModule)
.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY)
// 此项必须配置,否则会报java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to XXX
.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY)
;
}
static class LocalDateTimeSerializer extends JsonSerializer<LocalDateTime> {
@Override
public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeString(value.format(DateTimeFormatter.ofPattern(PATTERN)));
}
}
static class LocalDateTimeDeserializer extends JsonDeserializer<LocalDateTime> {
@Override
public LocalDateTime deserialize(JsonParser p, DeserializationContext deserializationContext) throws IOException {
return LocalDateTime.parse(p.getValueAsString(), DateTimeFormatter.ofPattern(PATTERN));
}
}
}
package com.yzm.redis05.config;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisClientConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
import javax.annotation.Resource;
import java.time.Duration;
@Configuration
public class RedisConfig {
@Resource(name = "myObjectMapper")
private ObjectMapper objectMapper;
//=============================================== master =======================================================
@Bean(name = "masterProperties")
@ConfigurationProperties(prefix = "spring.redis.master")
public RedisProperties masterProperties() {
return new RedisProperties();
}
@Primary
@Bean(name = "masterFactory")
public RedisConnectionFactory masterFactory(@Qualifier("masterProperties") RedisProperties redisProperties) {
return this.createRedisConnFactory(redisProperties);
}
@Bean(name = "masterRedisTemplate")
public RedisTemplate<String, Object> masterRedisTemplate(@Qualifier("masterFactory") RedisConnectionFactory redisConnectionFactory) {
return this.buildRedisTemplate(redisConnectionFactory);
}
//=============================================== slave =======================================================
@Bean(name = "slaveProperties")
@ConfigurationProperties(prefix = "spring.redis.slave")
public RedisProperties slaveProperties() {
return new RedisProperties();
}
@Bean(name = "slaveFactory")
public RedisConnectionFactory slaveFactory(@Qualifier("slaveProperties") RedisProperties redisProperties) {
return this.createRedisConnFactory(redisProperties);
}
@Bean(name = "slaveRedisTemplate")
public RedisTemplate<String, Object> slaveRedisTemplate(@Qualifier("slaveFactory") RedisConnectionFactory redisConnectionFactory) {
return this.buildRedisTemplate(redisConnectionFactory);
}
//=============================================== slave2 =======================================================
@Bean(name = "slave2Properties")
@ConfigurationProperties(prefix = "spring.redis.slave2")
public RedisProperties slave2Properties() {
return new RedisProperties();
}
@Bean(name = "slave2Factory")
public RedisConnectionFactory slave2Factory(@Qualifier("slave2Properties") RedisProperties redisProperties) {
return this.createRedisConnFactory(redisProperties);
}
@Bean(name = "slave2RedisTemplate")
public RedisTemplate<String, Object> slave2RedisTemplate(@Qualifier("slave2Factory") RedisConnectionFactory redisConnectionFactory) {
return this.buildRedisTemplate(redisConnectionFactory);
}
public RedisConnectionFactory createRedisConnFactory(RedisProperties redisProperties) {
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration();
standaloneConfiguration.setHostName(redisProperties.getHost());
standaloneConfiguration.setPort(redisProperties.getPort());
standaloneConfiguration.setDatabase(redisProperties.getDatabase());
standaloneConfiguration.setPassword(redisProperties.getPassword());
return lettuceConnectionFactory(redisProperties, standaloneConfiguration);
// return jedisConnectionFactory(redisProperties, standaloneConfiguration);
}
//Lettuce
private LettuceConnectionFactory lettuceConnectionFactory(RedisProperties redisProperties, RedisStandaloneConfiguration standaloneConfiguration) {
LettuceClientConfiguration build = LettucePoolingClientConfiguration.builder().
commandTimeout(Duration.ofMillis(redisProperties.getTimeout()))
.poolConfig(createPoolConfig(redisProperties, new GenericObjectPoolConfig()))
.build();
LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(standaloneConfiguration, build);
lettuceConnectionFactory.afterPropertiesSet();
return lettuceConnectionFactory;
}
//Jedis
private JedisConnectionFactory jedisConnectionFactory(RedisProperties redisProperties, RedisStandaloneConfiguration standaloneConfiguration) {
JedisClientConfiguration build = JedisClientConfiguration.builder()
.connectTimeout(Duration.ofMillis(redisProperties.getTimeout()))
.usePooling()
.poolConfig(createPoolConfig(redisProperties, new JedisPoolConfig()))
.build();
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(standaloneConfiguration, build);
jedisConnectionFactory.afterPropertiesSet();
return jedisConnectionFactory;
}
private GenericObjectPoolConfig createPoolConfig(RedisProperties redisProperties, GenericObjectPoolConfig poolConfig) {
//最大连接数, 默认8个
poolConfig.setMaxTotal(redisProperties.getPool().getMaxActive());
//最大空闲连接数, 默认8个
poolConfig.setMaxIdle(redisProperties.getPool().getMaxIdle());
//最小空闲连接数, 默认0个
poolConfig.setMinIdle(redisProperties.getPool().getMinIdle());
//获取连接时的最大等待毫秒数,默认-1
poolConfig.setMaxWaitMillis(redisProperties.getPool().getMaxWait());
//最小可驱逐空闲时间 毫秒,达到此值后空闲资源将被移除 默认1800000毫秒(30分钟)
poolConfig.setMinEvictableIdleTimeMillis(1800000);
//空闲资源的检测周期(单位为毫秒) 如果为负数,则不运行逐出线程, 默认-1
poolConfig.setTimeBetweenEvictionRunsMillis(-1);
poolConfig.setTestWhileIdle(false);
poolConfig.setTestOnCreate(false);
poolConfig.setTestOnBorrow(false);
poolConfig.setTestOnReturn(false);
return poolConfig;
}
public RedisTemplate<String, Object> buildRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
// 配置连接工厂
template.setConnectionFactory(redisConnectionFactory);
//使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
jacksonSerializer.setObjectMapper(objectMapper);
//使用StringRedisSerializer来序列化和反序列化redis的key,value采用json序列化
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(jacksonSerializer);
// 设置hash key 和value序列化模式
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(jacksonSerializer);
template.afterPropertiesSet();
return template;
}
}
3.接口
package com.yzm.redis05.controller;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
@RestController
public class RedisController {
@Resource(name = "masterRedisTemplate")
private RedisTemplate<String, Object> writeTemplate;
@Resource(name = "slaveRedisTemplate")
private RedisTemplate<String, Object> readTemplate;
@Resource(name = "slave2RedisTemplate")
private RedisTemplate<String, Object> read2Template;
@GetMapping("/set")
public String set(String key, String value) {
writeTemplate.opsForValue().set(key, value);
return "success";
}
@GetMapping("/get")
public String get(String key) {
System.out.println("readTemplate = " + readTemplate.opsForValue().get(key));
System.out.println("read2Template = " + read2Template.opsForValue().get(key));
return "success";
}
}
4.测试
http://localhost:8080/set?key=name&value=yzm
http://localhost:8080/get?key=name