Jedis连接工具
什么是Jedis?
测试
-
在本地主机进行测试
1、打开 Redis 服务
<!--导入jedis的包--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.2.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.66</version> </dependency>
3、测试连接
package com.zxh; import redis.clients.jedis.Jedis; public class TestPing { public static void main(String[] args) { // 1、 new Jedis 对象即可 Jedis jedis = new Jedis("127.0.0.1",6379); // jedis 所有的命令就是我们之前学习的所有指令!所以之前的指令学习很重要! System.out.println(jedis.ping()); } }
输出:
常用API
所有的api命令,就是我们对应的上面学习的指令,一个都没有变化!
package com.zxh; import com.alibaba.fastjson.JSONObject; import redis.clients.jedis.Jedis; import redis.clients.jedis.Transaction; public class TestTx { public static void main(String[] args) { // 建立连接 Jedis jedis = new Jedis("127.0.0.1", 6379); jedis.flushDB(); // 清除当前数据库 JSONObject jsonObject = new JSONObject(); jsonObject.put("username", "zxh"); jsonObject.put("age", 20); String result = jsonObject.toJSONString(); // 开启事务 Transaction multi = jedis.multi(); try { // 命令入队 multi.set("user1", result); multi.set("user2", result); int i = 1/0; // 制造异常 //执行事务 multi.exec(); } catch (Exception e) { // 抛出异常,取消事务 multi.discard(); //消事务 e.printStackTrace(); } finally { System.out.println(jedis.get("user1")); System.out.println(jedis.get("user2")); jedis.close(); // 关闭连接 } } }
结果:
SpringBoot整合
准备工作和源码分析
简介
SpringBoot 操作数据:spring-data => jpa jdbc mongodb redis!
SpringData 也是和 SpringBoot齐名的项目!
准备工作
1、创建SpringBoot项目
-
勾选支持项
2、依赖
<!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
源码分析
进入 Redis 的依赖可以找到,底层使用的就是 Jedis,不过可以看到还有另外一个 lettuce
说明
Jedis:采用的是直连,所以在多个线程操作的话,是不安全的!如果想要避免不安全,使用 Jedis pool 连接池! 更像 BIO(Blocking I/O,阻塞IO)
lettuce:采用netty,实例可以在多个线程中进行共享,不存在线程不安全的情况!可以减少线程数据量,更像 NIO(Nonblocking I/O,非阻塞IO)
NIO和BIO解释
Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高
两张图让你了解BIO和NIO的区别:
这两张图可以看出,NIO的单线程能处理连接的数量比BIO要高出很多,而为什么单线程能处理更多的连接呢?原因就是图二中出现的Selector
当一个连接建立之后,它有两个步骤要做:
-
第一步是接收客户端发过来的全部数据
-
第二步是服务端处理完请求之后返回response客户端
NIO和BIO的区别主要是在第一步:
-
在BIO中,等待客户端发数据这个过程是阻塞的,这样就造成了一个线程只能处理一个请求的情况,而机器能支持的最大线程数是有限的,这就是为什么BIO不能支持高并发的原因。
-
而NIO中,当一个Socket 建立之后,Thread 并不会阻塞接收这个 Socket,而是将这个请求交给 Selector,Selector 会不断的去遍历所有的 Socket,一旦有一个Socket 建立完成,他会通知Thread,然后 Thread 处理完数据后再返回给客户端——这个过程是不阻塞的,这样就能让一个 Thread 处理更多的请求了。
RedisAutoConfiguration自动配置源码分析
1、在SpringBoot的自动配置文件中,搜索redis可以找到对应的 Redis自动配置类
2、RedisAutoConfiguration类的源码
@Configuration(proxyBeanMethods = false) @ConditionalOnClass(RedisOperations.class) // 对应的引用配置文件 RedisProperties.class,所以说redis所有的配置可以在这里查看 @EnableConfigurationProperties(RedisProperties.class) @Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class }) public class RedisAutoConfiguration { // 默认的是 RedisTemplate,他没有过多的设置,其中的 Redis对象都是还需要序列化的! // 两个泛型都是Object类型,我们之后使用需要强转 <String, Object> // 所以@ConditionalOnMissingBean 表示我们可以自定义 RedisTemplate 来使用,那样会更加方便 @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(redisConnectionFactory); return template; } // StringRedisTemplate,由于string类型是reids中最常用的类型,所以单独提出来,方便开发人员调用 @Bean @ConditionalOnMissingBean public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory); return template; } }
配置文件
- 可以在配置文件 以 spring.redis.xxx 来进行配置
两个API使用哪个?
-
哪个好已经在上面说过了,lettuce支持高并发
-
可以看到 源码中,两个方法都传入了RedisConnectionFactory对象
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory)
进入这个接口可以看到有两个实现类
所以在配置文件中配置时需要注意:SpringBoot 2.x以后 默认使用的lettuce,不要傻傻的配置到jedis了
测试
1、导入依赖
<!-- redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
# 配置 redis,不过默认配置就是本机,端口也是6379,这里为了看的清除
spring.redis.host=127.0.0.1
spring.redis.port=6379
3、测试连接
-
在测试类中进行测试,可以直接注入 RedisTemplate 对象
package com.zxh; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @SpringBootTest class Redis02SpringbootApplicationTests { @Autowired RedisTemplate redisTemplate; @Test void contextLoads() { RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory(); RedisConnection connection = connectionFactory.getConnection(); System.out.println(connection.ping()); } }
结果:
package com.zxh; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @SpringBootTest class Redis02SpringbootApplicationTests { @Autowired RedisTemplate redisTemplate; @Test void contextLoads() { /* redisTemplate:操作不同的数据类型,api和之前的命令都是一样的 opsForValue():操作字符串,类型String opsForSet opsForHash opsForZSet opsForGeo opsForHyperLogLog */ // 除了进本的操作,我们常用的方法都可以直接通过redisTemplate操作,比如事务,和基本的 CRUD // 获取redis的连接对象 // RedisConnectionFactory connectionFactory = redisTemplate.getConnectionFactory(); // RedisConnection connection = connectionFactory.getConnection(); // System.out.println(connection.ping()); redisTemplate.opsForValue().set("name", "zxh"); System.out.println(redisTemplate.opsForValue().get("name")); } }
结果:
自定义RestTemplate
为什么要自定义?
-
首先RestTemplate 它没有过多的配置
-
默认的RestTemplate 使用的是jdk序列化,会有一些问题
关于序列化
-
我们基于上面的测试,试一下存入的是中文的情况
@SpringBootTest class Redis02SpringbootApplicationTests { @Autowired RedisTemplate redisTemplate; @Test void contextLoads() { redisTemplate.opsForValue().set("name", "愤怒的张迅豪"); System.out.println(redisTemplate.opsForValue().get("name")); } }
结果:
2、使用客户端连接,发现已经被转义过的
源码分析的时候就说过了,所有的reids对象都需要进行序列化,而我们默认使用的RestTemplate的序列化,所以说是有问题的
3、源码分析
我们可以看到源码中,创建了默认的RedisTemplate
类
点进去可以看到,序列化设置都是null
在构造器中,可以看到默认的序列化方式
<!-- jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.2</version> </dependency>
package com.zxh.config; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; 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.StringRedisSerializer; @Configuration // 配置类 public class RedisConfig { // 自定义RedisTemplate @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory){ // 为了开发的方便,一般直接使用 <String, Object> RedisTemplate<String, Object> template = new RedisTemplate<>(); template.setConnectionFactory(factory); // redis连接的线程安全工厂,源码也是这样配置的 // Json序列化配置 Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); //解决jackson2无法序列化LocalDateTime的问题,这里扩展一个LocalDateTime类型,它是日期类型对象 jdk1.8出的(并且这个类是不可变的和线程安全的,可以研究一下它的API),当然还需要对这个对象进行json格式的转换,如下图: om.disable(SerializationFeature.WRITE_DATE_KEYS_AS_TIMESTAMPS); om.registerModule(new JavaTimeModule()); jackson2JsonRedisSerializer.setObjectMapper(om); // String 的序列化 StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); // key采用String的序列化方式 template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } }
如果不配合使用,LocalDateTime类型放到Redis数据库时,会变得很长一段,很难维护。
测试
控制台没问题
客户端连接,命令行也可以显示
最后:在我们真实的开发中,或者你们在公司,一般都可以看到一个公司自己封装RedisUtil