zoukankan      html  css  js  c++  java
  • springboot 整合Redis

    0、导入 maven依赖

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

    1、SDR说明
    Spring Data Redis(SDR),是SpringFramework提供的一套简化访问Redis的API,是对Jedis的又一层封装。
    SDR集成了Jedis,JRedis,SRP,Lettuce这四种开源的Redis Connector,这些Connector都是针对于Redis的开源Java库。

    其中,JRedis和SRP从spring-data-redis1.7开始,就不支持了

    2、RedisTemplate说明

      SDR支持底层次的通过连接器connector连接到Redis,支持高层次的友好的模板类RedisTemplate,RedisTemplate是建立在低级别的connection基础之上。

      RedisConnection接收或返回字节数组需要自身处理连接,比如关闭连接,而RedisTemplate负责处理串行化和反串行化,并且对连接进行管理

      RedisTemplate是线程安全的,能够用于多个实例中。
      

      RedisTemplate是SDR的一个核心Helper类,该类是一个高级的抽象(对Jedis的又一层封装),它封装了对Redis中的数据的CRUD操作,包含“高级封装”。

    ---????-->

    RedisTemplate默认选择java-based串行化,也可以切换为其它的串行化方式,

    或者设置enabledDefaultSerializer为false或者设置串行化器为null,则RedisTemplate用raw byte arrays表示数据。


    (1)高级封装(推荐使用)
      高级封装的操作包含:OpsForValue(),OpsForList(),OpsForSet(),OpsForZset(),OpsForHash()等等。
      SDR官方文档中对Redistemplate的介绍:

        the template is in fact the central class of the Redis module due to its rich feature set. The template offers a high-level abstraction for Redis interactions.
      

      通过Redistemplate可以调用ValueOperations和ListOperations等等方法,分别是对Redis命令的高级封装。
      但是ValueOperations等等这些命令最终是要转化成为RedisCallback来执行的

      也就是说通过使用RedisCallback可以实现更强的功能

        SDR文档对RedisCallback的介绍:

        RedisTemplate and StringRedisTemplate allow the developer to talk directly to Redis through the RedisCallback interface.

        This gives complete control to the developer as it talks directly to the RedisConnection。

    (2)从高级封装获得低级封装的过渡:
      RedisOperations<String, Object> operations = opsForValue.getOperations();

    3、RedisConnection提供了“低级封装”。

      低级封装的操作是通过连接到Redis的Connection对象,直接对Redis数据进行操作。
    低级封装的核心是:

    redisTemplate.execute(

      new RedisCallback(){

        //  .....

      }

    )

    ===========   redis 源码头=============================

    RedisTemplate的使用

    这个类作为一个模版类,提供了很多快速使用redis的api,而不需要自己来维护连接,事务。

     
    public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V> {

    private boolean enableTransactionSupport = false;
    private boolean exposeConnection = false;
    private boolean initialized = false;
    private boolean enableDefaultSerializer = true;
    private RedisSerializer<?> defaultSerializer = new JdkSerializationRedisSerializer();

    private RedisSerializer keySerializer = null;
    private RedisSerializer valueSerializer = null;
    private RedisSerializer hashKeySerializer = null;
    private RedisSerializer hashValueSerializer = null;
    private RedisSerializer<String> stringSerializer = new StringRedisSerializer();

    private ScriptExecutor<K> scriptExecutor;

    // cache singleton objects (where possible)
    private ValueOperations<K, V> valueOps;
    private ListOperations<K, V> listOps;
    private SetOperations<K, V> setOps;
    private ZSetOperations<K, V> zSetOps;
    ================== 实战 ======================
    package cn.test.config.data;

    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.cache.CacheManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.data.redis.cache.RedisCacheManager;
    import org.springframework.data.redis.connection.RedisConnectionFactory;
    import org.springframework.data.redis.connection.RedisNode;
    import org.springframework.data.redis.connection.RedisSentinelConfiguration;
    import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
    import org.springframework.data.redis.serializer.StringRedisSerializer;
    import redis.clients.jedis.JedisPoolConfig;

    import java.util.HashSet;
    import java.util.Set;

    /**
    * Created by Miraculous on 15/7/4.
    */
    @Configuration
    public class RedisConfig {

    @Value("${test.data.redis.defaultExpiration}")
    private Long defaultExpiration;
    @Value("${test.redis.master.host}")
    private String masterHost;
    @Value("${test.redis.master.port}")
    private int masterPort;
    @Value("${test.redis.master.name}")
    private String masterName;
    @Value("${test.redis.sentinel1.host}")
    private String sentinel1Host;
    @Value("${test.redis.sentinel1.port}")
    private int sentinel1port;
    @Value("${test.redis.sentinel2.host}")
    private String sentinel2Host;
    @Value("${test.redis.sentinel2.port}")
    private int sentinel2port;
    @Value("${test.redis.sentinel3.host}")
    private String sentinel3Host;
    @Value("${test.redis.sentinel3.port}")
    private int sentinel3port;

      /**
        连接工厂
      */
    private RedisConnectionFactory generateDevConnectionFactory() {
    JedisConnectionFactory factory = new JedisConnectionFactory();
    factory.setHostName(masterHost);
    factory.setPort(masterPort);
    factory.setUsePool(true);
    factory.setConvertPipelineAndTxResults(true);
    JedisPoolConfig poolConfig = generatePoolConfig();
    factory.setPoolConfig(poolConfig); // 注入连接池
    factory.afterPropertiesSet();
    return factory;
    }

    private RedisConnectionFactory generateReleaseConnectionFactory() {
    RedisSentinelConfiguration sentinelConfiguration = new RedisSentinelConfiguration();
    RedisNode master = new RedisNode(masterHost, masterPort);
    master.setName(masterName);
    Set<RedisNode> sentinels = new HashSet<>();
    RedisNode sentinel1 = new RedisNode(sentinel1Host, sentinel1port);
    RedisNode sentinel2 = new RedisNode(sentinel2Host, sentinel2port);
    RedisNode sentinel3 = new RedisNode(sentinel3Host, sentinel3port);
    sentinels.add(sentinel1);
    sentinels.add(sentinel2);
    sentinels.add(sentinel3);
    sentinelConfiguration.setMaster(master);
    sentinelConfiguration.setSentinels(sentinels);
    JedisPoolConfig poolConfig = generatePoolConfig();
    JedisConnectionFactory factory = new JedisConnectionFactory(sentinelConfiguration, poolConfig);
    factory.setHostName(masterHost);
    factory.setPort(masterPort);
    factory.setTimeout(10000);
    factory.setUsePool(true);
    factory.setConvertPipelineAndTxResults(true);
    factory.afterPropertiesSet();
    return factory;
    }

      /**redis连接池基本配置
        */
    private JedisPoolConfig generatePoolConfig() {
    JedisPoolConfig poolConfig = new JedisPoolConfig();
    poolConfig.setMinIdle(20);
    poolConfig.setMaxTotal(300);
    poolConfig.setMaxWaitMillis(5000);
    poolConfig.setTestOnBorrow(true);
    return poolConfig;
    }

    @Bean(name = "redisConnectionFactory")
    RedisConnectionFactory factory() {
    if (StringUtils.isEmpty(masterName)) {
    return generateDevConnectionFactory();
    } else {
    return generateReleaseConnectionFactory();
    }
    }

    @Bean(name = "redisTemplate")
    public RedisTemplate<String, Object> redisTemplate(
    RedisConnectionFactory factory) {
    final RedisTemplate<String, Object> template = new RedisTemplate<>();
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    JdkSerializationRedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();

    template.setEnableTransactionSupport(false); //是否启用事务支持
    template.setKeySerializer(stringRedisSerializer); //keySerializer:这个是对key的默认序列化器。默认值是StringSerializer。
    template.setHashKeySerializer(stringRedisSerializer);//对hash结构数据的hashkey序列化器,默认值是取自DefaultSerializer的JdkSerializationRedisSerializer。
    template.setValueSerializer(jdkSerializationRedisSerializer); //这个是对value的默认序列化器,默认值是取自DefaultSerializer的JdkSerializationRedisSerializer。
    template.setDefaultSerializer(jdkSerializationRedisSerializer);
    template.setConnectionFactory(factory); // 注入工厂
    return template;
    }

    @Bean(name = "stringRedisTemplate")
    public RedisTemplate<String, String> stringRedisTemplate(
    RedisConnectionFactory factory) {
    final RedisTemplate<String, String> template = new RedisTemplate<>();
    StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
    template.setEnableTransactionSupport(true);
    template.setKeySerializer(stringRedisSerializer);
    template.setHashKeySerializer(stringRedisSerializer);
    template.setValueSerializer(stringRedisSerializer);
    template.setDefaultSerializer(stringRedisSerializer);
    template.setConnectionFactory(factory);
    return template;
    }

    @Bean(name = "cacheManager")
    public CacheManager cacheManager(RedisTemplate<String, Object> redisTemplate) {
    RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
    cacheManager.setDefaultExpiration(defaultExpiration);
    // cacheManager.setExpires(ImmutableMap.of("apixDistrictCache", 3600 * 24 * 7L));
    cacheManager.setUsePrefix(true);
    return cacheManager;
    }
    }

    一、为什么要使用Serializer

      因为redis是以key-value的形式将数据存在内存中,key就是简单的string,key似乎没有长度限制,不过原则上应该尽可能的短小且可读性强,无论是否基于持久存储,key在服务的整个生命周期中都会在内存中,因此减小key的尺寸可以有效的节约内存,同时也能优化key检索的效率。

      value在redis中,存储层面仍然基于string,在逻辑层面,可以是string/set/list/map,不过redis为了性能考虑,使用不同的“encoding”数据结构类型来表示它们。(例如:linkedlist,ziplist等)。

      所以可以理解为,其实redis在存储数据时,都把数据转化成了byte[]数组的形式,那么在存取数据时,需要将数据格式进行转化,那么就要用到序列化和反序列化了,这也就是为什么需要配置Serializer的原因。

    二、 RedisTempalte的Serializer

      用过jedis操作的都知道,所有connection的操作方法,都是传入字节数组。那么,将一个对象和字节相互转换,就需要通过序列化和反序列化。

    模版方法中,Spring提供了默认的StringSerializer和JdkSerializer,第一个很简单,就是通过String.getBytes()来实现的。而且在Redis中,所有存储的值都是字符串类型的。所以这种方法保存后,通过Redis-cli控制台,是可以清楚的查看到我们保存了什么key,value是什么。

    但是对于JdkSerializationRedisSerializer来说,这个序列化方法就是Jdk提供的了。

    首先要求我们要被序列化的类继承自Serializable接口,然后通过,然后通过Jdk对象序列化的方法保存。

    (注:这个序列化保存的对象,即使是个String类型的,在redis控制台,也是看不出来的,因为它保存了一些对象的类型什么的额外信息)

    这么一长串,其实就是一个int类型的123。

    三、事物支持

    enableTransactionSupport:是否启用事务支持。我们在代码中搜索下用到这个变量的地方,会看到,在调用RedisCallback之前,有一行代码是如果启用事务支持,那么conn = RedisConnectionUtils.bindConnection(factory, enableTransactionSupport),也就是说,系统自动帮我们拿到了事务中绑定的连接。

    可以在一个方法的多次对Redis增删该查中,始终使用同一个连接。但是,即使使用了同样的连接,没有进行connection.multi()和connection.exec(),依然是无法启用事务的。

      我没有仔细的查阅代码,但是可以知道的是,Spring已经对这个,给了我们一个更好的支持:@Transactional

    在调用RedisTempalte中的execute()方法的地方,加入这个注解(是spring包下面提供的,不要引用成rt包下的注解),能让这个方法中的所有execute,自动加入multi()以及异常的回滚或者是正常运行时候的提交!

       redis对事务提供支持,包括multi,exec,discard命令,这些命令也能用于RedisTemplate,然后redisTemplate不保证用相同的连接在同一个事务执行所有操作

      SDR提供SessionCallback接口用于同线程的多操作执行。

    四、 RedisSerializer 接口 的是实现子类

    public interface RedisSerializer<T> {
    }

      其中JdkSerializationRedisSerializer和StringRedisSerializer是最基础的序列化策略

      其中“JacksonJsonRedisSerializer”与“OxmSerializer”都是基于stirng存储,因此它们是较为“高级”的序列化(最终还是使用string解析以及构建java对象)。

      基本推荐使用JdkSerializationRedisSerializer和StringRedisSerializer,因为其他两个序列化策略使用起来配置很麻烦,

      如果实在有需要序列化成Json和XML格式,可以使用java代码将String转化成相应的Json和XML。

  • 相关阅读:
    C#神奇的扩展方法
    OCIEnvCreate 失败,返回代码为 -1,但错误消息文本不可用
    xslt格式化日期的方法
    c# 对象存cookie
    Oracle Client安装报错:引用数据不可用于验证此操作系统分发的先决条件
    Web.config中设置启用webservice远程调试访问 参数看不到
    ORA-12514: TNS: 监听程序当前无法识别连接描述符中请求的服务
    IIS无法加载字体文件(*.woff,*.svg)的解决办法
    word 里面没输入法
    photoshop cc 2018破解补丁(pscc2018注册机) 附使用方法
  • 原文地址:https://www.cnblogs.com/xingzc/p/5995562.html
Copyright © 2011-2022 走看看