zoukankan      html  css  js  c++  java
  • Jedis连接工具 和 SpringBoot整合Redis

    引用学习:https://space.bilibili.com/95256449/

    Jedis连接工具

    什么是Jedis?

    它是官方推荐的Java连接开发工具!使用Java操作 Redis中间件!如果你使用java操作 Redis ,那么要对 jedis 十分的熟悉!

    测试

    • 在本地主机进行测试

    1、打开 Redis 服务

    2、新建maven项目,导入依赖

    <!--导入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的区别:

                    阻塞IO

                    非阻塞IO

    这两张图可以看出,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)

    进入这个接口可以看到有两个实现类

     可以看到 JedisConnectionFactory中有很多类没有生效

     可以看到 LettuceConnectionFactory是生效的

     所以在配置文件中配置时需要注意:SpringBoot 2.x以后 默认使用的lettuce,不要傻傻的配置到jedis了

    测试

    1、导入依赖

    <!-- redis -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

    2、配置文件

    # 配置 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());
        }
    
    }

    结果:

    4、API测试

    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序列化,会有一些问题

    关于序列化

    • 我们基于上面的测试,试一下存入的是中文的情况

    1、测试

    @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

     在构造器中,可以看到默认的序列化方式

    自定义的RestTemplate

    • 开发通用模板,在公司拿过来直接用即可!

    • 还需要导入jackson依赖

    <!-- jackson-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.10.2</version>
    </dependency>

    RedisConfig配置类

    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

     

    引用学习:https://space.bilibili.com/95256449/

    致力于记录学习过程中的笔记,希望大家有所帮助(*^▽^*)!
  • 相关阅读:
    [单选题]请求文件“time.inc”,当发生错误时就终止脚本,正确的方式是:
    [单选题]条件语句的时候不应该使用哪一种控制结构
    [高德地图]学习笔记--基本结构
    nodejs实战:小爬虫
    linux实用命令(2016/11/8-永远)
    自适应响应式布局-实现原理
    解决npm安装慢的方法
    git进阶(分支与标签管理)
    git进阶(远程仓库github)
    git入门命令(只涉及本地仓库管理)
  • 原文地址:https://www.cnblogs.com/zxhbk/p/13054709.html
Copyright © 2011-2022 走看看