简要
spring对redis做了封装,需要引入的jar包为 spring-data-redis.jar.
spring-data-redis.1.x.jar跟spring-data-redis.2.x.jar在实现上有所不同。
本文基于1.8.12与2.x两个版本,分析如何在spring项目中使用、配置redis。
代码实现
1.8.12版本
JedisConnectionFactory类图
根据JedisConnectionFactory.java类图可以看出,提供了不同入参的构造器,以满足Redis三种模式(单机模式、哨兵模式、集群模式)的连接配置。
其中:
JedisConnectionFactory(); 采用默认localhost:6379获取单机模式连接;
JedisConnectionFactory(JedisPoolConfig poolConfig); 采用localhost:6379获取单机模式连接,并使用自定义连接池配置;
JedisConnectionFactory(JedisShardInfo shardInfo); shardInfo封装了单机模式连接所需属性以及ssl连接配置;
【上代码】
单机模式:
@Log4j2
@Service
public class RedisConnectionFactory1 {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private int redisPort;
@Value("${spring.data.redis.password}")
private String redisPassword;
private StringRedisTemplate redisTemplate;
@PostConstruct
public void getJedisConnection(){
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
jedisConnectionFactory.setHostName(redisHost);
jedisConnectionFactory.setPort(redisPort);
//使用连接池配置
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(20);//连接池最大连接数
jedisPoolConfig.setMaxIdle(10);//连接池最大空闲数
jedisConnectionFactory.setPoolConfig(jedisPoolConfig);
jedisConnectionFactory.setUsePool(true);//可以不写,默认值为true
jedisConnectionFactory.afterPropertiesSet();//必须有这一步,用来初始化一些默认参数,否则连接会失败
redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
redisTemplate.afterPropertiesSet();
}
public StringRedisTemplate getRedisTemplate(){
return this.redisTemplate;
}
}
测试代码:
1 @Log4j2
2 @RunWith(SpringRunner.class)
3 @SpringBootTest(classes = ServerApplication.class)
4 public class RedisConnectionTest {
5 @Autowired
6 private RedisService redisService;
7 @Autowired
8 private MyRedisConnection myRedisConnection;
9
10
11 public void test1(){
12 RedisConnection connection = myRedisConnection.getRedisTemplate().getConnectionFactory().getConnection();
13
14 connection.set("test".getBytes(),"value".getBytes());
15
16 Set<byte[]> keys = connection.keys("*".getBytes());
17
18 for(byte[] b: keys){
19 System.out.println(new String(b));
20 }
21
22 System.out.println(new String(connection.get("test".getBytes())));
23
24 }
25 //多线程测试连接池
26 @Test
27 public void test2(){
28 //启动1000个线程
29 for (int i = 0; i < 100; i++) {
30 ClientThread t = new ClientThread(i);
31 t.start();
32 }
33 try {
34 Thread.sleep(6000);
35 } catch (InterruptedException e) {
36 e.printStackTrace();
37 }
38 }
39
40 //线程类
41 class ClientThread extends Thread {
42 int i;
43
44 ClientThread(int i) {
45 this.i = i;
46 }
47
48 public void run() {
49 RedisConnection connection = myRedisConnection.getRedisTemplate().getConnectionFactory().getConnection();
50 Date date = new Date();
51 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
52 String time = sdf.format(date);
53 connection.set(("key" + i).getBytes(), time.getBytes());
54 try {
55 //每次睡眠一个随机时间
56 Thread.sleep((int) (Math.random() * 5000));
57 String foo = new String(connection.get(("key" + i).getBytes()));
58 System.out.println("【输出>>>>】key:" + foo + " 第:" + i + "个线程");
59 } catch (InterruptedException e) {
60 e.printStackTrace();
61 }finally {
62 if (connection != null) {
63 connection.close();
64 }
65 }
66 }
67 }
}
哨兵模式:
了解哨兵模式之前,需要先了解【主从复制模式】。
主从复制模式是redis集群模式中的一种,其他两种分别为:哨兵模式、cluster模式。由一个master server + n个slave server构成,slave server启动并连接到master后,会与master完成一次同步过程,master将整个数据库文件传输给slave,slave将接收到数据库文件保存到内存中。自此简单的主从复制模式已经完成,master server可以将客户端的读写请求交给slave server完成,客户端也可以选择进行读写分离,以提高redis的相应效率。
哨兵模式是基于主从复制模式的,只需要再启动一个redis服务,用来监听master server的服务状态,并在master server宕机后,自动选举一个slave node变为新的master,主从模式就变为哨兵模式。用来监听的redis服务即为“哨兵”。为了整个redis服务的正常运行,我们可以启动多个哨兵进程,以防单个哨兵进程的判断失误情况发生。
@Log4j2
@Service
public class MyRedisConnection {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private int redisPort;
@Value("${spring.data.redis.password}")
private String redisPassword;
private StringRedisTemplate redisTemplate;
@PostConstruct
public void getJedisConnection(){
log.info("init。。。。");
/**
* master server:127.0.0.1:6380
* slave server1:127.0.0.1:6381
* slave server2:127.0.0.1:6382
*
* sentinel server:127.0.0.1:26381
*
* 将6380kill掉后,客户端不需要切换ip
*/
Set<String> sentinelNode = Sets.newHashSet();
sentinelNode.add("127.0.0.1:26381");
RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration("mymaster",sentinelNode);
//使用连接池配置
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(20);
jedisPoolConfig.setMaxIdle(10);
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(sentinelConfiguration,jedisPoolConfig);
jedisConnectionFactory.afterPropertiesSet();//必须有这一步,否则连接会失败
redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
redisTemplate.afterPropertiesSet();
}
public StringRedisTemplate getRedisTemplate(){
return this.redisTemplate;
}
}
2.0.5版本
对比1.8.16版本,可以发现,主要的区别是封装类的替换。1.8.16中用来配封装客户端连接配置的JedisShardInfo.java类,被JedisClientConfiguration替换,封装的更加简洁明了。
在编码上,旧版本中setHostName(String);setPassword(String);setPort(Int)等set方法,在新版本中都是@Deprecated标注的,这就要求我们寻求更佳的实现方法。
以下,仅用单机+连接池方式为示例:
@Log4j2
@Service
public class MyRedisConnection {
@Value("${spring.data.redis.host}")
private String redisHost;
@Value("${spring.data.redis.port}")
private int redisPort;
@Value("${spring.data.redis.password}")
private String redisPassword;
private StringRedisTemplate redisTemplate;
@PostConstruct
public void getJedisConnection(){
log.info("init。。。。");
RedisStandaloneConfiguration standaloneConfiguration = new RedisStandaloneConfiguration(redisHost,redisPort);
/**使用连接池配置。
*
* 为了获取一个单机+连接池的redis,只能通过JedisConnectionFactory(standaloneConfiguration,jedisClientConfiguration);这个构造器
* 现在就要想方设法实例化JedisClientConfiguration对象
*/
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(20);
jedisPoolConfig.setMaxIdle(10);
/**
* 非常复杂!!!并且通过强转得到的JedisPoolingClientConfigurationBuilder对象,其实是很不规范的
*
* 因为JedisClientConfiguration.builder();返回的是JedisClientConfigurationBuilder类型,
* 之所以能强转是因为方法内返回的是DefaultJedisClientConfigurationBuilder类型,
* 而DefaultJedisClientConfigurationBuilder同时实现了JedisClientConfigurationBuilder、JedisPoolingClientConfigurationBuilder两个接口
*/
JedisClientConfiguration.JedisPoolingClientConfigurationBuilder jpcb =
(JedisClientConfiguration.JedisPoolingClientConfigurationBuilder)JedisClientConfiguration.builder();
jpcb.poolConfig(jedisPoolConfig);
JedisClientConfiguration jedisClientConfiguration = jpcb.build();
//
JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(standaloneConfiguration,jedisClientConfiguration);
redisTemplate = new StringRedisTemplate();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
redisTemplate.afterPropertiesSet();
}
public StringRedisTemplate getRedisTemplate(){
return this.redisTemplate;
}
}