Redis客户端之Jedis(一)介绍

目录

一、Jedis介绍:

1、背景:

2、Jedis连接池介绍:

二、Jedis API:

1、连接池API

2、其他常用API: 

三、SpringBoot集成Jedis: 

1、Redis集群模式:

(1)配置文件:配置redis、jedis属性信息

(2)Jedis连接池配置类,连接Redis:

(3)Jedis工具类:这里展示基本CRUD操作

2、Redis单机模式:

四、jedis常见应用:

1、Redis五种数据结构基本的增删改查:

2、Redis分布式锁:

(1)加锁:

(2)解锁:

(3)demo:


一、Jedis介绍:
1、背景:

Jedis是基于Java语言的Redis的客户端,Jedis = Java + Redis。Redis不仅可以使用命令来操作,现在基本上主流的语言都有API支持,比如Java、C#、C++、PHP、Node.js、Go等。在官方网站里有一些Java的客户端:Jedis、Redisson、Jredis、JDBC-Redis等,其中官方推荐使用Jedis和Redisson。简言之Jedis是Redis的Java版本API,通过使用Jedis可以操作Redis中的数据。

2、Jedis连接池介绍:

jedis连接资源的创建与销毁是很消耗程序性能,所以jedis为我们提供了jedis的连接池技术,jedis

连接池在创建时初始化一些连接对象存储到连接池中,使用jedis连接资源时不需要自己创建jedis对

象,而是从连接池中获取一个资源进行redis的操作。使用完毕后,不需要销毁该jedis连接资源,

而是将该资源归还给连接池,供其他请求使用。

二、Jedis API:
1、连接池API

JedisPoolConfig配置类

功能说明

JedisPoolConfig()

创建一个配置对象,使用无参构造方法就可以了

void setMaxTotal()

设置连接池最大的连接数

void setMaxWaitMillis()

设置得到连接对象Jedis最长等待时间

JedisPool连接池类

说明

JedisPool(配置对象,服务器名,端口号)

创建连接池

Jedis getResource()

从连接池中得到一个Jedis连接对象

void close()

连接池关闭方法,通常不关闭连接池

2、其他常用API: 

方法

功能

new Jedis(host, port)

创建Jedis的连接,参数:主机名,端口号

set(key,value)

添加一个字符串的键和值

get(key)

得到指定键的值

del(key)

删除指定键和值

hset(key,field,value)

添加一个hash类型的键-字段-值

hget(key,field)

通过hash键-字段得到它的值

lpush(key,values)

从左边添加一个list类型的键和元素

lpop(key)

从左边弹出一个元素

rpop(key)

从右边弹出一个元素

close()

关闭连接

三、SpringBoot集成Jedis: 

pom依赖:加上redis、jedis依赖

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

实际应用一般采用Redis集群,这里重点看下集群模式下的集成方式。

1、Redis集群模式:
(1)配置文件:配置redis、jedis属性信息
#redis配置
my.redis.server.jedis.pool.maxTotal=500
my.redis.server.jedis.pool.maxIdle=10
my.redis.server.jedis.pool.maxWaitMillis=5000
my.redis.server.jedis.pool.min-idle=5
my.redis.server.timeout=5000 
#哨兵配置
my.redis.sentinel.nodes=xxx.xx.xxx.111:26379,xxx.xx.xxx.222:26379,xxx.xx.333:26379
my.redis.sentinel.password=wtyy
my.redis.sentinel.master-name=mymaster
my.redis.sentinel.database=10
my.redis.sentinel.pool.max-total=10
my.redis.sentinel.pool.max-idle=5
my.redis.sentinel.pool.min-idle=5
(2)Jedis连接池配置类,连接Redis:
@Configuration
public class JedisConfig {private Logger logger = LoggerFactory.getLogger(JedisConfig.class);@Value("${my.redis.server.jedis.pool.maxTotal}")private int maxTotal;@Value("${my.redis.server.jedis.pool.maxIdle}")private int maxIdle;@Value("${my.redis.server.jedis.pool.maxWaitMillis}")private int maxWaitMillis;@Value("${my.redis.server.timeout}")private int timeout;@Value("${my.redis.sentinel.nodes}")private String redisSentinelNodes;@Value("${my.redis.sentinel.pool.max-total}")private int redisSentinelMaxTotal;@Value("${my.redis.sentinel.pool.max-idle}")private int redisSentinelMaxIdle;@Value("${my.redis.sentinel.pool.min-idle}")private int redisSentinelMinIdle;@Value("${my.redis.sentinel.master-name}")private String redisSentinelMasterName;@Value("${my.redis.sentinel.password}")private String redisSentinelPassword;@Value("${my.redis.sentinel.database}")private int dataBase;@Bean(name = "jedisPool")public JedisSentinelPool jedisPool() {JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxTotal(maxTotal);jedisPoolConfig.setMaxIdle(maxIdle);jedisPoolConfig.setMaxWaitMillis(maxWaitMillis);//sentinelString[] hosts = redisSentinelNodes.split(",");Set<String> sentinels = new HashSet<>(Arrays.asList(hosts));GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();poolConfig.setMaxTotal(redisSentinelMaxTotal);poolConfig.setMaxIdle(redisSentinelMaxIdle);poolConfig.setMinIdle(redisSentinelMinIdle);JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(redisSentinelMasterName,sentinels, jedisPoolConfig,timeout,redisSentinelPassword,dataBase);return  jedisSentinelPool;}
}
(3)Jedis工具类:这里展示基本CRUD操作
@SuppressWarnings("unused")
@Component
public class RedisClient {private static boolean BORROW = true; // 在borrow一个事例时是否提前进行validate操作private static Logger logger = Logger.getLogger(RedisClient.class);@Autowiredprivate JedisSentinelPool pool;/*** 获取连接*/public  synchronized Jedis getJedis() {try {if (pool != null) {return pool.getResource();} else {return null;}} catch (Exception e) {logger.info("连接池连接异常");return null;}}/*** @Description: 关闭连接* @param @param jedis* @return void 返回类型*/public static void getColse(Jedis jedis) {if (jedis != null) {jedis.close();}}/*** 格式化Key*/public static String format(String formatKey, String... keyValues) {if (keyValues == null || keyValues.length == 0) {return formatKey;}StringBuilder key = new StringBuilder();char[] chars = formatKey.toCharArray();int index = -1;boolean inmark = false;boolean firstinmark = false;for (int i = 0; i < chars.length; i++) {char ch = chars[i];if (ch == '{') {index++;inmark = true;firstinmark = true;} else if (ch == '}') {inmark = false;} else if (inmark) {if (firstinmark) {firstinmark = false;key.append(keyValues[index]);}} else {key.append(chars[i]);}}return key.toString();}/********************************** 针对key的操作 **************************************//*** 删除一个key** @param keyFormat*            key标识* @param keyValues*            key变量* @return 被删除的keys的数量*/public Long del(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.del(key);} finally {if (jedis != null) {jedis.close();}}}/*** 查询一个key是否存在** @param keyFormat*            key标识* @param keyValues*            key变量* @return key是否存在。*/public boolean exists(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.exists(key);} finally {if (jedis != null) {jedis.close();}}}/*** 设置一个key的过期的秒数** @param keyFormat*            key标识* @param seconds*            过期的秒数* @param keyValues*            key变量* @return 1表示设置成功, 0 表示设置失败或者无法被设置*/public Long expire(String keyFormat, int seconds, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.expire(key, seconds);} finally {if (jedis != null) {jedis.close();}}}/*** 设置一个UNIX时间戳的过期时间** @param keyFormat*            key标识* @param expireDate*            过期时间* @param keyValues*            key变量* @return 1表示设置成功, 0 表示设置失败或者无法被设置*/public Long expireAt(String keyFormat, Date expireDate, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.pexpireAt(key, expireDate.getTime());} finally {if (jedis != null) {jedis.close();}}}/*** 移除给定key的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。** @param keyFormat*            key标识* @param keyValues*            key变量* @return 当生存时间移除成功时,返回 1 . 如果 key 不存在或 key 没有设置生存时间,返回 0 .*/public Long persist(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.persist(key);} finally {if (jedis != null) {jedis.close();}}}/*** 设置一个key的过期的毫秒数** <pre>* 这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。* </pre>** @param keyFormat*            key标识* @param milliSeconds*            过期的毫秒数* @param keyValues*            key变量* @return 设置成功,返回 1,不存在或设置失败,返回 0*/public Long pexpire(String keyFormat, long milliSeconds,String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.pexpire(key, milliSeconds);} finally {if (jedis != null) {jedis.close();}}}/*** 获取key的有效毫秒数** <pre>* 这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而不是像 TTL 命令那样,以秒为单位。* </pre>** @param keyFormat*            key标识* @param keyValues*            key变量* @return 当 key 不存在时,返回 -2 。当 key 存在但没有设置剩余生存时间时,返回 -1 。否则,以毫秒为单位,返回 key*         的剩余生存时间。*/public Long pttl(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.pttl(key);} finally {if (jedis != null) {jedis.close();}}}/*** 获取key的有效时间(单位:秒)** <pre>* 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。* </pre>** @param keyFormat*            key标识* @param keyValues*            key变量* @return 当 key 不存在时,返回 -2 。当 key 存在但没有设置剩余生存时间时,返回 -1 。否则,以秒为单位,返回 key*         的剩余生存时间。*/public Long ttl(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.ttl(key);} finally {if (jedis != null) {jedis.close();}}}/********************************** 针对字符串(string)的操作 **************************************//*** 追加一个值到key上** <pre>* 如果 key 已经存在,并且值为字符串,那么这个命令会把 value 追加到原来值(value)的结尾。* 如果 key 不存在,那么它将首先创建一个空字符串的key,再执行追加操作,这种情况 APPEND 将类似于 SET 操作。* </pre>** @param keyFormat*            key标识* @param value*            要追加的值* @param keyValues*            key变量* @return 返回append后字符串值(value)的长度。*/public Long append(String keyFormat, String value, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.append(key, value);} finally {if (jedis != null) {jedis.close();}}}/*** 整数原子减1** <pre>* 对key对应的数字做减1操作。如果key不存在,那么在操作之前,这个key对应的值会被置为0。* 如果key有一个错误类型的value或者是一个不能表示成数字的字符串,就返回错误。* 这个操作最大支持在64位有符号的整型数字。* </pre>** @param keyFormat*            key标识* @param keyValues*            key变量* @return 数字:减小之后的value*/public Long decr(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.decr(key);} finally {if (jedis != null) {jedis.close();}}}/*** 原子减指定的整数** <pre>* 将key对应的数字减decrement。如果key不存在,操作之前,key就会被置为0。* 如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。* 这个操作最多支持64位有符号的正型数字。* </pre>** @param keyFormat*            key标识* @param integer*            要减小的数值* @param keyValues*            key变量* @return 返回一个数字:减少之后的value值。*/public Long decrby(String keyFormat, long integer, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.decrBy(key, integer);} finally {if (jedis != null) {jedis.close();}}}/*** @param keyFormat*            key标识* @param keyValues*            key变量* @return key对应的value,或者null(key不存在时)*/public String get(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.get(key);} finally {if (jedis != null) {jedis.close();}}}/*** 设置一个key的value,并获取设置前的值** <pre>* 自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。* exp:* GETSET可以和INCR一起使用实现支持重置的计数功能。* 举个例子:每当有事件发生的时候,一段程序都会调用INCR给key mycounter加1,但是有时我们需要获取计数器的值,并且自动将其重置为0。* 这可以通过GETSET mycounter "0"来实现:* </pre>** @param keyFormat*            key标识* @param value*            要设置的值* @param keyValues*            key变量* @return 设置之前的值*/public String getSet(String keyFormat, String value, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.getSet(key, value);} finally {if (jedis != null) {jedis.close();}}}/*** 执行原子加1操作** <pre>* 对key对应的数字做加1操作。如果key不存在,那么在操作之前,这个key对应的值会被置为0。* 如果key有一个错误类型的value或者是一个不能表示成数字的字符串,就返回错误。这个操作最大支持在64位有符号的整型数字。* 提醒:这是一个string操作,因为Redis没有专用的数字类型。key对应的string都被解释成10进制64位有符号的整型来执行这个操作。* Redis会用相应的整数表示方法存储整数,所以对于表示数字的字符串,没必要为了用字符串表示整型存储做额外开销。* </pre>** @param keyFormat*            key标识* @param keyValues*            key变量* @return 增加之后的value*/public Long incr(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.incr(key);} finally {if (jedis != null) {jedis.close();}}}/*** 执行原子加1操作,并且设置过期时间(单位:s)** <pre>* 本操作是在{@linkplain RedisClient#incr(String, String...)}之上增加了一个设置过期时间的操作* </pre>** @param keyFormat*            key标识* @param expireTime*            过期时间(单位:s)* @param keyValues*            key变量* @return 增加之后的value*/public Long incr(String keyFormat, int expireTime, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();long result = jedis.incr(key);jedis.expire(key, expireTime);return result;} finally {if (jedis != null) {jedis.close();}}}/*** 执行原子增加一个整数** <pre>* 将key对应的数字加increment。如果key不存在,操作之前,key就会被置为0。* 如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。这个操作最多支持64位有符号的正型数字。* 查看方法{@linkplain RedisClient#incr(String, String...)}了解关于增减操作的额外信息。* </pre>** @param keyFormat*            key标识* @param increment*            要增加的数值* @param keyValues*            key变量* @return 增加后的value*/public Long incrBy(String keyFormat, long increment, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.incrBy(key, increment);} finally {if (jedis != null) {jedis.close();}}}/*** 执行原子增加一个浮点数** <pre>* 将key对应的数字加increment。如果key不存在,操作之前,key就会被置为0。* 如果key的value类型错误或者是个不能表示成数字的字符串,就返回错误。* </pre>** @param keyFormat*            key标识* @param increment*            要增加的数值* @param keyValues*            key变量* @return 增加后的value*/public Double incrByFloat(String keyFormat, double increment,String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.incrByFloat(key, increment);} finally {if (jedis != null) {jedis.close();}}}/*** 设置一个key的value值** <pre>* 警告:如果key已经存在了,它会被覆盖,而不管它是什么类型。* </pre>** @param keyFormat*            key标识* @param value*            要设置的值* @param keyValues*            key变量** @return 总是"OK"*/public String set(String keyFormat, String value, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.set(key, value);} finally {if (jedis != null) {jedis.close();}}}/*** 设置key-value并设置过期时间(单位:秒)** <pre>* 设置key对应字符串value,并且设置key在给定的seconds时间之后超时过期。* 该命令相当于执行了{@link #set(String, String, String...) SET} + {@link #expire(String, int, String...) EXPIRE}.并且该操作是原子的* </pre>** @param keyFormat*            key标识* @param seconds*            超时时间(单位:s)* @param value*            设置的值* @param keyValues*            key变量* @return 状态码*/public String setex(String keyFormat, int seconds, String value,String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.setex(key, seconds, value);} finally {if (jedis != null) {jedis.close();}}}/*** 设置key-value并设置过期时间(单位:毫秒)** <pre>* 跟{@link #setex(String, int, String, String...)}效果差不多,唯一区别是超时时间是ms* </pre>** @param keyFormat*            key标识* @param milliseconds*            超时时间(单位:ms)* @param value*            设置的值* @param keyValues*            key变量* @return 状态码*/public String psetex(String keyFormat, int milliseconds, String value,String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.psetex(key, (long) milliseconds, value);} finally {if (jedis != null) {jedis.close();}}}/*** 设置的一个关键的价值,只有当该键不存在** <pre>* 如果key不存在,就设置key对应字符串value。在这种情况下,该命令和SET一样。* 当key已经存在时,就不做任何操作。SETNX是"SET if Not eXists"。* </pre>** @param keyFormat*            key标识* @param value*            设置的value* @param keyValues*            key变量* @return 1 如果key被set,0 如果key没有被set*/public Long setnx(String keyFormat, String value, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.setnx(key, value);} finally {if (jedis != null) {jedis.close();}}}/*** 设置hash里面一个字段的值** @param keyFormat*            key标识* @param field*            字段* @param value*            值* @param keyValues*            key变量* @return 含义如下:1如果field是一个新的字段 0如果field原来在map里面已经存在**/public Long hset(String keyFormat, String field, String value,String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.hset(key, field, value);} finally {if (jedis != null) {jedis.close();}}}/******************************* SET 操作 *************************//*** 添加一个元素到集合(set)里** <pre>* 添加一个指定的member元素到集合的 key中.* 如果已经在集合key中存在则忽略.如果集合key 不存在,则新建集合key,并添加member元素到集合key中.* 如果key 的类型不是集合则返回错误.* </pre>** @param keyFormat*            key标识* @param value*            元素* @param keyValues*            key变量* @return 返回新成功添加到集合里元素的数量,不包括已经存在于集合中的元素.*/public Long sadd(String keyFormat, String value, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.sadd(key, value);} finally {if (jedis != null) {jedis.close();}}}/*** 获取集合里面的元素数量** @param keyFormat*            key标识* @param keyValues*            key变量* @return 集合的基数(元素的数量),如果key不存在,则返回 0.*/public Long scard(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.scard(key);} finally {if (jedis != null) {jedis.close();}}}/*** 获取集合里面的所有key** @param keyFormat*            key标识* @param keyValues*            key变量* @return 集合中的所有元素.*/public Set<String> smembers(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.smembers(key);} finally {if (jedis != null) {jedis.close();}}}/*** 删除并获取一个集合里面的元素** <pre>* 移除并返回一个集合中的随机元素* 该命令与 {@link #srandmember(String, String...)}相似,不同的是srandmember命令返回一个随机元素但是不移除.* </pre>** @param keyFormat*            key标识* @param keyValues*            key变量* @return 被移除的元素, 当key不存在的时候返回 null .*/public String spop(String keyFormat, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.spop(key);} finally {if (jedis != null) {jedis.close();}}}/*** 从集合里删除一个元素** @param keyFormat*            key标识* @param member*            要删除的元素* @param keyValues*            key变量* @return 从集合中移除元素的个数,不包括不存在的成员.*/public Long srem(String keyFormat, String member, String... keyValues) {String key = format(keyFormat, keyValues);Jedis jedis = null;try {jedis = getJedis();return jedis.srem(key, member);} finally {if (jedis != null) {jedis.close();}}}/**省略 其他不常用操作**/}
2、Redis单机模式:

配置方式有两种:

(1)代码配置Redis连接:这里省略jedis的一些属性配置

my.redis.server.host=xxx.xx.xxx
my.redis.server.port=6379
my.redis.server.password = wtyy
my.redis.server.jedis.pool.maxTotal=500
my.redis.server.jedis.pool.maxIdle=10
my.redis.server.jedis.pool.maxWaitMillis=5000
my.redis.server.jedis.pool.min-idle=5
my.redis.server.timeout=5000 
@Configuration
public class JedisConfig {private Logger logger = LoggerFactory.getLogger(JedisConfig.class);@Value("${my.redis.server.host}")private String host;@Value("${my.redis.server.port}")private int port;@Value("${my.redis.server.password}")private String password;@Value("${my.redis.server.jedis.pool.maxTotal}")private int maxTotal;@Value("${my.redis.server.jedis.pool.maxIdle}")private int maxIdle;@Value("${my.redis.server.jedis.pool.maxWaitMillis}")private int maxWaitMillis;@Value("${my.redis.server.timeout}")private int timeout;@Bean(name = "jedisPool")public JedisPool jedisPool() {JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(maxTotal);config.setMaxIdle(maxIdle);config.setMaxWaitMillis(maxWaitMillis);return new JedisPool(config, host, port, timeout,password);}
}

(2)在配置文件中配置redis连接:不建议使用

spring.redis.database=1
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=0
spring.redis.timeout=0
四、jedis常见应用:
1、Redis五种数据结构基本的增删改查:

上面的集成demo中,RedisClient演示过了jedis对于Redis的基本CRUD操作,这里不重复。

2、Redis分布式锁:
(1)加锁:
public class RedisTool {private static final String LOCK_SUCCESS = "OK";private static final String SET_IF_NOT_EXIST = "NX";private static final String SET_WITH_EXPIRE_TIME = "PX";/*** 尝试获取分布式锁* @param jedis Redis客户端* @param lockKey 锁* @param requestId 请求标识* @param expireTime 超期时间* @return 是否获取成功*/public static boolean tryGetDistributedLock(Jedis jedis, String lockKey, String requestId, int expireTime) {String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);if (LOCK_SUCCESS.equals(result)) {return true;}return false;}
}
(2)解锁:
public class RedisTool {private static final Long RELEASE_SUCCESS = 1L;/*** 释放分布式锁* @param jedis Redis客户端* @param lockKey 锁* @param requestId 请求标识* @return 是否释放成功*/public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));if (RELEASE_SUCCESS.equals(result)) {return true;}return false;}
}
(3)demo:
/**
*异步导出
*/
public void runTask(ExportRecord record) {logger.info("--------导出XXX任务开始 -------------");boolean lock = fileExportService.lockTask(record);if (!lock) {return;}//...todo...导出业务逻辑fileExportService.finishTask(record);logger.info("--------导出XXX任务结束 -------------");
}FileExportCommonServiceImpl{/*** redis锁  最长时间  /s*/public static final Integer REDIS_LOCK_DOWNLOAD_MAX_TIME = 60 * 2;@Overridepublic boolean lockTask(ExportRecord record) {boolean lock = redisClient.getLock(record.getId(), record.getId(), REDIS_LOCK_DOWNLOAD_MAX_TIME);if (!lock) {logger.error("获取锁失败,taskId:{}", record.getId());return lock;}//更新record任务状态为进行中//todoreturn lock;}@Overridepublic boolean finishTask(ExportRecord record) {record.setTaskEndTime(new Date());//更新task任务结束时间//todoreturn redisClient.releaseLock(record.getId(), record.getId());}
}
@SuppressWarnings("unused")
@Component
public class RedisClient {private  final Logger logger = LoggerFactory.getLogger(RedisClient.class);private  final String SUCCESS_OK = "OK";private  final Long SUCCESS_STATUS_LONG = 1L;// SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作private  final String SET_IF_NOT_EXIST = "NX";// 给key加一个过期的设置,具体时间由第五个参数决定private  final String SET_WITH_EXPIRE_TIME = "PX";@Autowiredprivate JedisSentinelPool jedisPool;public JedisSentinelPool getJedisPool(){return jedisPool;}/*** 获取分布式锁** @param lockKey*            key为锁* @param requestId*            加锁请求* @param expireTime*            key的过期时间* @return*/public  boolean getLock(String lockKey, String requestId, int expireTime) {boolean ret = false;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return ret;}String status = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);if (SUCCESS_OK.equalsIgnoreCase(status)) {ret = true;}} catch (Exception e) {logger.error("redis 获取分布式锁 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return ret;}/*** 释放分布式锁** @param lockKey* @param requestId*/public  boolean releaseLock(String lockKey, String requestId) {boolean ret = false;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return ret;}/** 其他请求误解锁问题 if(requestId.equals(jedis.get(lockKey))) { jedis.del(lockKey); }*/String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object status = jedis.eval(script, Collections.singletonList(lockKey),Collections.singletonList(requestId));if (SUCCESS_STATUS_LONG.equals(status)) {ret = true;}} catch (Exception e) {logger.error("redis 释放分布式锁 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return ret;}/*** 序列化存入对象** @param key* @param obj* @return*/public  boolean set(byte[] key, Object obj) {boolean ret = false;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return ret;}ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(obj);String status = jedis.set(key, baos.toByteArray());if (SUCCESS_OK.equalsIgnoreCase(status)) {ret = true;}} catch (Exception e) {logger.error("redis set 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return ret;}/*** 取序列化对象** @param key* @return*//*public  Object getObj(byte[] key) {Object ret = null;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return ret;}byte[] rets = jedis.get(key);try{ByteArrayInputStream bais = new ByteArrayInputStream(rets);ObjectInputStream ois = new ObjectInputStream(bais);return ois.readObject();}catch (Exception e) {logger.error("ObjectInputStream反序列化对象出错", e);}} catch (Exception e) {logger.error("redis get 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return ret;}*//*** hash数据类型存储对象** @param key* @param obj* @return*/public  boolean setHm(String key, Object obj) {boolean ret = false;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return ret;}Map<String, String> hash = objToMap(obj);String status = jedis.hmset(key, hash);if (SUCCESS_OK.equalsIgnoreCase(status)) {ret = true;}} catch (Exception e) {logger.error("redis setHm 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return ret;}/*** 修改对象属性** @param key* @param field* @param value* @return*/public  boolean setHm(String key, String field, String value) {boolean ret = false;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return ret;}Long status = jedis.hset(key, field, value);if (0L == status) {ret = true;}} catch (Exception e) {logger.error("redis setHm 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return ret;}/*** 根据fields 查询key对象属性列表** @param key* @param fields* @return*/public List<String> getHm(String key, String... fields) {List<String> ret = null;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return null;}ret = jedis.hmget(key, fields);} catch (Exception e) {logger.error("redis getHm 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return ret;}/*** 根据fields 查询key对象属性列表** @param key* @return*/public Long incr(String key) {Long count = null;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return null;}count = jedis.incr(key);} catch (Exception e) {logger.error("redis incr 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return count;}/*** 根据field 查询key对象属性** @param key* @param* @return*/public  String getHm(String key, String field) {String ret = null;Jedis jedis = null;try {jedis = jedisPool.getResource();if (jedis == null) {return null;}ret = jedis.hget(key, field);} catch (Exception e) {logger.error("redis getHm 出错", e);jedisPool.returnBrokenResource(jedis);} finally {if (null != jedis) {jedisPool.returnResource(jedis);}}return ret;}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/650153.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3项目中typescript如何export引入(imported)的interface

引入接口后&#xff0c;不能原封不动地直接export出去。 typescript支持面向对象语言中常见的接口&#xff08;interface&#xff09;、类&#xff08;class&#xff09;等。但我近几天发现&#xff0c;一个interface&#xff0c;通过import引入后&#xff0c;如果直接再expor…

【HarmonyOS应用开发】TypeScript快速入门(二)

内容比较长&#xff0c;干货满满&#xff0c;全是实战操作内容&#xff0c;希望耐心观看&#xff0c;如果对你有所帮助&#xff0c;请点个赞&#xff01; ArkTS是HarmonyOS优选的主力应用开发语言。它在TypeScript&#xff08;简称TS&#xff09;的基础上&#xff0c;匹配ArkUI…

桌球室台球房计费系统的安装方法,台球厅计时计费管理系统软件试用版教程

桌球室台球房计费系统的安装方法&#xff0c;台球厅计时计费管理系统软件试用版教程 一、前言 1、软件的安装方法&#xff1a; 如图&#xff0c;我们以 佳易王桌球计时计费软件V18.0版本为例说明。软件下载后的文件是压缩包文件&#xff0c;将文件解压后&#xff0c;将文件夹…

Blender教程-视图分割与3D游标-05

一、简介 Blender试图分割的作用主要有以下几点&#xff1a; 多角度查看模型&#xff1a;通过视图分割&#xff0c;用户可以从多个角度查看和比较模型&#xff0c;更好地理解和评估模型的细节和比例。多模型处理&#xff1a;当你在Blender中同时处理多个模型时&#xff0c;视…

AWS 专题学习 P11 (Machine Learning)

文章目录 专题总览机器学习服务1. Amazon Rekognition2. Amazon Rekognition - Content Moderation3. Amazon Transcribe4. Amazon Polly5. Amazon Polly - Lexicon & SSML6. Amazon TranscribeAmazon Translate7. Amazon Lex & Connect8. Amazon Comprehend9. Amazon …

k8s实例

k8s实例举例 &#xff08;1&#xff09;Kubernetes 区域可采用 Kubeadm 方式进行安装。 &#xff08;2&#xff09;要求在 Kubernetes 环境中&#xff0c;通过yaml文件的方式&#xff0c;创建2个Nginx Pod分别放置在两个不同的节点上&#xff0c;Pod使用动态PV类型的存储卷挂载…

DolphinDB学习(2):增删改查数据表(分布式表的基本操作)

文章目录 创建数据表1. 创建数据表全流程2. 核心&#xff1a;创建table3. 在已有的数据表中追加新的数据 数据表自身的操作1. 查询有哪些数据表2. 删除某张数据表3. 修改数据表的名称 博客里只介绍最常见的分区表&#xff08;createPartitionedTable&#xff09;的创建方法&…

机器学习笔记:地理加权回归(GWR)

1 传统的线性回归 机器学习笔记&#xff1a;线性回归_线性回归的读书笔记-CSDN博客 最优的β为&#xff1a; 2 地理加权回归&#xff08;GWR&#xff09; 2.1 模型概述 地理加权回归&#xff08;Geographically Weighted Regression&#xff0c;GWR&#xff09;是传统回归分…

详解矩阵的LDU分解

目录 一. 矩阵分解 二. 解方程 三. 例题说明 四. 矩阵的LDU分解 五. 矩阵三角分解的唯一性 一. 矩阵分解 其实我们可以把一个线性系统&#xff08;Linear System&#xff09;看成两个三角系统&#xff08;Triangular Systems&#xff09;&#xff0c;本文章将解释为什么可…

《PCI Express体系结构导读》随记 —— 第I篇 第3章 PCI总线的数据交换(5)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第I篇 第3章 PCI总线的数据交换&#xff08;4&#xff09; 3.2 PCI设备的数据传递 PCI设备的数据传递使用地址译码方式&#xff0c;当一个存储器读写总线事务到达PCI总线时&#xff0c;在这条总线上的所有PCI设…

【陈工笔记-Transformer】Transformer的基础认识

对Transformer生动形象的比喻 Transformer包括了Encoder和Decoder&#xff0c;在知乎上看到了对两个部分关系的一种理解&#xff0c;非常有趣。即&#xff0c;“一个人学习跳舞&#xff0c;Encoder是看别人是如何跳舞的&#xff0c;Decoder是将学习到的经验和记忆&#xff0c;…

旷视low-level系列(一):Bayer Pattern Unification and Bayer Preserving Au

文章目录 1. Motivation2. Contribution3. Methods3.1 BayerUnify3.2 BayerAug 4. CommentsReference 1. Motivation 对于RAW域去噪&#xff0c;通常会将单通道bayer格式的RAW图打包成4通道&#xff0c;然后送入神经网络。不同厂家生产的sensor出的RAW图可能具有不同的bayer模…

SpringBoot中阿里云OSS的使用

目录 1 登录/注册阿里云并进入控制台 2 进入OSS控制台 3 创建bucket 4 查看bucket 5 获取AccessKey 6 查看帮助文档 7 添加Maven依赖 8 获取示例代码并改造成工具类 9 测试 1 登录/注册阿里云并进入控制台 2 进入OSS控制台 3 创建bucket 4 查看bucket 5 获取AccessKe…

最优化基础 - (最优化问题分类、凸集)

系统学习最优化理论 什么是最优化问题&#xff1f; 决策问题&#xff1a; &#xff08;1&#xff09;决策变量 &#xff08;2&#xff09;目标函数&#xff08;一个或多个&#xff09; &#xff08;3&#xff09;一个可由可行策略组成的集合&#xff08;等式约束或者不等式约束…

Ubuntu使用Docker部署Redis并实现远程访问本地数据库

文章目录 前言1. 安装Docker步骤2. 使用docker拉取redis镜像3. 启动redis容器4. 本地连接测试4.1 安装redis图形化界面工具4.2 使用RDM连接测试 5. 公网远程访问本地redis5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定TCP地址远程访问 前言 本文主要介绍如何在Ub…

Windows10系统任务栏变小怎么处理

首先&#xff0c;邮件任务栏&#xff0c;点击任务栏设置&#xff1b; 然后&#xff0c;将小任务栏 使能关闭&#xff08;图中为打开状态&#xff09;&#xff1b; 或者&#xff0c;你也可以取消锁定任务栏&#xff0c;然后在任务栏的边缘&#xff0c;进行上下拉拖动&#xff…

漏洞复现-EduSoho任意文件读取漏洞(附漏洞检测脚本)

免责声明 文章中涉及的漏洞均已修复&#xff0c;敏感信息均已做打码处理&#xff0c;文章仅做经验分享用途&#xff0c;切勿当真&#xff0c;未授权的攻击属于非法行为&#xff01;文章中敏感信息均已做多层打马处理。传播、利用本文章所提供的信息而造成的任何直接或者间接的…

IDC机房交换机核心技术与应用指南

IDC机房交换机核心技术与应用指南 ​ 在这个快速发展的数字时代&#xff0c;数据中心作为信息技术的心脏&#xff0c;不仅承载着海量数据的处理、存储和传输&#xff0c;更是支撑着全球企业运营和互联网服务的关键基础设施。在众多构成数据中心的组件中&#xff0c;IDC机房交换…

【运行Python爬虫脚本示例】

主要内容&#xff1a;Python中的两个库的使用。 1、requests库&#xff1a;访问和获取网页内容&#xff0c; 2、beautifulsoup4库&#xff1a;解析网页内容。 一 python 爬取数据 1 使用requests库发送GET请求&#xff0c;并使用text属性获取网页内容。 然后可以对获取的网页…

2024 高级前端面试题之 JS 「精选篇」

该内容主要整理关于 JS 的相关面试题&#xff0c;其他内容面试题请移步至 「最新最全的前端面试题集锦」 查看。 JS模块精选篇 1. 数据类型基础1.1 JS内置类型1.2 null和undefined区别1.3 null是对象吗&#xff1f;为什么&#xff1f;1.4 1.toString()为什么可以调用&#xff1…