spring 项目中使用redis
简要
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; } }