redis集群的总结、搭建、 springboot整合测试
redis集群的相关总结,测试,springboot整合
一,redis集群总结:主从模式、哨兵模式和Cluster集群
- 主从模式:一主多从,不能自动切换主,每台都是全量日志
- 哨兵模式:一主多从,主有故障的时候,哨兵投票切换主,是主从模式的升级版,切换主的时候,是不可用状态,每台都是全量日志
- Cluster集群:推荐是3主3从,工作方式:key–槽位—节点, 每台只保存该槽位的key, 故障案例:主2挂的时候,从2去顶替,这时候,该节点对应的key可读不可写,如果主2和从2都挂了,整个集群不可用
二,Cluster集群搭建
1.开三台虚拟机,系统为CentOS 7.6 64位。先配置好第一台的环境(安装好redis和集群配置,没有gcc的,需要安装gcc,下面的步骤是配置详情),其余两台克隆即可!
2.安装redis,本人用的redis-7.0.14.tar.gz版本
- 检查是否安装了gcc(redis的编译需要它):gcc -v ,没有的话先安装这个:yum install gcc-c++
- 创建工作目录,比如/usr/local/redis,把reids包放进去,并解压:tar -zxvf redis-7.0.14.tar.gz
- 进入到解压后的目录(redis-7.0.14)进行编译:make
- 进入redis-7.0.14/src 文件目录下进行安装:make install
3.添加集群的配置,比如3主3从的端口分配为:8001,8002,8003,8004,8005,8006
- 创建工作目录,比如/usr/local/redis-cluster-work
- 在redis-cluster-work里创建2个工作目录,8001和8002
- 在8001里面,创建redis.conf配置文件:touch redis.conf
- 编写redis.conf文件(vim redis.conf),内容如下,写完之后wq保存:
# 修改为后台启动
daemonize yes
# 修改端口号
port 8001
# 指定数据文件存储位置
dir /usr/local/redis-cluster-work/8001/
# 开启集群模式
cluster-enabled yes
# 集群节点信息文件配置
cluster-config-file nodes-8001.conf
# 集群节点超时间
cluster-node-timeout 15000
# 去掉bind绑定地址
# bind 127.0.0.1
# 关闭保护模式
protected-mode no
# 开启aof模式持久化
appendonly yes
# 设置连接Redis需要密码123(选配)
requirepass 123456
# 设置Redis节点与节点之间访问需要密码123(选配)
masterauth 123456
**如果是云服务器,需要改一下配置,因为云服务器涉及到公私地址的问题,上面的配置会发生连接的公网地址,当时集群内找的是内部地址
cluster-announce-ip 公网地址
bind 0.0.0.0
- 把这个文件,复制到8002的目录,修改里面端口为本目录的端口
- 关闭防护墙(为了测试方便,开着的情况需要放开这6个端口,并且放开这6个端口加10000之后的端口,加1万的端口是通信端口):systemctl stop firewalld
- 克隆2台,并修改配置文件为对应的端口
- 把这六台redis启动:
redis-server /usr/local/redis-cluster-work/8001/redis.conf
redis-server /usr/local/redis-cluster-work/8002/redis.conf
redis-server /usr/local/redis-cluster-work/8003/redis.conf
redis-server /usr/local/redis-cluster-work/8004/redis.conf
redis-server /usr/local/redis-cluster-work/8005/redis.conf
redis-server /usr/local/redis-cluster-work/8006/redis.conf
- 启动之后,用ps -ef | grep redis验证一下,集群启动是带[cluster]标志的:
[root@localhost 8005]# ps -ef | grep redis
root 18478 1 0 08:45 ? 00:01:11 redis-server *:8005 [cluster]
root 18484 1 0 08:45 ? 00:01:11 redis-server *:8006 [cluster]
root 22963 18402 0 15:10 pts/1 00:00:00 grep --color=auto redis
- 至此6台redis已启动成功,下面开始启动集群(咱这是5.0之上的版本,用客户端操作即可):
10.1随机主从分配的方式:
redis-cli -a 123456 --cluster create --cluster-replicas 1 192.168.208.128:8001 192.168.208.128:8002 192.168.208.129:8003 192.168.208.129:8004 192.168.208.130:8005 192.168.208.130:8006
10.2指定主从分配的方式,需要先指定3台主,然后再指定3台从(需要主的id):
先指定3台主
redis-cli -a 123456 --cluster create 192.168.208.128:8001 192.168.208.129:8003 192.168.208.130:8005
[root@localhost redis-cluster-work]# redis-cli -a 123456 --cluster create 192.168.208.128:8001 192.168.208.129:8003 192.168.208.130:8005
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Performing hash slots allocation on 3 nodes...
Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
M: 21bd38c67330b802ee88b7fe0843509aa2afb2ad 192.168.208.128:8001
slots:[0-5460] (5461 slots) master
M: eeb3577336a2d9ade1a195364abca9e96e3cbf30 192.168.208.129:8003
slots:[5461-10922] (5462 slots) master
M: a2e8be14c508ad72c62a0efb3ee96386188fb626 192.168.208.130:8005
slots:[10923-16383] (5461 slots) master
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
>>> Performing Cluster Check (using node 127.0.0.1:8001)
M: 21bd38c67330b802ee88b7fe0843509aa2afb2ad 192.168.208.128:8001
slots:[0-5460] (5461 slots) master
M: a2e8be14c508ad72c62a0efb3ee96386188fb626 192.168.208.130:8005
slots:[10923-16383] (5461 slots) master
M: eeb3577336a2d9ade1a195364abca9e96e3cbf30 192.168.208.129:8003
slots:[5461-10922] (5462 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
[root@localhost redis-cluster-work]#
再指定3台从,里面的21bd38c67330b802ee88b7fe0843509aa2afb2ad是主的id,通过CLUSTER NODES可以查看每台的id:
[root@localhost redis-cluster-work]# redis-cli -a 123456 -c -h 192.168.208.128 -p 8001
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.208.128:8001> cluster nodes
a2e8be14c508ad72c62a0efb3ee96386188fb626 192.168.208.130:8005@18005 master - 0 1706938093285 3 connected 10923-16383
eeb3577336a2d9ade1a195364abca9e96e3cbf30 192.168.208.129:8003@18003 master - 0 1706938092282 2 connected 5461-10922
21bd38c67330b802ee88b7fe0843509aa2afb2ad 192.168.208.128:8001@18001 myself,master - 0 1706938092000 1 connected 0-5460
192.168.208.128:8001>
redis-cli -a 123456 --cluster add-node 192.168.208.128:8002 192.168.208.128:8001 --cluster-slave --cluster-master-id 21bd38c67330b802ee88b7fe0843509aa2afb2ad
说明:上述命令把8002节点加入到8001节点(这里任意写一个集群中节点的端口即可)的集群中,并且当做node_id为21bd38c67330b802ee88b7fe0843509aa2afb2ad的从节点。如果不指定 --cluster-master-id 会随机分配到任意一个主节点
[root@localhost redis-cluster-work]# redis-cli -a 123456 --cluster add-node 192.168.208.128:8002 192.168.208.128:8001 --cluster-slave --cluster-master-id 21bd38c67330b802ee88b7fe0843509aa2afb2ad
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
>>> Adding node 192.168.208.128:8002 to cluster 192.168.208.128:8001
>>> Performing Cluster Check (using node 192.168.208.128:8001)
M: 21bd38c67330b802ee88b7fe0843509aa2afb2ad 192.168.208.128:8001
slots:[0-5460] (5461 slots) master
M: a2e8be14c508ad72c62a0efb3ee96386188fb626 192.168.208.130:8005
slots:[10923-16383] (5461 slots) master
M: eeeb3577336a2d9ade1a195364abca9e96e3cbf30 192.168.208.129:8003
slots:[5461-10922] (5462 slots) master
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 192.168.208.128:8002 to make it join the cluster.
Waiting for the cluster to join
>>> Configure node as replica of 192.168.208.130:8005.
[OK] New node added correctly.
[root@localhost redis-cluster-work]#
- 至此集群已配置好,下面的命令,可以查看集群情况:
11.1查看集群信息:redis-cli -a 123456 --cluster info 192.168.208.128:8001
[root@localhost src]# redis-cli -a 123456 --cluster info 192.168.208.128:8001
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
192.168.208.128:8001 (21bd38c6...) -> 0 keys | 5461 slots | 1 slaves.
192.168.208.130:8005 (35dabf60...) -> 0 keys | 5461 slots | 1 slaves.
192.168.208.129:8003 (29b2688d...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.
11.2连接集群
# -a 密码认证
# -c 连接集群
# -h 集群中任意一个Redis节点IP
# -p 集群中任意一个Redis节点端口
redis-cli -a 123456 -c -h 192.168.208.128 -p 8001
11.3登录到客户端之后的查看集群信息:CLUSTER INFO
192.168.208.128:8001> CLUSTER INFO
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:6
cluster_my_epoch:1
cluster_stats_messages_ping_sent:23854
cluster_stats_messages_pong_sent:25111
cluster_stats_messages_fail_sent:5
cluster_stats_messages_sent:48970
cluster_stats_messages_ping_received:25111
cluster_stats_messages_pong_received:23853
cluster_stats_messages_fail_received:2
cluster_stats_messages_received:48966
total_cluster_links_buffer_limit_exceeded:0
11.4登录到客户端之后的查看集群信息:CLUSTER NODES
192.168.208.128:8001> CLUSTER NODES
21bd38c67330b802ee88b7fe0843509aa2afb2ad 192.168.208.128:8001@18001 myself,master - 0 1704871951000 1 connected 0-5460
febaa75d1df7fe08abf2ac2a3a21331efc883540 192.168.208.128:8002@18002 slave 35dabf604f98dc37da4b1d231b77dd362310901c 0 1704871950374 5 connected
77e839b6c51d92bb98afd6aa84087a9b183a8661 192.168.208.130:8006@18006 slave 29b2688d2fed7f3cdc40b2743f852dc4e5451a67 0 1704871949000 3 connected
35dabf604f98dc37da4b1d231b77dd362310901c 192.168.208.130:8005@18005 master - 0 1704871951403 5 connected 10923-16383
300713f6e6fdd1007131ac82b2f40b194e3e7651 192.168.208.129:8004@18004 slave 21bd38c67330b802ee88b7fe0843509aa2afb2ad 0 1704871950000 1 connected
29b2688d2fed7f3cdc40b2743f852dc4e5451a67 192.168.208.129:8003@18003 master - 0 1704871952425 3 connected 5461-10922
- 集群伸缩,比如增加2个节点,一主一从,需要先把主添加到集群,然后再分配槽位,最后把从加入到主,具体命令以后更新~~
三, springboot整合测试
先说一下客户端:Jedis 和 Lettuce 是 Java 操作 Redis 的客户端,在 Spring Boot 1.x 版本默认使用的是 jedis ,而在 Spring Boot 2.x 版本默认使用的就是Lettuce:
- Jedis 非线程安全的,配合连接池使用,为每个Jedis实例增加物理连接,可以达到多线程使用的目的
- Lettuce 基于Netty的,线程安全的
那么,整合测试是这两种客户端都实现,并对比测试情况
1, springboot整合之Jedis客户端方式
项目依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
配置文件
#RedisCluster集群节点及端口信息
spring.redis.cluster.nodes=192.168.208.128:8001,192.168.208.128:8002,192.168.208.129:8003,192.168.208.129:8004,192.168.208.130:8005,192.168.208.130:8006
#Redis密码
spring.redis.cluster.password=123456
#redis cluster只使用db0
spring.redis.cluster.index=0
#以毫秒为单位的连接超时时间
spring.redis.cluster.timeout=10000
#在群集中执行命令时要遵循的最大重定向数目
spring.redis.cluster.max-redirects=5
#Redis连接池在给定时间可以分配的最大连接数。使用负值无限制
spring.redis.cluster.jedis.pool.max-active=1000
#池中“空闲”连接的最大数量。使用负值表示无限数量的空闲连接
spring.redis.cluster.jedis.pool.max-idle=8
#目标为保持在池中的最小空闲连接数。这个设置只有在设置max-idle的情况下才有效果
spring.redis.cluster.jedis.pool.min-idle=5
#连接分配在池被耗尽时抛出异常之前应该阻塞的最长时间量(以毫秒为单位)。使用负值可以无限期地阻止
spring.redis.cluster.jedis.pool.max-wait=1000
下面是三个配置类
读配置文件的ClusterRedisProperties类:
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ClusterRedisProperties {
@Value("${spring.redis.cluster.timeout}")
private Integer redisTimeout;
@Value("${spring.redis.cluster.jedis.pool.max-active}")
private Integer poolMaxActive;
@Value("${spring.redis.cluster.jedis.pool.max-idle}")
private Integer poolMaxIdle;
@Value("${spring.redis.cluster.jedis.pool.min-idle}")
private Integer poolMinIdle;
@Value("${spring.redis.cluster.jedis.pool.max-wait}")
private Integer poolMaxWait;
@Value("${spring.redis.cluster.nodes}")
private String clusterNodes;
@Value("${spring.redis.cluster.max-redirects}")
private Integer clusterMaxRedirects;
@Value("${spring.redis.cluster.password}")
private String password;
public Integer getRedisTimeout() {
return redisTimeout;
}
public void setRedisTimeout(Integer redisTimeout) {
this.redisTimeout = redisTimeout;
}
public Integer getPoolMaxActive() {
return poolMaxActive;
}
public void setPoolMaxActive(Integer poolMaxActive) {
this.poolMaxActive = poolMaxActive;
}
public Integer getPoolMaxIdle() {
return poolMaxIdle;
}
public void setPoolMaxIdle(Integer poolMaxIdle) {
this.poolMaxIdle = poolMaxIdle;
}
public Integer getPoolMinIdle() {
return poolMinIdle;
}
public void setPoolMinIdle(Integer poolMinIdle) {
this.poolMinIdle = poolMinIdle;
}
public Integer getPoolMaxWait() {
return poolMaxWait;
}
public void setPoolMaxWait(Integer poolMaxWait) {
this.poolMaxWait = poolMaxWait;
}
public String getClusterNodes() {
return clusterNodes;
}
public void setClusterNodes(String clusterNodes) {
this.clusterNodes = clusterNodes;
}
public Integer getClusterMaxRedirects() {
return clusterMaxRedirects;
}
public void setClusterMaxRedirects(Integer clusterMaxRedirects) {
this.clusterMaxRedirects = clusterMaxRedirects;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
装配Bean(JedisCluster实例)的JedisClusterConfig类:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import java.util.HashSet;
import java.util.Set;
@Configuration
public class JedisClusterConfig {
@Autowired
private ClusterRedisProperties redisProperties;
@Bean
public JedisCluster getJedisCluster() {
String[] serverArray = redisProperties.getClusterNodes().split(",");
Set<HostAndPort> nodes = new HashSet<>();
for (String ipPort : serverArray) {
String[] ipPortPair = ipPort.split(":");
nodes.add(new HostAndPort(ipPortPair[0].trim(), Integer.valueOf(ipPortPair[1].trim())));
}
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(redisProperties.getPoolMaxActive());
config.setMaxIdle(redisProperties.getPoolMaxIdle());
config.setMinIdle(redisProperties.getPoolMinIdle());
config.setMaxWaitMillis(redisProperties.getPoolMaxWait());
config.setTestOnBorrow(true);
if (redisProperties.getPassword() == null || "".equals(redisProperties.getPassword())) {
return new JedisCluster(nodes, redisProperties.getRedisTimeout(), redisProperties.getRedisTimeout(), redisProperties.getClusterMaxRedirects(), config);
}else {
return new JedisCluster(nodes, redisProperties.getRedisTimeout(), redisProperties.getRedisTimeout(), redisProperties.getClusterMaxRedirects(), redisProperties.getPassword(), config);
}
}
}
自定义的工具类RedisClientTemplate:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisCluster;
@Service
public class RedisClientTemplate {
private static final Logger log = LoggerFactory.getLogger(RedisClientTemplate.class);
@Autowired
private JedisCluster jedisCluster;
public boolean setToRedis(String key, Object value) {
try {
String str = jedisCluster.set(key, String.valueOf(value));
if ("OK".equals(str))
return true;
} catch (Exception ex) {
log.error("setToRedis:{Key:" + key + ",value" + value + "}", ex);
}
return false;
}
public Object getRedis(String key) {
String str = null;
try {
str = jedisCluster.get(key);
} catch (Exception ex) {
log.error("getRedis:{Key:" + key + "}", ex);
}
return str;
}
}
测试的3个类:
Test02 :多线程并发测试
Test03 :单线程下写5000个String测试耗时
Test04 :单线程下读5000个String测试耗时
import com.wd.testRedisCluster.util.SpringUtil;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test02 {
private final static ExecutorService executorServiceHandlerData = Executors.newFixedThreadPool(200);
public static void test() {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
RedisClientTemplate redisClientTemplate = SpringUtil.getBean("redisClientTemplate", RedisClientTemplate.class);
for (int i = 1; i <= 5000; i++) {
final Integer index = i;
executorServiceHandlerData.submit(new Runnable() {
@Override
public void run() {
try {
System.out.println("t" + index + "设置结果:" + redisClientTemplate.setToRedis("t" + index, new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())));
} catch (Exception e) {
e.printStackTrace();
System.out.println("第" + index + "次异常");
}
}
});
}
try {
executorServiceHandlerData.shutdown();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
import com.wd.testRedisCluster.util.SpringUtil;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test03 {
public static void test() {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
RedisClientTemplate redisClientTemplate = SpringUtil.getBean("redisClientTemplate", RedisClientTemplate.class);
long begin = System.currentTimeMillis();
for (int i = 1; i <= 5000; i++) {
final Integer index = i;
redisClientTemplate.setToRedis("t" + index, new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()));
// System.out.println("t" + index + "设置结果:" + redisClientTemplate.setToRedis("t" + index, new SimpleDateFormat("HH:mm:ss.SSS").format(new Date())));
}
System.out.println("存储完成耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
}
}
import com.wd.testRedisCluster.util.SpringUtil;
public class Test04 {
public static void test() {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
RedisClientTemplate redisClientTemplate = SpringUtil.getBean("redisClientTemplate", RedisClientTemplate.class);
long begin = System.currentTimeMillis();
for (int i = 1; i <= 5000; i++) {
System.out.println("t" + i + "获取结果:" +redisClientTemplate.getRedis("t" + i));
}
System.out.println("读取完成耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
}
}
启动主类中测试
import com.wd.testRedisCluster.config.Test02;
import com.wd.testRedisCluster.config.Test03;
import com.wd.testRedisCluster.config.Test04;
import com.wd.testRedisCluster.util.GlobalUtil;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class StartMain {
public static void main(String[] args) {
//自定义配置文件路径
new SpringApplicationBuilder(StartMain.class)
.properties("spring.config.location=file:" + GlobalUtil.getProperty() + "application.properties")
.build()
.run(args);
// Test02.test();
Test03.test();
// Test04.test();
}
}
2, springboot整合之lettuce客户端方式
依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<!-- <version>2.3.4.RELEASE</version>-->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce pool 缓存连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.50.Final</version>
</dependency>
<!--添加fastjson包-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.16.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.16.1</version>
</dependency>
配置文件
spring.redis.cluster.nodes=192.168.208.128:8001,192.168.208.128:8002,192.168.208.129:8003,192.168.208.129:8004,192.168.208.130:8005,192.168.208.130:8006
#Redis密码
spring.redis.password=123456
#redis cluster只使用db0
spring.redis.cluster.index=0
#在群集中执行命令时要遵循的最大重定向数目
spring.redis.cluster.max-redirects=5
#以毫秒为单位的连接超时时间
spring.redis.timeout=10000
#Redis连接池在给定时间可以分配的最大连接数。使用负值无限制
spring.redis.lettuce.pool.max-active=1000
#池中“空闲”连接的最大数量。使用负值表示无限数量的空闲连接
spring.redis.lettuce.pool.max-idle=8
#目标为保持在池中的最小空闲连接数。这个设置只有在设置max-idle的情况下才有效果
spring.redis.lettuce.pool.min-idle=5
#连接分配在池被耗尽时抛出异常之前应该阻塞的最长时间量(以毫秒为单位)。使用负值可以无限期地阻止
spring.redis.lettuce.pool.max-wait=1000
配置类RedisConfig
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisSerializer<Object> serializer = redisSerializer();
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(serializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(serializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
public RedisSerializer<Object> redisSerializer() {
// 创建JSON序列化器
Jackson2JsonRedisSerializer<Object> serializer =
new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
// 必须设置,否则无法将json转化为对象,会转化为Map类型
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
ObjectMapper.DefaultTyping.NON_FINAL);
serializer.setObjectMapper(objectMapper);
return serializer;
}
}
剩下的就是3个测试类了
Test02 :多线程并发测试
Test03 :单线程下写5000个String测试耗时
Test04 :单线程下读5000个String测试耗时
import com.wd.testRedisCluster.util.SpringUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test02 {
private final static ExecutorService executorServiceHandlerData = Executors.newFixedThreadPool(200);
public static void test() {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
StringRedisTemplate redisClientTemplate = SpringUtil.getBean("stringRedisTemplate", StringRedisTemplate.class);
for (int i = 1; i <= 5000; i++) {
final Integer index = i;
executorServiceHandlerData.submit(new Runnable() {
@Override
public void run() {
try {
redisClientTemplate.opsForValue().set("t" + index, new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()));
System.out.println("t" + index + "设置结果:true" );
} catch (Exception e) {
e.printStackTrace();
System.out.println("第" + index + "次异常");
}
}
});
}
try {
executorServiceHandlerData.shutdown();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
import com.wd.testRedisCluster.util.SpringUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.text.SimpleDateFormat;
import java.util.Date;
public class Test03 {
public static void test() {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
StringRedisTemplate redisClientTemplate = SpringUtil.getBean("stringRedisTemplate", StringRedisTemplate.class);
long begin = System.currentTimeMillis();
for (int i = 1; i <= 5000; i++) {
redisClientTemplate.opsForValue().set("t" + i, new SimpleDateFormat("HH:mm:ss.SSS").format(new Date()));
// System.out.println("t" + i + "设置结果:true" );
}
System.out.println("存储完成耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
}
}
import com.wd.testRedisCluster.util.SpringUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
public class Test04 {
public static void test() {
try {
Thread.sleep(8000);
} catch (InterruptedException e) {
e.printStackTrace();
}
StringRedisTemplate redisClientTemplate = SpringUtil.getBean("stringRedisTemplate", StringRedisTemplate.class);
long begin = System.currentTimeMillis();
for (int i = 1; i <= 5000; i++) {
System.out.println("t" + i + "获取结果:" +redisClientTemplate.opsForValue().get("t" + i));
}
System.out.println("读取完成耗时:" + (System.currentTimeMillis() - begin) + "毫秒");
}
}
启动类中测试:
import com.wd.testRedisCluster.config.Test02;
import com.wd.testRedisCluster.config.Test03;
import com.wd.testRedisCluster.config.Test04;
import com.wd.testRedisCluster.util.GlobalUtil;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
@SpringBootApplication
public class StartMain {
public static void main(String[] args) {
//自定义配置文件路径
new SpringApplicationBuilder(StartMain.class)
.properties("spring.config.location=file:" + GlobalUtil.getProperty() + "application.properties")
.build()
.run(args);
// Test02.test();
Test03.test();
// Test04.test();
}
}
3, 两种客户端操作的对比:
test02中线程并发,都支持,虽然jedis是线程不安全的,但是它有连接池
test03中写的测试,lettuce比jedis快
次数 | jedis | lettuce |
---|---|---|
1次 | 3026毫秒 | 2769毫秒 |
2次 | 3419毫秒 | 3208毫秒 |
3次 | 2891毫秒 | 3188毫秒 |
4 次 | 3358毫秒 | 3151毫秒 |
5次 | 3326毫秒 | 3039毫秒 |
6 次 | 3216毫秒 | 2958毫秒 |
7次 | 3438毫秒 | 3107毫秒 |
8次 | 3532毫秒 | 3125毫秒 |
9次 | 3397毫秒 | 3169毫秒 |
10次 | 3372毫秒 | 3098毫秒 |