六、spring 与redis整合的两种方式,及管道使用_事务监视执行_lua脚本实现事务保证原子性等代码示例

  redis与spring的整合一般分为spring-data-redis整合和jedis整合,想了解两者的区别请移步。本片重点介绍整合的步骤,及相关的操作案例
1、通过spring-data-redis,实现jedis与Spring的整合,进而管理jedis实例、操作redis服务
1.1 如何配置
1)引入相关依赖:
<!--redis连接总结 配置-->
        <!--使用jedis 需要引入  commons-pool 的依赖,否则Jedis会实例化失败-->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.7.1</version>
        </dependency>
        <dependency>
            <groupId>commons-pool</groupId>
            <artifactId>commons-pool</artifactId>
            <version>1.5.6</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.6.2.RELEASE</version>
        </dependency>

        <!-- redis中 如果存储的是Map<String,Object>需要导入jackson相关的包,存储的时候使用json序列化器存储。如果不导入jackson的包会报错。 -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.5.1</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.5.1</version>
        </dependency>
View Code
    2)连接工厂配置文件:spring-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    ">
        <!-- 连接池基本参数配置,类似数据库连接池 -->
        <context:property-placeholder location="classpath:conf/redis.properties" ignore-unresolvable="true" />

        <!-- redis连接池 -->
        <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <property name="maxTotal" value="${redis.maxActive}" />
            <property name="maxIdle" value="${redis.maxIdle}" />
            <property name="testOnBorrow" value="${redis.testOnBorrow}" />
        </bean>

        <!-- 连接池配置,类似数据库连接池 -->
        <bean id="jedisConnectionFactory"
              class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
            <property name="hostName" value="${redis.host}"></property>
            <property name="port" value="${redis.port}"></property>
            <!-- <property name="password" value="${redis连接总结.pass}"></property> -->
            <property name="poolConfig" ref="poolConfig"></property>
        </bean>

        <!--redis操作模版,使用该对象可以操作redis  -->
        <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate" >
            <property name="connectionFactory" ref="jedisConnectionFactory" />
            <!--如果不配置Serializer,那么存储的时候缺省使用String,如果用User类型存储,那么会提示错误User can't cast to String!!  -->
            <property name="keySerializer" >
                <bean class="org.springframework.data.redis.serializer.StringRedisSerializer" />
            </property>
            <property name="valueSerializer" >
                <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
            </property>
            <property name="hashKeySerializer">
                <bean class="org.springframework.data.redis.serializer.StringRedisSerializer"/>
            </property>
            <property name="hashValueSerializer">
                <bean class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer"/>
            </property>
            <!--开启事务  -->
            <property name="enableTransactionSupport" value="true"></property>
        </bean >
        <!-- 下面这个是整合Mybatis的二级缓存使用的:暂未验证过,先注释掉-->
        <!--
        <bean id="redisCacheTransfer" class="cn.qlq.jedis.RedisCacheTransfer">
            <property name="jedisConnectionFactory" ref="jedisConnectionFactory" />
        </bean>
        -->
    </beans>
View Code
    3)redis.properties
#访问地址
        redis.host=127.0.0.1
        #访问端口
        redis.port=6379
        #注意,如果没有password,此处不设置值,但这一项要保留
        redis.password=@redisLearn

        #最大空闲数,数据库连接的最大空闲时间。超过空闲时间,数据库连接将被标记为不可用,然后被释放。设为0表示无限制。
        redis.maxIdle=300
        #连接池的最大数据库连接数。设为0表示无限制
        redis.maxActive=600

        #最大建立连接等待时间。如果超过此时间将接到异常。设为-1表示无限制。
        redis.maxWait=1000

        #在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
        redis.testOnBorrow=true

        #客户端连接超时时间
        redis.timeout=30000

        #可用数据库数
        redis.database = 0
View Code
    4)在spring/spring-context.xml文件中引入:spring-redis.xml
<import resource="spring-redis.xml" />

1.2 其整合原理:
1)通过org.springframework.data.redis.connection.jedis.JedisConnectionFactory来管理,即通过工厂类管理,然后通过配置的模版bean,操作redis服务,代码段中充斥大量与业务无关的模版片段代码,代码冗余,不易维护,比如像下面的代码:
2)spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。在RedisTemplate中提供了几个常用的接口方法的使用,并明确定义了对5种数据结构操作,具体如下:
redisTemplate.opsForValue();//操作字符串
redisTemplate.opsForHash();//操作hash
redisTemplate.opsForList();//操作list
redisTemplate.opsForSet();//操作set
redisTemplate.opsForZSet();//操作有序set

问题:StringRedisTemplate与 RedisTemplate关系
StringRedisTemplate继承RedisTemplate,两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据。SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略。StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
1.3 代码测试:
@Controller
    @RequestMapping("/redis")
    public class RedisController {
        //由于本地同时配置了redis 和jedis ,因此当配置jedis 时,需要注释该属性,使用redis时候再放开
        @Resource(name="redisTemplate")
        private RedisTemplate redisTemplate;
        @RequestMapping("/operate.do")
        @ResponseBody
        public Map springRedisDo() {
            Map result=new HashMap();

            // stringRedisTemplate的操作
            // String读写
            redisTemplate.delete("myStrKey");
            redisTemplate.opsForValue().set("myStrKey", "strValue");
            String strValue= (String) redisTemplate.opsForValue().get("myStrKey");
            result.put("strValue",strValue);

            // List读写
            redisTemplate.delete("myListKey");
            redisTemplate.opsForList().rightPush("myListKey", "listValue1");
            redisTemplate.opsForList().rightPush("myListKey", "listValue2");
            redisTemplate.opsForList().leftPush("myListKey", "listValue3");
            List<String> myListKeyValues = redisTemplate.opsForList().range("myListKey", 0, -1);
            for (String s : myListKeyValues) {
                System.out.println("myListKey数据元素>>>"+s);
            }
            result.put("myListKeyValues",myListKeyValues);

            // Set读写
            redisTemplate.delete("mySet");
            redisTemplate.opsForSet().add("mySetKey", "setValue1");
            redisTemplate.opsForSet().add("mySetKey", "setValue2");
            redisTemplate.opsForSet().add("mySetKey", "setValue3");
            redisTemplate.opsForSet().add("mySetKey", "setValue3");
            redisTemplate.opsForSet().add("mySetKey", "setValue3");
            Set<String> setValues = redisTemplate.opsForSet().members("mySetKey");
            for (String s : setValues) {
                System.out.println("mySetKey数据元素>>>"+s);
            }
            result.put("setValues",setValues);

            // Hash读写
            redisTemplate.delete("myHashKey");
            redisTemplate.opsForHash().put("myHashKey", "BJ", "北京");
            redisTemplate.opsForHash().put("myHashKey", "SH", "上海");
            redisTemplate.opsForHash().put("myHashKey", "TJ", "天津");
            Map<String, String> hashValues = redisTemplate.opsForHash().entries("myHashKey");
            List myHashList= redisTemplate.opsForHash().values("myHashKey");
            System.out.println("myHashList数据信息>>>"+myHashList);
            for (Map.Entry entry : hashValues.entrySet()) {
                System.out.println("myHashValues>>>"+entry.getKey() + " - " + entry.getValue());
            }
            result.put("hashValues",hashValues);

            return result;
        }
    }
View Code
2、通过jedis,实现redis与Spring的整合,进而管理jedis实例、操作redis服务
2.1 如何配置
1)引入相关依赖:使用jedis 需要引入 commons-pool 的依赖,否则Jedis会实例化失败 
         <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
            <version>2.7.1</version>
        </dependency>
        <dependency>
            <groupId>commons-pool</groupId>
            <artifactId>commons-pool</artifactId>
            <version>1.5.6</version>
        </dependency>
View Code
    2)连接池配置文件:spring-jedis.xml
<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
    ">
        <!-- 连接池基本参数配置,类似数据库连接池 -->
        <context:property-placeholder location="classpath:conf/redis.properties" ignore-unresolvable="true" />
        <!-- redis连接池 -->
        <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
            <property name="maxTotal" value="${redis.maxActive}" />
            <property name="maxIdle" value="${redis.maxIdle}" />
            <property name="testOnBorrow" value="${redis.testOnBorrow}" />
        </bean>
        <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
            <constructor-arg name="poolConfig" ref="poolConfig" />
            <constructor-arg name="host" value="${redis.host}" />
            <constructor-arg name="port" value="${redis.port}" type="int" />
            <constructor-arg name="timeout" value="${redis.timeout}" type="int" />
            <constructor-arg name="password" value="${redis.password}" />
            <constructor-arg name="database" value="${redis.database}" type="int" />
        </bean>

        <!-- 集群方式:暂未进行测试,因此先注释掉 -->
        <!--
        <bean id="shardedJedisPool" class="jedis.clients.jedis.ShardedJedisPool"  scope="singleton">
            <constructor-arg index="0" ref="poolConfig" />
            <constructor-arg index="1">
                <list>
                    <bean class="jedis.clients.jedis.JedisShardInfo">
                        <constructor-arg name="host" value="${jedis.host}" />
                    </bean>
                </list>
            </constructor-arg>
         </bean>
         -->
        <!-- 下面这个是整合Mybatis的二级缓存使用的:暂未进行测试,因此先注释掉  -->
        <!--
        <bean id="redisCacheTransfer" class="cn.qlq.jedis.RedisCacheTransfer">
            <property name="jedisConnectionFactory" ref="jedisConnectionFactory" />
        </bean>
        -->
    </beans>
View Code
    3)redis.properties同1.1中的
4)在spring/spring-context.xml文件中引入:spring-jedis.xml
<import resource="spring-jedis.xml" />
2.2)通过jedis方式的理论:
通过redis.clients.jedis.JedisPool 连接池进行连接管理,通过池对象获取jedis实例,然后通过jedis实例直接操作redis服务,剔除了与业务无关的冗余代码,从工厂类到池的方式变化,就相当于mybatis连接mysql方变化是一样的,代码变得更简洁,维护也更容易了。Jedis使用apache commons-pool2对Jedis资源池进行管理,所以在定义JedisPool时一个很重要的参数就是资源池GenericObjectPoolConfig,使用方式如下,其中有很多资源管理和使用的参数。如下面的代码片段
private JedisPool jedisPool;
public String save(String key,String val) {
Jedis jedis = jedisPool.getResource();
return jedis.set(key, val);
}
2.3)代码测试:包含管道使用、批次查询、删除、事务监视执行、lua脚本实现事务保证原子性
import org.apache.commons.lang3.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    import org.springframework.util.Assert;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.Transaction;

    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;

    /*
     * @Copyright (C), 2002-2020,
     * @ClassName: JedisController
     * @Author:
     * @Date: 2020/9/8 11:04
     * @Description:
     * @History:
     * @Version:1.0
     */
    @Controller
    @RequestMapping("/jedis/")
    public class JedisController {

        @Autowired
        private JedisPool jedisPool;

        @Autowired
        private com.zj.weblearn.jedis.JedisClient jedisClient;

        /**
         * @Method:
         * @Author:
         * @Description: param: 通过jedis客户端,往Redis中 存入数据
         * @Return:
         * @Exception:
         * @Date: 2020/9/10 10:38
         */
        @RequestMapping("save")
        @ResponseBody
        public Map getSave(String key, String val) {
            Map result = new HashMap();
            boolean executeResult = false;
            Jedis jedis = null;
            try {
                jedis = jedisPool.getResource();
                jedis.set(key, val);
                executeResult = true;
            } catch (Exception e) {
                System.out.println("获取jedis链接异常" + e);
            }
            result.put("executeResult", executeResult);
            return result;
        }

        /**
         * @Method:
         * @Author:
         * @Description: param: 查询Redis中存储的信息
         * @Return:
         * @Exception:
         * @Date: 2020/9/10 10:40
         */
        @RequestMapping("queryKeyInfo.do")
        @ResponseBody
        public Map getKey(String key) {
            Map result = new HashMap();
            Jedis jedis = jedisPool.getResource();
            String redisValue = jedis.get(key);
            result.put("key", redisValue);
            return result;
        }

        //http://localhost:8080/jedis/hmset.do?userCode=122222
        @RequestMapping("saveHashSet.do")
        @ResponseBody
        public Map saveHmset(String userCode) {
            Map result = new HashMap();
            Map hash = new HashMap();
            hash.put("userName", "ceshi");
            hash.put("userRole", "shenHe");

            jedisClient.hmset(userCode, hash);
            result.put("userCode", jedisClient.hgetAll(userCode));
            return result;
        }

        //http://localhost:8080/jedis/toHincrBy.do?key=122222&field=level&value=2
        @RequestMapping("toHincrBy.do")
        @ResponseBody
        public Map toHincrBy(String key, String field, long value) {
            Map result = new HashMap();
            result.put("increaseAfterVal", jedisClient.hincrBy(key, field, value));
            return result;
        }

        @RequestMapping("pipeline.do")
        @ResponseBody
        public Map testPipeline() {
            Map<String, String> resultMap = new HashMap<>();

            try {
                //1.操作数据准备
                Map<String, String> notPipeLineparamMap = new HashMap<>();
                List<String> keyList=new ArrayList<>();
                Map<String, String> pipeLineparamMap = new HashMap<>();
                for (int i = 0; i < 10000; i++) {
                    notPipeLineparamMap.put("key_" + i, String.valueOf(i));
                    pipeLineparamMap.put("pkey_" + i, String.valueOf(i));
                    keyList.add("pkey_" + i);
                }

                //2.通过非pipeline操作数据写入记时
                long setStart = System.currentTimeMillis();
                for (Map.Entry<String, String> entry : notPipeLineparamMap.entrySet()) {
                    jedisClient.set(entry.getKey(), entry.getValue());
                }
                long setEnd = System.currentTimeMillis();
                resultMap.put("非pipeline操作10000次字符串数据类型set写入,耗时:", (setEnd - setStart) + "毫秒");

                //3.pipeline操作数据写入记时
                long pipelineStart = System.currentTimeMillis();
                jedisClient.setPassPipeline(pipeLineparamMap);
                long pipelineEnd = System.currentTimeMillis();
                resultMap.put("pipeline操作10000次字符串数据类型set写入,耗时:", (pipelineEnd - pipelineStart) + "毫秒");

                //4.通过非pipeline操作删除数据
                long delStart = System.currentTimeMillis();
                for (Map.Entry<String, String> entry : notPipeLineparamMap.entrySet()) {
                    //jedisClient.del(entry.getKey());
                }
                long delEnd = System.currentTimeMillis();
                resultMap.put("非pipeline操作10000次字符串数据类型del删除,耗时:", (delEnd - delStart) + "毫秒");

                //5.通过pipeline操作删除数据局
                long pipelineDelStart = System.currentTimeMillis();
                jedisClient.delPassPipeline(pipeLineparamMap);
                long pipelineDelEnd = System.currentTimeMillis();
                resultMap.put("pipeline操作10000次字符串数据类型del删除,耗时:", (pipelineDelEnd - pipelineDelStart) + "毫秒");

                //6.通过管道批次读取数据
                jedisClient.readPassPipeline(keyList);
            } catch (Exception e) {
                System.out.println("e>>>>>"+e);
                e.printStackTrace();
            }

            return resultMap;
        }

        /**
         * @Method:
         * @Author:
         * @Description: redis 自身提供Transaction 是一个假的事务,其是无法保证事务的原子性的
         * param:
         * @Return:
         * @Exception:
         * @Date: 2020/9/17 17:47
         */
        @RequestMapping("transaction.do")
        @ResponseBody
        public Map testTransaction() {
            Map<String, Object> resultMap = new HashMap<>();
            List<Object> execObjectList = null;
            try {
                Jedis jedis = jedisClient.getJedis();
                jedis.watch("userLevel", "root");
                //使用watch监视key,此时在事务执行前key被改动,事务将取消不会执行所有命令;
                Transaction transaction = jedis.multi();
                transaction.set("userLevel", "normal");
                transaction.set("userAddress", "beijing");
                execObjectList = transaction.exec();
                jedis.close();
                resultMap.put("isSuccess", true);
            } catch (Exception e) {
                resultMap.put("isSuccess", false);
            }
            return resultMap;
        }

        /**
         * @Method:
         * @Author:
         * @Description: 在redis中可通过Lua 脚本实现事务,保证原子性
         * param:
         * @Return:
         * @Exception:
         * @Date: 2020/9/17 17:47
         */
        @RequestMapping("transactionOperateByLua.do")
        @ResponseBody
        public Map transactionOperateByLua() {
            Map<String, Object> resultMap = new HashMap<>();
            List<Object> execObjectList = null;
            List<String> keys = new ArrayList<>();
            keys.add("name");
            keys.add("age");

            List<String> values = new ArrayList<>();
            values.add("kevin");
            values.add("25");
            try {
                Jedis jedis = jedisClient.getJedis();
                jedis.eval("redis.call('set', KEYS[1], ARGV[1]) redis.call('set', KEYS[2], ARGV[2])", keys, values);
                jedis.close();
                resultMap.put("isSuccess", true);
            } catch (Exception e) {
                resultMap.put("isSuccess", false);
            }
            return resultMap;
        }


        /**
         * @Method:
         * @Author:
         * @Description: 在redis中可通过Lua 脚本实现事务,保证原子性
         * param:
         * @Return:
         * @Exception:
         * @Date: 2020/9/17 17:47
         */
        @RequestMapping("distributedLock.do")
        @ResponseBody
        public Map distributedLock(String testKey) {
            Map<String, Object> resultMap = new HashMap<>();
            try {
                Assert.isTrue(StringUtils.isNotBlank(testKey), "key不能为空");
                String key="distributeKey";
                String value="distributeVal";
                long expireTime=60L;
                String replyCode=jedisClient.nxPxSet(key,value,expireTime);
                resultMap.put("isSuccess", true);
                resultMap.put("replyCode", replyCode);
            } catch (Exception e) {
                resultMap.put("isSuccess", false);
            }
            return resultMap;
        }

    }

    import org.apache.commons.lang3.time.DateUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import redis.clients.jedis.Jedis;
    import redis.clients.jedis.JedisPool;
    import redis.clients.jedis.Pipeline;
    import redis.clients.jedis.Response;

    import java.sql.Timestamp;
    import java.text.ParseException;
    import java.text.SimpleDateFormat;
    import java.util.*;

    /*
     * @Copyright (C), 2002-2020,
     * @ClassName: JedisClient
     * @Author:
     * @Date: 2020/9/16 9:35
     * @Description:
     * @History:
     * @Version:1.0
     */
    @Service("jedisClient")
    public class JedisClient {

        private static final Logger LOGGER = LoggerFactory.getLogger(JedisClient.class);

        @Autowired
        private JedisPool jedisPool;

        /**
         * 将key 的值设为value ,当且仅当key 不存在,等效于 SETNX
         */
        public static final String NX = "NX";

        /**
         * seconds — 以秒为单位设置 key 的过期时间,等效于EXPIRE key seconds
         */
        public static final String EX = "EX";

        public static final int FIVE_MIN = 300; // 5分钟

        /**
         * 功能描述: <br>
         * [功能描述]:保存key,value格式的数据
         * [应用场景]:
         */
        public void set(final String key, String val) {
            Jedis jedis = jedisPool.getResource();
            jedis.set(key, val);
            jedis.close();
        }

        /*
         * 设置redis的值 常用方法 SETNX works exactly like SET with the only difference that
         * if the key already exists no operation is performed. SETNX actually means
         * "SET if Not eXists".
         */
        public Long setnx(final String key, String val) {
            Jedis jedis = jedisPool.getResource();
            Long result = jedis.setnx(key, val);
            final int seconds = getExpire(key);
            if (result == 1 && seconds > 0) {
                jedis.expire(key, seconds);
            }
            jedis.close();
            return result;
        }


        /**
         * 得到过期时间,如果出现异常,默认五分钟过期
         * @param key
         * @throws ParseException
         */
        public static int getExpire(String key) {
            if (key == null)
                return FIVE_MIN; // key不存在,那么5秒钟过期。
            try {
                String vkey = getExpireRule(key);

                String tag1 = "{exp=";
                String tag2 = "{exp:";
                if (vkey.indexOf(tag1) > -1 || vkey.indexOf(tag2) > -1) {
                    if (vkey.indexOf(tag1) > -1) {
                        vkey = vkey.substring(vkey.indexOf(tag1) + tag1.length());
                    }
                    if (vkey.indexOf(tag2) > -1) {
                        vkey = vkey.substring(vkey.indexOf(tag2) + tag2.length());
                    }

                    vkey = vkey.substring(0, vkey.indexOf("}"));
                    if (vkey.startsWith("to:")) {
                        int time_sec = getExpireToConstructs(vkey);
                        return time_sec;
                        // 到整点小时过期,加上随机数防止并发压力,时间紧简单写,以后整理
                    } else if (vkey.startsWith("every:1h")) {
                        int time_sec = getExpireEveryOneConstructs();
                        return time_sec;
                    } else if (vkey.startsWith("every:2h")) {
                        int time_sec = getExpireEveryTwoConstructs();
                        return time_sec;
                    } else {
                        return Integer.parseInt(vkey);
                    }
                } else {
                    return FIVE_MIN;
                }
            } catch (Exception e) {
                // 出异常默认为5分钟过期
                LOGGER.error(e.getMessage(), e);
                return FIVE_MIN;
            }
        }

        public static int getExpireEveryTwoConstructs() {
            Calendar cal = Calendar.getInstance();
            cal.setTime(new Date());
            cal.add(Calendar.HOUR_OF_DAY, 2);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, 0);
            int time_sec = Math.round((float) (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000);
            if (time_sec <= 0) {
                time_sec = FIVE_MIN;
            }
            return time_sec;
        }

        public static int getExpireToConstructs(String vkey) {
            int toDay = Integer.parseInt(vkey.substring(3, vkey.indexOf("/")));
            String time = vkey.substring(vkey.indexOf("/") + 1);
            if (time.indexOf(":") == time.lastIndexOf(":")) {
                time += ":00";
            }
            SimpleDateFormat onlyDate = new SimpleDateFormat("yyyy-MM-dd");
            String strToDate = onlyDate.format(DateUtils.addDays(new Date(), toDay)) + " " + time;

            Timestamp toDate = Timestamp.valueOf(strToDate);

            long toTime = toDate.getTime();
            int time_sec = Math.round((float) (toTime - System.currentTimeMillis()) / 1000);

            if (time_sec <= 0) {
                time_sec = FIVE_MIN;
            }
            return time_sec;
        }


        public static int getExpireEveryOneConstructs() {
            Calendar cal = Calendar.getInstance();
            cal.setTime(new Date());
            cal.add(Calendar.HOUR_OF_DAY, 1);
            cal.set(Calendar.MINUTE, 0);
            cal.set(Calendar.SECOND, new Random().nextInt(30));
            int time_sec = Math.round((float) (cal.getTimeInMillis() - System.currentTimeMillis()) / 1000);
            if (time_sec <= 0) {
                time_sec = FIVE_MIN;
            }
            return time_sec;
        }

        /**
         * 默认的过期时间是5分钟
         */
        public static String getExpireRule(String key) {
            if (key == null)
                return "{exp=" + FIVE_MIN + "}";
            String vkey = key.toLowerCase().replaceAll(" ", "");

            String tag1 = "{exp=";
            String tag2 = "{exp:";
            try {
                String expireRule = "";
                if (vkey.indexOf(tag1) > -1 || vkey.indexOf(tag2) > -1) {
                    if (vkey.indexOf(tag1) > -1) {
                        expireRule = vkey.substring(vkey.indexOf(tag1));
                    } else if (vkey.indexOf(tag2) > -1) {
                        expireRule = vkey.substring(vkey.indexOf(tag2));
                    }
                    expireRule = expireRule.substring(0, expireRule.indexOf("}") + 1);
                    return expireRule.toLowerCase();
                } else {
                    return "{exp=" + FIVE_MIN + "}";// 默认都5分钟过期
                }
            } catch (Exception e) {
                LOGGER.error(e.getMessage(), e);
                return "{exp=" + FIVE_MIN + "}";
            }
        }


        /**
         * 功能描述: <br>
         * [功能描述]:通过管道进行批次 保存key,value格式的数据
         * [应用场景]:pipeline提供了命令的批量提交,当我们有批量查询或者写入操作时,通过pipeline可以很大程度上控制网络开销
         */
        public void setPassPipeline(Map<String, String> valMap) {
            Jedis jedis = jedisPool.getResource();
            Pipeline pipeline = jedis.pipelined();
            for (Map.Entry<String, String> entry : valMap.entrySet()) {
                pipeline.set(entry.getKey(), entry.getValue());
            }
            pipeline.sync();
            jedis.close();
        }

        /**
         * 功能描述: <br>
         * [功能描述]:删除redis 中指定的key的存储信息
         * [应用场景]:
         */
        public void del(final String key) {
            Jedis jedis = jedisPool.getResource();
            jedis.del(key);
            jedis.close();
        }

        /**
         * 功能描述: <br>
         * [功能描述]:通过管道进行批次删除key的数据
         * [应用场景]:pipeline提供了命令的批量提交,当我们有批量查询或者写入操作时,通过pipeline可以很大程度上控制网络开销
         */
        public void delPassPipeline(Map<String, String> valMap) {
            Jedis jedis = jedisPool.getResource();
            Pipeline pipeline = jedis.pipelined();
            for (Map.Entry<String, String> entry : valMap.entrySet()) {
                pipeline.del(entry.getKey());
            }
            pipeline.sync();
            jedis.close();
        }

        /**
         * @Method:
         * @Author:
         * @Description: 通过管道批次读取
         * param:
         * @Return:
         * @Exception:
         * @Date: 2020/11/12 15:42
         */
        public void readPassPipeline(List<String> keys) {
            Jedis jedis = jedisPool.getResource();
            Pipeline pipeline = jedis.pipelined();
            Map<String,Map<String,String>> result = new HashMap<String,Map<String,String>>();

            //使用pipeline hgetall
            Map<String, Response<Map<String,String>>> responses = new HashMap<String,Response<Map<String,String>>>(keys.size());
            long start = System.currentTimeMillis();
            for(String key : keys) {
                responses.put(key, pipeline.hgetAll(key));
            }
            pipeline.sync();
            for(String k : responses.keySet()) {
                result.put(k, responses.get(k).get());
            }
            System.out.println("result>>>>"+result);
            long end = System.currentTimeMillis();
            System.out.println("result size:[" + result.size() + "] ..");
            System.out.println("hgetAll with pipeline used [" + (end - start) / 1000 + "] seconds ..");
            jedis.disconnect();
        }





        /**
         * 功能描述: <br>
         * [功能描述]:同时将多个 field-value (域-值)对设置到哈希表 key 中。
         * [应用场景]:
         */
        public void hmset(final String key, final Map<String, String> hash) {
            Jedis jedis = jedisPool.getResource();
            jedis.hmset(key, hash);
            jedis.close();
        }

        /**
         * 功能描述: <br>
         * [功能描述]:一次获取哈希表 key 的值(多个 field-value (域-值))
         * [应用场景]:
         */
        public Map hgetAll(final String key) {
            Jedis jedis = jedisPool.getResource();
            Map result = jedis.hgetAll(key);
            jedis.close();
            return result;
        }

        /**
         * 功能描述: <br>
         * [功能描述]:为哈希表 key 中的域 field 的值加上增量 increment 。
         * [应用场景]:
         */
        public Long hincrBy(final String key, final String field, final long value) {
            Jedis jedis = jedisPool.getResource();
            long result = jedis.hincrBy(key, field, value);
            jedis.close();
            return result;
        }

        /**
         * 功能描述: <br>
         * [功能描述]:<br>
         * 将字符串值 value 关联到 key 。<br>
         * 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。<br>
         * 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。<br>
         * seconds 过去时间(秒)
         * 如果服务器返回 OK ,那么这个客户端获得锁。
         * 如果服务器返回 NIL ,那么客户端获取锁失败,可以在稍后再重试。
         * [应用场景]:
         */
        public String nxPxSet(final String key, final String value, final long time) {
            Jedis jedis = jedisPool.getResource();
            String replyCode = jedis.set(key, value, NX, EX, time);
            jedis.close();
            return replyCode;
        }

        /**
         * 功能描述: <br>
         * [功能描述]:获取jedis对象
         * [应用场景]:
         */
        public Jedis getJedis() {
            Jedis jedis = jedisPool.getResource();
            return jedis;
        }

        /**
         * 功能描述: <br>
         * [功能描述]:关闭jedis对象
         * [应用场景]:
         */
        public void closeJedis(Jedis jedis) {
            jedis.close();
        }


        /**
         * 返回名称为key的set的所有元素 Return all the members (elements) of the set value
         * stored at key. This is just syntax glue for SINTER.
         */
        public Set<String> smembers(final String key) {
            Jedis jedis = jedisPool.getResource();
            Set<String> setResult = jedis.smembers(key);
            jedis.close();
            return setResult;
        }

        //redis 在分布式(Distributed)下 锁的应用,待补充

    }
View Code

参看博文:
https://www.cnblogs.com/qlqwjy/p/8562703.html
https://www.cnblogs.com/wlandwl/p/redis.html redis介绍及jedis基础操作
https://blog.csdn.net/zxl646801924/article/details/82770026 redis和jedis的用法区别
https://www.cnblogs.com/liuqingzheng/p/9831331.html window redis 安装
https://blog.csdn.net/rexueqingchun/article/details/79803949 项目中引入jedis
https://www.cnblogs.com/springlight/tag/redis/ redis 相关学习
https://blog.csdn.net/li1376417539/article/details/104951358/ spring官方文档中文
https://spring.io/projects/spring-data-redis spring 整合redis
https://www.cnblogs.com/yiwangzhibujian/p/7053840.html redis相关
https://blog.csdn.net/j16421881/article/details/78764287 commons-pool
及对开发工作中用到的代码进行总结
如有疑问,欢迎留言!

posted @ 2021-01-22 09:08  爱笑的berg  阅读(461)  评论(0编辑  收藏  举报