1. 概述
默认有三种方式连接redis.
- 第一种:jedis—传统的项目–ssm
- 第二种:lettuce:---->刚出现没有多久就被springboot整合进来。
- 第三种:springboot连接redis
2. 使用Jedis来连接redis
Jedis Client是Redis官网推荐的一个面向java客户端,库文件实现了对各类API进行封装调用。
- 引入jedis的jar包
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.3.1</version>
</dependency>
- 编写连接代码
public class JedisTest {
public static void main(String[] args) {
//1.或的Connection,通过ip和断开
Jedis jedis=new Jedis("127.0.0.1",6379);
//2.指定访问redis的密码(我的redis没有设置密码)
//jedis.auth("111111");
//3.测试连接是否成功
System.out.println(jedis.ping());
}
}
可以发现连接成功
- 测试操作redis
public class JedisTest {
public static void main(String[] args) {
//1.或的Connection,通过ip和断开
Jedis jedis=new Jedis("127.0.0.1",6379);
//2.指定访问redis的密码(我的redis没有设置密码)
//jedis.auth("111111");
//3.测试连接是否成功
System.out.println(jedis.ping());
//4.操作redis
jedis.set("k1","v1");
jedis.set("k2","v2");
Set<String> keys = jedis.keys("*");
keys.forEach(System.out::println);
}
}
3. 使用Lettuce来连接redis
Lettuce是一个Redis的Java驱动包。jedis和Lettuce都是Redis的客户端,它们都可以连接Redis服务器,但是在SpringBoot2.0之后默认使用的Lettuce这个客户端来连接Redis服务器,因为当使用Jedis客户端连接Redis服务器的时候,每个线程都要拿自己常见的Jedis实例取连接Redis客户端,当有很多个线程的时候,不仅开销大需要反复的创建关闭一个Jedis连接,而且线程也是不安全的,一个线程通过Jedis实例更改Redis服务器中的额数据之后会影响另一个线程。但是如果使用Lettuce这个客户端来连接Redis服务器的时候,就不会出现上面的问题,Lettuce底层使用的是Netty,当有多个线程都需要连接Redis服务器的时候,可以保证只创建一个Lettuce连接,使所有的线程都共享这一个Lettuce连接,这样可以减少创建关闭一个Lettuce连接时候的开销,而且这种方式也是线程安全的,不会出现一个线程通过Lettuce更改Redis服务器中的数据之后影响另一个线程的情况。
- 导入依赖包
<dependency>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</dependency>
- 测试代码
public class LettuceTest {
public static void main(String[] args) {
//1.使用构建器链式编程来builder我们的RedisURI
RedisURI uri = RedisURI.builder().redis("127.0.0.1").withPort(6379).build() /*.withAUthentication("username","password")*/;
//2.创建连接客户端
RedisClient redisClient = RedisClient.create(uri);
StatefulRedisConnection<String, String> connect = redisClient.connect();
//3.创建操作的命令
RedisCommands<String, String> commands = connect.sync();
//String
List<String> keys = commands.keys("*");
keys.forEach(System.out::println);
//string
commands.set("k5","v5");
System.out.println(commands.get("k5"));
//4. 关闭释放资源
connect.close();
}
}
4. 使用Redis Template来连接Redis
- 单机模式
导入pom
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
注意
spring-boot-starter-data-redis
默认就引入了lettuce,为了防止版本冲突,可以删除原先的版本
配置
spring:
redis:
host: 127.0.0.1
port: 6379
#password:
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
创建redis的配置类
@Configuration
public class RedisConf {
//这里先不配置,测试一下待会的错误
}
创建Service接口
public interface OrderService {
public void addOrder();
public String getOrderById(String keys);
}
@Service
public class OrderServiceImpl implements OrderService {
public static final String ORDER_KEY="ord:";
@Autowired
private RedisTemplate redisTemplate;
@Override
public void addOrder() {
int kyeId= ThreadLocalRandom.current().nextInt(1000)+1;
String serialNo= UUID.randomUUID().toString();
String key=ORDER_KEY+kyeId;
String value="京东订单"+serialNo;
System.out.println(value);
redisTemplate.opsForValue().set(key,value);
}
@Override
public String getOrderById(String keys) {
return (String) redisTemplate.opsForValue().get(keys);
}
}
创建Controller
RestController
@Slf4j
public class OrderController {
@Autowired
private OrderService orderService;
@RequestMapping(value = "/order/add",method = RequestMethod.POST)
public void addOrder(){
orderService.addOrder();
}
@RequestMapping(value = "/order/{keyId}",method = RequestMethod.GET)
public void getOrder(@PathVariable String keyId){
System.out.println(orderService.getOrderById("ord:"+keyId));
}
}
访问添服务
首先redis数据库内容增加了:
可以发现是乱码
idea也有输出
出现乱码的原因归根到底是序列话的问题,Redis的键和值都是Spring提供的Serializer序列化到数据库的,RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerialzer,所以我们要使用StringRedisSerialzer来进行序列化。有一下解决方案
(1)使用StringRedisTemplate
@Service
public class OrderServiceImpl implements OrderService {
public static final String ORDER_KEY="ord:";
// @Autowired
// private RedisTemplate redisTemplate;
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public void addOrder() {
int kyeId= ThreadLocalRandom.current().nextInt(1000)+1;
String serialNo= UUID.randomUUID().toString();
String key=ORDER_KEY+kyeId;
String value="京东订单"+serialNo;
System.out.println(value);
stringRedisTemplate.opsForValue().set(key,value);
}
@Override
public String getOrderById(String keys) {
return stringRedisTemplate.opsForValue().get(keys);
}
}
可以看到现在就正常了
查询服务也可以查到
(2)配置Redis配置类
@Configuration
public class RedisConf {
@Bean
public RedisTemplate<String,Object> redisTemplate(LettuceConnectionFactory lettuceConnectionFactory){
RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
redisTemplate.setConnectionFactory(lettuceConnectionFactory);
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setHashValueSerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
- 集群模式
启动redis集群(集群搭建可以看上篇文章)
改写配置文件
spring:
redis:
cluster:
max-redirects:3
nodes: 192.168.111.175:6381, 192.168.111.175:6382, 192.168.111.172:6383, 192.168.111.172:6384,192.168.111.181:6385, 192.168.111.181:6386,
lettuce:
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0
通过微服务访问redis集群
访问方式和单机模式一样的,集群对客户是透明的。
常见问题
:
SpringBoot客户端没有动态感知到RedisCluster的最新集群信息例如,Redis Cluster集群部署采用的3主3从拓补结构,数据读写访问master节点,slave节点负责备份,当master宕机后主从切换成功,redis手动ok,但是存在两个经典bug:
出现上面故障的原因是,SpringBoot2.x版本,redis默认使用的连接池是Lettuce,当redis集群节点发生变化后,Lettuce默认是不会刷新节点拓补结构的。
上面问题的解决方案方案是:
- 排除lettuce采用jedis(不推荐)
2. 重写连接工程实例(改源码,极度不推荐)
3. 刷新节点集群的拓补结构感应(推荐)
spring:
redis:
cluster:
max-redirects:3
nodes: 192.168.111.175:6381, 192.168.111.175:6382, 192.168.111.172:6383, 192.168.111.172:6384,192.168.111.181:6385, 192.168.111.181:6386,
lettuce:
cluster:
#这个是默认关闭的,这里设置为2s刷新
refresh:
adaptive: true;
period: 2000
pool:
max-active: 8
max-wait: -1ms
max-idle: 8
min-idle: 0