SpringBoot整合Redis及Redis简介

前言

由于版本原因,SpringBoot2.0整合Redis和低版本的SpringBoot不太一样,此方案基于Srping Boot 2.x

Linux下Redis安装

整合

maven 依赖

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 下面的不用加,用于分布式共享session的配置 -->
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-data-redis</artifactId>
</dependency>

分布式共享session参考:参考

配置文件application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Redis 数据库索引
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=maxzhao
# 连接池最大连接数
spring.redis.jedis.pool.max-active=1000
# 连接池最大阻塞等待时间,负值没有限制
spring.redis.jedis.pool.max-wait=-1
# 连接池中最大空闲连接
spring.redis.jedis.pool.max-idle=10
# 连接池中的最小空闲连接
spring.redis.jedis.pool.min-idle=2
# 超时时间 毫秒
spring.redis.timeout=1000

对应的配置类:org.springframework.boot.autoconfigure.data.redis.RedisProperties

Jedis 与 Lettuce 两种连接方式

  • defautl : 默认,0配置 ,也就是走的是 lettuce 单通道方式。 端口:8081
  • jedis : 使用Jedis 连接池。 端口:8082
  • lettue: lettuce 连接池方式。 端口:8083
  1. 使用jedis:当多线程使用同一个连接时,是线程不安全的。所以要使用连接池,为每个jedis实例分配一个连接。
  2. 使用Lettuce:当多线程使用同一连接实例时,是线程安全的。

注入

1
2
3
4
5
6
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
// StringRedisTemplate是继承RedisTemplate的,
// RedisTemplate在StringRedisTemplate中泛型定义为String

StringRedisTemplate 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class StringRedisTemplate extends RedisTemplate<String, String> {
public StringRedisTemplate() {
this.setKeySerializer(RedisSerializer.string());
this.setValueSerializer(RedisSerializer.string());
this.setHashKeySerializer(RedisSerializer.string());
this.setHashValueSerializer(RedisSerializer.string());
}

public StringRedisTemplate(RedisConnectionFactory connectionFactory) {
this();
this.setConnectionFactory(connectionFactory);
this.afterPropertiesSet();
}

protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
return new DefaultStringRedisConnection(connection);
}
}

StringRedisTemplate 测试

这里用的是Springboot Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
@RunWith(SpringRunner.class)
@SpringBootTest(classes = IttestApplication.class)
public class RedisTest {


@Autowired
private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;

@Test
public void testRedisTemplate() {
String prefix = "maxzhao:redisTemplate:";
HashOperations hashOperations = redisTemplate.opsForHash();
ValueOperations valueOps = redisTemplate.opsForValue();
ListOperations listOps = redisTemplate.opsForList();
SetOperations setOps = redisTemplate.opsForSet();
ZSetOperations zSetOps = redisTemplate.opsForZSet();
GeoOperations geoOperations = redisTemplate.opsForGeo();
ClusterOperations clusterOperations = redisTemplate.opsForCluster();
Map map = Arrays.stream(new String[]{"a", "B"}).collect(Collectors.toMap(Function.identity(), Function.identity()));
hashOperations.putAll(prefix + "hash", map);
}

@Test
public void testStringRedisTemplate() {
String prefix = "maxzhao:stringRedisTemplate:";
HashOperations hashOperations = stringRedisTemplate.opsForHash();
hashOperations.putAll(prefix + "hash", Arrays.stream(new String[]{"a", "b"}).collect(Collectors.toMap(Function.identity(), Function.identity())));
hashOperations.putAll(prefix + "hash", Arrays.stream(new String[]{"c", "d"}).collect(Collectors.toMap(Function.identity(), Function.identity())));
hashOperations.putAll(prefix + "hash", Arrays.stream(new String[]{"e", "f"}).collect(Collectors.toMap(Function.identity(), Function.identity())));
hashOperations.put(prefix + "hash", "max", "maxvalue");// a b c d e f max
hashOperations.get(prefix + "hash", "max");// maxvalue
hashOperations.delete(prefix + "hash", "f");// return 1 database:a b c d e max
hashOperations.delete(prefix + "hash", "c", "d");// return 2 database:a b e max
hashOperations.hasKey(prefix + "hash", "max");//return true
hashOperations.values(prefix + "hash");// return map object
ValueOperations valueOperations = stringRedisTemplate.opsForValue();
valueOperations.set(prefix + "value", "value");
valueOperations.set(prefix + "value", "valueTest");//value 被覆盖
valueOperations.get(prefix + "value");// return valueTest

ListOperations listOps = stringRedisTemplate.opsForList();
// listOps.remove(prefix + "list", listOps.size(prefix + "list"), 1);
listOps.leftPush(prefix + "list", "A");
listOps.leftPush(prefix + "list", "B");
listOps.rightPush(prefix + "list", "C", "D");//只有 1:B 2:A
listOps.leftPush(prefix + "list", "C");
listOps.leftPush(prefix + "list", "D");//return 3 1:D 2:C 3:A
listOps.range(prefix + "list", 0, listOps.size(prefix + "list"));//return 3 0:D 1:C 2:A list下标从0开始
listOps.leftPop(prefix + "list");//只有 1:A 返回的为B
listOps.leftPush(prefix + "list2", "A");
listOps.leftPush(prefix + "list2", "B");//只有 1:B 2:A
listOps.rightPush(prefix + "list2", "C");//只有 1:B 2:A 3 C

// set 是无序的,所有pop等获取value的操作,得到结果可能不同
SetOperations setOps = stringRedisTemplate.opsForSet();
setOps.add(prefix + "set", "A");//return 1
setOps.add(prefix + "set", "A");//return 0
setOps.add(prefix + "set", "B");//return 1
setOps.difference(prefix + "set", "A");//return HashSet A,B
setOps.isMember(prefix + "set", "A");//return true
setOps.isMember(prefix + "set", "C");//return false
setOps.members(prefix + "set");//return HashSet A,B
setOps.pop(prefix + "set");// 出序列并删除 1个
setOps.add(prefix + "set", "A","B", "C", "D", "E");//return 5
setOps.pop(prefix + "set", 2);// 出序列并删除 2个
setOps.add(prefix + "set", "A","B", "C", "D", "E");
setOps.move(prefix + "set", "D", "A");//return true database=BCE
// 把当前key=set的c值,move到 key=set1
setOps.move(prefix + "set", "C", prefix + "set1");//return true
setOps.remove(prefix + "set", "C", "D");//删除
// 有序的set
ZSetOperations zSetOps = stringRedisTemplate.opsForZSet();

GeoOperations geoOperations = stringRedisTemplate.opsForGeo();
// 只有jedis 和 lettuce 支持Redis Cluster。
ClusterOperations clusterOperations = stringRedisTemplate.opsForCluster();
System.out.println("====================");
}
}

Redis Cluster

Enabling Redis Cluster

集群的支持是基于非集群通讯构建的。RedisClusterConnection 是RedisConnection 的一个扩展,用来处理和Redis Cluster的通讯,转换错误信息到Spring DAO异常层。RedisClusterConnection 是通过RedisConnectionFactory 创建的,该工程的创建要依据于RedisClusterConfiguration配置。

Example 1. Sample RedisConnectionFactory Configuration for Redis Cluster

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
public class ClusterConfigurationProperties {

/*
* spring.redis.cluster.nodes[0] = 127.0.0.1:7379
* spring.redis.cluster.nodes[1] = 127.0.0.1:7380
* ...
*/
List<String> nodes;

/**
* Get initial collection of known cluster nodes in format {@code host:port}.
*
* @return
*/
public List<String> getNodes() {
return nodes;
}

public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}

@Configuration
public class AppConfig {

/**
* Type safe representation of application.properties
*/
@Autowired ClusterConfigurationProperties clusterProperties;

public @Bean RedisConnectionFactory connectionFactory() {

return new JedisConnectionFactory(
new RedisClusterConfiguration(clusterProperties.getNodes()));
}
}

通过配置文件配置:

1
2
3
# 这个初始化的配置为驱动库指定了一组初始化集群节点。集群可以在线修改配置,但修改结果只会保存在本驱动的内存中,不会写入到配置文件中。
spring.redis.cluster.nodes: Comma delimited list of host:port pairs.
spring.redis.cluster.max-redirects: Number of allowed cluster redirections.

参考:官方文档中文版(个人站)

参考:官方文档中文版(官网)

参考:官方文档中文版(官网doc)

参考:SpringBoot的配置文件

RedisTemplate源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142

public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
// 是否支持事物,必须通过设置setEnableTransactionSupport(true)显式地为使用中的每个RedisTemplate启用事务支持这将强制将正在使用的重断开绑定到当前线程触发MULTI。如果事务完成时没有错误,则调用EXEC,否则丢弃。一旦在MULTI中,RedisConnection将对写操作进行排队,所有只读操作(例如键)都将通过管道传输到一个新的(非线程绑定的)RedisConnection。
private boolean enableTransactionSupport = false;
// 有一个this.createRedisConnectionProxy代理
private boolean exposeConnection = false;
// afterPropertiesSet()方法用到,此方法允许bean实例在设置了所有bean属性后执行总体配置的验证和最终初始化。
private boolean initialized = false;
// 默认序列化
private boolean enableDefaultSerializer = true;
// 默认使用JdkSerializationRedisSerializer序列化
@Nullable
private RedisSerializer<?> defaultSerializer;
@Nullable
private ClassLoader classLoader;
// 默认使用 this.defaultSerializer 序列化
@Nullable
private RedisSerializer keySerializer = null;
// 默认使用 this.defaultSerializer 序列化
@Nullable
private RedisSerializer valueSerializer = null;
// 默认使用 this.defaultSerializer 序列化
@Nullable
private RedisSerializer hashKeySerializer = null;
// 默认使用 this.defaultSerializer 序列化
@Nullable
private RedisSerializer hashValueSerializer = null;
// 默认使用 new StringRedisSerializer(StandardCharsets.UTF_8)
private RedisSerializer<String> stringSerializer = RedisSerializer.string();
// 用于执行Redis脚本的ScriptExecutor
@Nullable
private ScriptExecutor<K> scriptExecutor;
@Nullable
private ValueOperations<K, V> valueOps;
@Nullable
private ListOperations<K, V> listOps;
@Nullable
private SetOperations<K, V> setOps;
@Nullable
private ZSetOperations<K, V> zSetOps;
@Nullable
private GeoOperations<K, V> geoOps;
@Nullable
private HyperLogLogOperations<K, V> hllOps;

public RedisTemplate() {
}

public void afterPropertiesSet() ;
// 在连接中执行给定的操作对象
@Nullable
public <T> T execute(RedisCallback<T> action) ;
// 在连接中执行给定的操作对象,该对象可以公开,也可以不公开。
@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection) ;
// 在可公开或不可公开的连接中执行给定的操作对象。此外,可以对连接进行流水线操作。注意,管道的结果被丢弃(使其适合只写的场景)。有道翻译
@Nullable
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline);
// 执行一个Redis会话。允许在同一个会话中执行多个操作,通过RedisOperations.multi()和RedisOperations.watch(Collection)操作启用“事务”功能。
public <T> T execute(SessionCallback<T> session) ;
//在管道连接上执行给定的Redis会话。允许流水线处理事务。注意,回调函数不能返回非空值,因为它被管道覆盖。
public List<Object> executePipelined(SessionCallback<?> session) ;
// 自定义序列化
public List<Object> executePipelined(SessionCallback<?> session, @Nullable RedisSerializer<?> resultSerializer) ;
// 在管道连接上执行给定的操作对象,返回结果。注意,回调函数不能返回非空值,因为它被管道覆盖。此方法将使用默认的序列化器反序列化结果
public List<Object> executePipelined(RedisCallback<?> action) ;
// 自定义序列化
public List<Object> executePipelined(RedisCallback<?> action, @Nullable RedisSerializer<?> resultSerializer) ;
public <T> T execute(RedisScript<T> script, List<K> keys, Object... args) ;
public <T> T execute(RedisScript<T> script, RedisSerializer<?> argsSerializer, RedisSerializer<T> resultSerializer, List<K> keys, Object... args) ;
public <T extends Closeable> T executeWithStickyConnection(RedisCallback<T> callback) ;
private Object executeSession(SessionCallback<?> session) {
return session.execute(this);
}
protected RedisConnection createRedisConnectionProxy(RedisConnection pm) ;
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) ;
@Nullable
protected <T> T postProcessResult(@Nullable T result, RedisConnection conn, boolean existingConnection) ;
public boolean isExposeConnection();
public void setExposeConnection(boolean exposeConnection) ;
public boolean isEnableDefaultSerializer() ;
public void setEnableDefaultSerializer(boolean enableDefaultSerializer) ;
@Nullable
public RedisSerializer<?> getDefaultSerializer() ;
public void setDefaultSerializer(RedisSerializer<?> serializer) ;
public void setKeySerializer(RedisSerializer<?> serializer) ;
public RedisSerializer<?> getKeySerializer() ;
public void setValueSerializer(RedisSerializer<?> serializer) ;
public RedisSerializer<?> getValueSerializer() ;
public RedisSerializer<?> getHashKeySerializer() ;
public void setHashKeySerializer(RedisSerializer<?> hashKeySerializer) ;
public RedisSerializer<?> getHashValueSerializer() ;
public void setHashValueSerializer(RedisSerializer<?> hashValueSerializer) ;
public RedisSerializer<String> getStringSerializer() ;
public void setStringSerializer(RedisSerializer<String> stringSerializer) ;
public void setScriptExecutor(ScriptExecutor<K> scriptExecutor) ;
private byte[] rawKey(Object key) ;
private byte[] rawString(String key) ;
private byte[] rawValue(Object value) ;
private byte[][] rawKeys(Collection<K> keys) ;
private K deserializeKey(byte[] value) ;
@Nullable
private List<Object> deserializeMixedResults(@Nullable List<Object> rawValues, @Nullable RedisSerializer valueSerializer, @Nullable RedisSerializer hashKeySerializer, @Nullable RedisSerializer hashValueSerializer) ;
private Set<?> deserializeSet(Set rawSet, @Nullable RedisSerializer valueSerializer) ;
private Set<TypedTuple<V>> convertTupleValues(Set<Tuple> rawValues, @Nullable RedisSerializer valueSerializer) ;
public List<Object> exec() ;
public List<Object> exec(RedisSerializer<?> valueSerializer) ;
protected List<Object> execRaw() ;
public Boolean delete(K key) ;
public Long delete(Collection<K> keys) ;
public Boolean unlink(K key) ;
public Long unlink(Collection<K> keys) ;
public Boolean hasKey(K key) ;
public Long countExistingKeys(Collection<K> keys) ;
public Boolean expire(K key, long timeout, TimeUnit unit) ;
public Boolean expireAt(K key, Date date) ;
public void convertAndSend(String channel, Object message);
public Long getExpire(K key) ;
public Long getExpire(K key, TimeUnit timeUnit) ;
public Set<K> keys(K pattern) ;
public Boolean persist(K key) ;
public Boolean move(K key, int dbIndex) ;
public K randomKey() ;
public void rename(K oldKey, K newKey) ;
public Boolean renameIfAbsent(K oldKey, K newKey) ;
public DataType type(K key);
public byte[] dump(K key) ;
public void restore(K key, byte[] value, long timeToLive, TimeUnit unit, boolean replace) ;
public void multi() ;
public void discard() ;
public void watch(K key) ;
public void watch(Collection<K> keys) ;
public void unwatch() ;
public List<V> sort(SortQuery<K> query) ;
public <T> List<T> sort(SortQuery<K> query, @Nullable RedisSerializer<T> resultSerializer) ;
public <T> List<T> sort(SortQuery<K> query, BulkMapper<T, V> bulkMapper) ;
public <T, S> List<T> sort(SortQuery<K> query, BulkMapper<T, S> bulkMapper, @Nullable RedisSerializer<S> resultSerializer) ;
public Long sort(SortQuery<K> query, K storeKey) ;
public void killClient(String host, int port);
public List<RedisClientInfo> getClientList() ;
public void slaveOf(String host, int port) ;
public void slaveOfNoOne() ;

完成的cache + redis application.yml配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spring:
cache:
type: REDIS
redis:
cache-null-values: false
time-to-live: 600000ms
use-key-prefix: true
cache-names: userCache,allUsersCache
redis:
host: 127.0.0.1
port: 6379
database: 0
lettuce:
shutdown-timeout: 200ms
pool:
max-active: 7
max-idle: 7
min-idle: 2
max-wait: -1ms

本文地址:SpringBoot整合Redis及Redis简介

推荐

Spring cache+ redis与redis 的比较

本文地址: https://github.com/maxzhao-it/blog/post/45583/