From 42b618bcac01f9239b132448cab0f0dcdc53573c Mon Sep 17 00:00:00 2001 From: caoyuan Date: Fri, 1 Dec 2023 17:57:56 +0800 Subject: [PATCH 1/4] =?UTF-8?q?fix:JbootCaredisCache=20=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=8F=82=E6=95=B0isUseFirstLevelOnly=20=E4=B8=BAtrue=E4=B8=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8redis=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cache/caredis/JbootCaredisCacheImpl.java | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java index 3f2e41c9..c02bf1bd 100644 --- a/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java +++ b/src/main/java/io/jboot/components/cache/caredis/JbootCaredisCacheImpl.java @@ -49,30 +49,32 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { private String clientId; private Cache keysCache = Caffeine.newBuilder() - .expireAfterAccess(10, TimeUnit.MINUTES) - .expireAfterWrite(10, TimeUnit.MINUTES) - .build(); + .expireAfterAccess(10, TimeUnit.MINUTES) + .expireAfterWrite(10, TimeUnit.MINUTES) + .build(); public JbootCaredisCacheImpl(JbootCacheConfig config) { super(config); this.caffeineCacheImpl = new CaffeineCacheImpl(config); - this.redisCacheImpl = new JbootRedisCacheImpl(config); + this.clientId = StrUtil.uuid(); this.serializer = Jboot.getSerializer(); - //在某些场景下,多个应用使用同一个 redis 实例,此时可以通过配置 cacheSyncMqChannel 来解决缓存冲突的问题 - if (StrUtil.isNotBlank(config.getCacheSyncMqChannel())){ - this.channel = config.getCacheSyncMqChannel(); - } - - this.redis = redisCacheImpl.getRedis(); - this.redis.subscribe(new BinaryJedisPubSub() { - @Override - public void onMessage(byte[] channel, byte[] message) { - JbootCaredisCacheImpl.this.onMessage((String) serializer.deserialize(channel), serializer.deserialize(message)); + if(!config.isUseFirstLevelOnly()) { + //在某些场景下,多个应用使用同一个 redis 实例,此时可以通过配置 cacheSyncMqChannel 来解决缓存冲突的问题 + if (StrUtil.isNotBlank(config.getCacheSyncMqChannel())) { + this.channel = config.getCacheSyncMqChannel(); } - }, serializer.serialize(channel)); + this.redisCacheImpl = new JbootRedisCacheImpl(config); + this.redis = redisCacheImpl.getRedis(); + this.redis.subscribe(new BinaryJedisPubSub() { + @Override + public void onMessage(byte[] channel, byte[] message) { + JbootCaredisCacheImpl.this.onMessage((String) serializer.deserialize(channel), serializer.deserialize(message)); + } + }, serializer.serialize(channel)); + } } @@ -241,8 +243,11 @@ public class JbootCaredisCacheImpl extends JbootCacheBase { private void publishMessage(int action, String cacheName, Object key) { clearKeysCache(cacheName); - JbootCaredisMessage message = new JbootCaredisMessage(clientId, action, cacheName, key); - redis.publish(serializer.serialize(channel), serializer.serialize(message)); + if(!config.isUseFirstLevelOnly()){ + JbootCaredisMessage message = new JbootCaredisMessage(clientId, action, cacheName, key); + redis.publish(serializer.serialize(channel), serializer.serialize(message)); + } + } private void clearKeysCache(String cacheName) { -- Gitee From b961af10a816aa79b555be9b522e8edd7b7fa6ef Mon Sep 17 00:00:00 2001 From: Roger Date: Sun, 14 Jul 2024 18:08:18 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix:JbootAopFactory=20=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E6=B3=A8=E8=A7=A3=E5=BD=93=E5=89=8D=E7=B1=BB=E5=8F=96=E4=B8=8D?= =?UTF-8?q?=E5=88=B0=EF=BC=8C=E5=88=99=E4=BB=8E=E5=BD=93=E5=89=8D=E7=B1=BB?= =?UTF-8?q?=E7=9A=84=E7=88=B6=E7=B1=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/aop/JbootAopFactory.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/main/java/io/jboot/aop/JbootAopFactory.java b/src/main/java/io/jboot/aop/JbootAopFactory.java index 3aab3f3c..937b466a 100644 --- a/src/main/java/io/jboot/aop/JbootAopFactory.java +++ b/src/main/java/io/jboot/aop/JbootAopFactory.java @@ -410,16 +410,17 @@ public class JbootAopFactory extends AopFactory { beanNameClassesMapping.put(beanName, implClass); } else { Class[] interfaceClasses = implClass.getInterfaces(); - if (interfaceClasses.length == 0) { - //add self - this.addMapping(implClass, implClass); - } else { - Class[] excludes = buildExcludeClasses(implClass); - for (Class interfaceClass : interfaceClasses) { - if (!inExcludes(interfaceClass, excludes)) { - this.addMapping(interfaceClass, implClass); - } + interfaceClasses = implClass.getSuperclass().getInterfaces(); + if (interfaceClasses.length == 0) { + this.addMapping(implClass, implClass); + continue; + } + } + Class[] excludes = buildExcludeClasses(implClass); + for (Class interfaceClass : interfaceClasses) { + if (!inExcludes(interfaceClass, excludes)) { + this.addMapping(interfaceClass, implClass); } } } -- Gitee From 2ea7f25090cdcd2de4f524c06993942385e96d54 Mon Sep 17 00:00:00 2001 From: Roger Date: Wed, 17 Jul 2024 13:48:04 +0800 Subject: [PATCH 3/4] =?UTF-8?q?fix:JbootJedisImpl=20redis=E9=87=8D?= =?UTF-8?q?=E8=BF=9E=E7=9A=84=E6=97=A5=E5=BF=97=E5=8F=98=E6=9B=B4=E4=B8=BA?= =?UTF-8?q?=E8=AD=A6=E5=91=8A=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/io/jboot/support/redis/jedis/JbootJedisImpl.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index cc58035e..b5028a1f 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -1495,7 +1495,7 @@ public class JbootJedisImpl extends JbootRedisBase { LOG.warn("Disconnect to redis channels : " + Arrays.toString(channels)); break; } catch (JedisConnectionException e) { - LOG.error("Failed connect to redis, reconnect it.", e); + LOG.warn("Failed connect to redis, reconnect it.", e); QuietlyUtil.sleepQuietly(1000); } finally { returnResource(jedis); @@ -1534,7 +1534,7 @@ public class JbootJedisImpl extends JbootRedisBase { LOG.warn("Disconnect to redis channel in subscribe binaryListener!"); break; } catch (Throwable e) { - LOG.error("Failed connect to redis, reconnect it.", e); + LOG.warn("Failed connect to redis, reconnect it.", e); QuietlyUtil.sleepQuietly(1000); } finally { if (jedis != null) { -- Gitee From e091438436fa70101a1a1289799c30d739b1a9b0 Mon Sep 17 00:00:00 2001 From: Roger Date: Fri, 23 Aug 2024 17:57:14 +0800 Subject: [PATCH 4/4] =?UTF-8?q?fix:Jboot=20new=20Thread()=20=E7=94=A8?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E6=9D=A5=E5=A4=84=E7=90=86=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/mq/qpidmq/JbootQpidmqImpl.java | 8 +- .../mq/redismq/JbootRedismqImpl.java | 7 +- .../redis/jedis/JbootJedisClusterImpl.java | 13 +- .../support/redis/jedis/JbootJedisImpl.java | 3002 ++++++++--------- .../sentinel/datasource/FileDataSource.java | 10 +- .../java/io/jboot/utils/NamedThreadPools.java | 65 +- 6 files changed, 1499 insertions(+), 1606 deletions(-) diff --git a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java index 746faca0..337d9f77 100644 --- a/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java +++ b/src/main/java/io/jboot/components/mq/qpidmq/JbootQpidmqImpl.java @@ -24,7 +24,9 @@ import io.jboot.components.mq.JbootmqConfig; import io.jboot.exception.JbootException; import io.jboot.exception.JbootIllegalConfigException; import io.jboot.utils.ArrayUtil; +import io.jboot.utils.NamedThreadPools; import io.jboot.utils.StrUtil; +import java.util.concurrent.ExecutorService; import org.apache.qpid.client.AMQAnyDestination; import org.apache.qpid.client.AMQConnection; import org.apache.qpid.jms.Connection; @@ -40,6 +42,8 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { private static final Log LOG = Log.getLog(JbootQpidmqImpl.class); + private static ExecutorService executorService = NamedThreadPools.newCachedThreadPool("jboot-qpid-receiveMsg-pool"); + private Connection connection = null; private boolean serializerEnable = true; private JbootQpidmqConfig qpidConfig = null; @@ -183,13 +187,13 @@ public class JbootQpidmqImpl extends JbootmqBase implements Jbootmq { Destination queue = new AMQAnyDestination(queueAddr); MessageConsumer queueConsumer = session.createConsumer(queue); queueThread = new Thread(new ReceiveMsgThread(queueConsumer, channel, serializerEnable)); - queueThread.start(); + executorService.execute(queueThread); String topicAddr = getTopicAddr(channel); Destination topic = new AMQAnyDestination(topicAddr); MessageConsumer topicConsumer = session.createConsumer(topic); topicThread = new Thread(new ReceiveMsgThread(topicConsumer, channel, serializerEnable)); - topicThread.start(); + executorService.execute(topicThread); } } diff --git a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java index f1249cd3..a1487760 100644 --- a/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java +++ b/src/main/java/io/jboot/components/mq/redismq/JbootRedismqImpl.java @@ -25,7 +25,9 @@ import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.JbootRedis; import io.jboot.support.redis.JbootRedisManager; import io.jboot.utils.ConfigUtil; +import io.jboot.utils.NamedThreadPools; import io.jboot.utils.StrUtil; +import java.util.concurrent.ExecutorService; import redis.clients.jedis.BinaryJedisPubSub; import java.util.HashMap; @@ -35,7 +37,7 @@ import java.util.Map; public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { private static final Log LOG = Log.getLog(JbootRedismqImpl.class); - + private static ExecutorService executorService = NamedThreadPools.newCachedThreadPool("jboot-redismq-pool"); private JbootRedis redis; private Thread dequeueThread; private BinaryJedisPubSub jedisPubSub; @@ -95,9 +97,8 @@ public class JbootRedismqImpl extends JbootmqBase implements Jbootmq, Runnable { channels[i] = channels[i] + "_" + database; } redis.subscribe(jedisPubSub, redis.keysToBytesArray(channels)); - dequeueThread = new Thread(this, "redis-dequeue-thread"); - dequeueThread.start(); + executorService.execute(dequeueThread); } @Override diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java index 1ce3c616..8c551fe0 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisClusterImpl.java @@ -20,8 +20,10 @@ import io.jboot.exception.JbootException; import io.jboot.support.redis.JbootRedisBase; import io.jboot.support.redis.JbootRedisConfig; import io.jboot.support.redis.RedisScanResult; +import io.jboot.utils.NamedThreadPools; import io.jboot.utils.QuietlyUtil; import io.jboot.utils.StrUtil; +import java.util.concurrent.ExecutorService; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import redis.clients.jedis.*; import redis.clients.jedis.exceptions.JedisConnectionException; @@ -42,6 +44,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { private int timeout = 2000; private int maxAttempts = 5; + private static ExecutorService executorService = NamedThreadPools.newCachedThreadPool("jboot-redismq-cluster-pool"); static final Log LOG = Log.getLog(JbootJedisClusterImpl.class); @@ -1255,7 +1258,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * A single JedisPubSub instance can be used to subscribe to multiple channels. * You can call subscribe or psubscribe on an existing JedisPubSub instance to change your subscriptions. */ - new Thread("jboot-redisCluster-subscribe-JedisPubSub") { + Thread t= new Thread("jboot-redisCluster-subscribe-JedisPubSub") { @Override public void run() { while (true) { @@ -1270,7 +1273,8 @@ public class JbootJedisClusterImpl extends JbootRedisBase { } } } - }.start(); + }; + executorService.execute(t); } /** @@ -1287,7 +1291,7 @@ public class JbootJedisClusterImpl extends JbootRedisBase { * A single JedisPubSub instance can be used to subscribe to multiple channels. * You can call subscribe or psubscribe on an existing JedisPubSub instance to change your subscriptions. */ - new Thread("jboot-redisCluster-subscribe-BinaryJedisPubSub") { + Thread t=new Thread("jboot-redisCluster-subscribe-BinaryJedisPubSub") { @Override public void run() { while (!isClose()) { @@ -1302,7 +1306,8 @@ public class JbootJedisClusterImpl extends JbootRedisBase { } } } - }.start(); + }; + executorService.execute(t); } diff --git a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java index b5028a1f..0a5900f6 100644 --- a/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java +++ b/src/main/java/io/jboot/support/redis/jedis/JbootJedisImpl.java @@ -1,17 +1,11 @@ /** * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package io.jboot.support.redis.jedis; @@ -20,1570 +14,1454 @@ import io.jboot.exception.JbootIllegalConfigException; import io.jboot.support.redis.JbootRedisBase; import io.jboot.support.redis.JbootRedisConfig; import io.jboot.support.redis.RedisScanResult; +import io.jboot.utils.NamedThreadPools; import io.jboot.utils.QuietlyUtil; import io.jboot.utils.StrUtil; -import redis.clients.jedis.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.stream.Collectors; +import redis.clients.jedis.BinaryJedisPubSub; +import redis.clients.jedis.Jedis; +import redis.clients.jedis.JedisPool; +import redis.clients.jedis.JedisPoolConfig; +import redis.clients.jedis.JedisPubSub; import redis.clients.jedis.exceptions.JedisConnectionException; import redis.clients.jedis.params.ScanParams; import redis.clients.jedis.resps.ScanResult; -import java.util.*; -import java.util.Map.Entry; -import java.util.stream.Collectors; - /** - * 参考: com.jfinal.plugin.redis - * JbootRedis 命令文档: http://redisdoc.com/ + * 参考: com.jfinal.plugin.redis JbootRedis 命令文档: http://redisdoc.com/ */ public class JbootJedisImpl extends JbootRedisBase { - protected JedisPool jedisPool; - protected JbootRedisConfig config; - - private static final Log LOG = Log.getLog(JbootJedisImpl.class); - - public JbootJedisImpl(JbootRedisConfig config) { - super(config); - - this.config = config; - - String host = config.getHost(); - Integer port = config.getPort(); - Integer timeout = config.getTimeout(); - String password = config.getPassword(); - Integer database = config.getDatabase(); - String clientName = config.getClientName(); - - if (host.contains(":")) { - port = Integer.valueOf(host.split(":")[1]); - } - - - JedisPoolConfig poolConfig = new JedisPoolConfig(); - - if (StrUtil.isNotBlank(config.getTestWhileIdle())) { - poolConfig.setTestWhileIdle(config.getTestWhileIdle()); - } - - if (StrUtil.isNotBlank(config.getTestOnBorrow())) { - poolConfig.setTestOnBorrow(config.getTestOnBorrow()); - } - - if (StrUtil.isNotBlank(config.getTestOnCreate())) { - poolConfig.setTestOnCreate(config.getTestOnCreate()); - } - - if (StrUtil.isNotBlank(config.getTestOnReturn())) { - poolConfig.setTestOnReturn(config.getTestOnReturn()); - } - - if (StrUtil.isNotBlank(config.getMinEvictableIdleTimeMillis())) { - poolConfig.setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis()); - } - - if (StrUtil.isNotBlank(config.getTimeBetweenEvictionRunsMillis())) { - poolConfig.setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis()); - } - - if (StrUtil.isNotBlank(config.getNumTestsPerEvictionRun())) { - poolConfig.setNumTestsPerEvictionRun(config.getNumTestsPerEvictionRun()); - } - - if (StrUtil.isNotBlank(config.getMaxTotal())) { - poolConfig.setMaxTotal(config.getMaxTotal()); - } - - if (StrUtil.isNotBlank(config.getMaxIdle())) { - poolConfig.setMaxIdle(config.getMaxIdle()); - } - - if (StrUtil.isNotBlank(config.getMinIdle())) { - poolConfig.setMinIdle(config.getMinIdle()); - } - - if (StrUtil.isNotBlank(config.getMaxWaitMillis())) { - poolConfig.setMaxWaitMillis(config.getMaxWaitMillis()); - } - - this.jedisPool = new JedisPool(poolConfig, host, port, timeout, timeout, password, database, clientName); - } - - - public JbootJedisImpl(JedisPool jedisPool) { - super(null); - this.jedisPool = jedisPool; - } - - /** - * 存放 key value 对到 redis - * 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。 - * 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。 - */ - @Override - public String set(Object key, Object value) { - Jedis jedis = getJedis(); - try { - return jedis.set(keyToBytes(key), valueToBytes(value)); - } finally { - returnResource(jedis); - } - } - - - @Override - public Long setnx(Object key, Object value) { - Jedis jedis = getJedis(); - try { - return jedis.setnx(keyToBytes(key), valueToBytes(value)); - } finally { - returnResource(jedis); - } - } - - /** - * 存放 key value 对到 redis - * 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。 - * 此方法用了修改 incr 等的值 - */ - @Override - public String setWithoutSerialize(Object key, Object value) { - Jedis jedis = getJedis(); - try { - return jedis.set(keyToBytes(key), value.toString().getBytes()); - } finally { - returnResource(jedis); - } - } - - - /** - * 存放 key value 对到 redis,并将 key 的生存时间设为 seconds (以秒为单位)。 - * 如果 key 已经存在, SETEX 命令将覆写旧值。 - */ - @Override - public String setex(Object key, int seconds, Object value) { - Jedis jedis = getJedis(); - try { - return jedis.setex(keyToBytes(key), seconds, valueToBytes(value)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回 key 所关联的 value 值 - * 如果 key 不存在那么返回特殊值 nil 。 - */ - @Override - @SuppressWarnings("unchecked") - public T get(Object key) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.get(keyToBytes(key))); - } finally { - returnResource(jedis); - } - } - - @Override - public String getWithoutSerialize(Object key) { - Jedis jedis = getJedis(); - try { - byte[] bytes = jedis.get(keyToBytes(key)); - if (bytes == null || bytes.length == 0) { - return null; - } - return new String(bytes); - } finally { - returnResource(jedis); - } - } - - - /** - * 删除给定的一个 key - * 不存在的 key 会被忽略。 - */ - @Override - public Long del(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.del(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 删除给定的多个 key - * 不存在的 key 会被忽略。 - */ - @Override - public Long del(Object... keys) { - if (keys == null || keys.length == 0) { - return 0L; - } - Jedis jedis = getJedis(); - try { - return jedis.del(keysToBytesArray(keys)); - } finally { - returnResource(jedis); - } - } - - /** - * 查找所有符合给定模式 pattern 的 key 。 - * KEYS * 匹配数据库中所有 key 。 - * KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 - * KEYS h*llo 匹配 hllo 和 heeeeello 等。 - * KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。 - * 特殊符号用 \ 隔开 - */ - @Override - public Set keys(String pattern) { - Jedis jedis = getJedis(); - try { - return jedis.keys(pattern); - } finally { - returnResource(jedis); - } - } - - /** - * 同时设置一个或多个 key-value 对。 - * 如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX 命令:它只会在所有给定 key 都不存在的情况下进行设置操作。 - * MSET 是一个原子性(atomic)操作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key 没有改变的情况,不可能发生。 - *

-     * 例子:
-     * Cache cache = RedisKit.use();			// 使用 JbootRedis 的 cache
-     * cache.mset("k1", "v1", "k2", "v2");		// 放入多个 key value 键值对
-     * List list = cache.mget("k1", "k2");		// 利用多个键值得到上面代码放入的值
-     * 
- */ - @Override - public String mset(Object... keysValues) { - if (keysValues.length % 2 != 0) { - throw new IllegalArgumentException("wrong number of arguments for met, keysValues length can not be odd"); - } - Jedis jedis = getJedis(); - try { - byte[][] kv = new byte[keysValues.length][]; - for (int i = 0; i < keysValues.length; i++) { - if (i % 2 == 0) { - kv[i] = keyToBytes(keysValues[i]); - } else { - kv[i] = valueToBytes(keysValues[i]); - } - } - return jedis.mset(kv); - } finally { - returnResource(jedis); - } - } - - /** - * 返回所有(一个或多个)给定 key 的值。 - * 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。 - */ - @Override - @SuppressWarnings("rawtypes") - public List mget(Object... keys) { - Jedis jedis = getJedis(); - try { - byte[][] keysBytesArray = keysToBytesArray(keys); - List data = jedis.mget(keysBytesArray); - return valueListFromBytesList(data); - } finally { - returnResource(jedis); - } - } - - /** - * 将 key 中储存的数字值减一。 - * 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。 - * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 - * 本操作的值限制在 64 位(bit)有符号数字表示之内。 - * 关于递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。 - */ - @Override - public Long decr(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.decr(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 将 key 所储存的值减去减量 decrement 。 - * 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY 操作。 - * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 - * 本操作的值限制在 64 位(bit)有符号数字表示之内。 - * 关于更多递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。 - */ - @Override - public Long decrBy(Object key, long longValue) { - Jedis jedis = getJedis(); - try { - return jedis.decrBy(keyToBytes(key), longValue); - } finally { - returnResource(jedis); - } - } - - /** - * 将 key 中储存的数字值增一。 - * 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。 - * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 - * 本操作的值限制在 64 位(bit)有符号数字表示之内。 - */ - @Override - public Long incr(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.incr(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 将 key 所储存的值加上增量 increment 。 - * 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。 - * 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 - * 本操作的值限制在 64 位(bit)有符号数字表示之内。 - * 关于递增(increment) / 递减(decrement)操作的更多信息,参见 INCR 命令。 - */ - @Override - public Long incrBy(Object key, long longValue) { - Jedis jedis = getJedis(); - try { - return jedis.incrBy(keyToBytes(key), longValue); - } finally { - returnResource(jedis); - } - } - - /** - * 检查给定 key 是否存在。 - */ - @Override - public boolean exists(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.exists(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 从当前数据库中随机返回(不删除)一个 key 。 - */ - @Override - public String randomKey() { - Jedis jedis = getJedis(); - try { - return jedis.randomKey(); - } finally { - returnResource(jedis); - } - } - - /** - * 将 key 改名为 newkey 。 - * 当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。 - * 当 newkey 已经存在时, RENAME 命令将覆盖旧值。 - */ - @Override - public String rename(Object oldkey, Object newkey) { - Jedis jedis = getJedis(); - try { - return jedis.rename(keyToBytes(oldkey), keyToBytes(newkey)); - } finally { - returnResource(jedis); - } - } - - /** - * 将当前数据库的 key 移动到给定的数据库 db 当中。 - * 如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果。 - * 因此,也可以利用这一特性,将 MOVE 当作锁(locking)原语(primitive)。 - */ - @Override - public Long move(Object key, int dbIndex) { - Jedis jedis = getJedis(); - try { - return jedis.move(keyToBytes(key), dbIndex); - } finally { - returnResource(jedis); - } - } - - /** - * 将 key 原子性地从当前实例传送到目标实例的指定数据库上,一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。 - */ - @Override - public String migrate(String host, int port, Object key, int destinationDb, int timeout) { - Jedis jedis = getJedis(); - try { - return jedis.migrate(host, port, keyToBytes(key), destinationDb, timeout); - } finally { - returnResource(jedis); - } - } - - /** - * 切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值。 - * 默认使用 0 号数据库。 - * 注意:在 Jedis 对象被关闭时,数据库又会重新被设置为初始值,所以本方法 select(...) - * 正常工作需要使用如下方式之一: - * 1:使用 RedisInterceptor,在本线程内共享同一个 Jedis 对象 - * 2:使用 JbootRedis.call(ICallback) 进行操作 - * 3:自行获取 Jedis 对象进行操作 - */ - @Override - public String select(int databaseIndex) { - Jedis jedis = getJedis(); - try { - return jedis.select(databaseIndex); - } finally { - returnResource(jedis); - } - } - - /** - * 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。 - * 在 JbootRedis 中,带有生存时间的 key 被称为『易失的』(volatile)。 - */ - @Override - public Long expire(Object key, int seconds) { - Jedis jedis = getJedis(); - try { - return jedis.expire(keyToBytes(key), seconds); - } finally { - returnResource(jedis); - } - } - - /** - * EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 - */ - @Override - public Long expireAt(Object key, long unixTime) { - Jedis jedis = getJedis(); - try { - return jedis.expireAt(keyToBytes(key), unixTime); - } finally { - returnResource(jedis); - } - } - - /** - * 这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。 - */ - @Override - public Long pexpire(Object key, long milliseconds) { - Jedis jedis = getJedis(); - try { - return jedis.pexpire(keyToBytes(key), milliseconds); - } finally { - returnResource(jedis); - } - } - - /** - * 这个命令和 EXPIREAT 命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳,而不是像 EXPIREAT 那样,以秒为单位。 - */ - @Override - public Long pexpireAt(Object key, long millisecondsTimestamp) { - Jedis jedis = getJedis(); - try { - return jedis.pexpireAt(keyToBytes(key), millisecondsTimestamp); - } finally { - returnResource(jedis); - } - } - - /** - * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 - * 当 key 存在但不是字符串类型时,返回一个错误。 - */ - @Override - @SuppressWarnings("unchecked") - public T getSet(Object key, Object value) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.getSet(keyToBytes(key), valueToBytes(value))); - } finally { - returnResource(jedis); - } - } - - /** - * 移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。 - */ - @Override - public Long persist(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.persist(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回 key 所储存的值的类型。 - */ - @Override - public String type(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.type(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 - */ - @Override - public Long ttl(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.ttl(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而不是像 TTL 命令那样,以秒为单位。 - */ - @Override - public Long pttl(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.pttl(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 对象被引用的数量 - */ - @Override - public Long objectRefcount(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.objectRefcount(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 对象没有被访问的空闲时间 - */ - @Override - public Long objectIdletime(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.objectIdletime(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 将哈希表 key 中的域 field 的值设为 value 。 - * 如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。 - * 如果域 field 已经存在于哈希表中,旧值将被覆盖。 - */ - @Override - public Long hset(Object key, Object field, Object value) { - Jedis jedis = getJedis(); - try { - return jedis.hset(keyToBytes(key), valueToBytes(field), valueToBytes(value)); - } finally { - returnResource(jedis); - } - } - - /** - * 同时将多个 field-value (域-值)对设置到哈希表 key 中。 - * 此命令会覆盖哈希表中已存在的域。 - * 如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。 - */ - @Override - public String hmset(Object key, Map hash) { - Jedis jedis = getJedis(); - try { - Map para = new HashMap(); - for (Entry e : hash.entrySet()) - para.put(valueToBytes(e.getKey()), valueToBytes(e.getValue())); - return jedis.hmset(keyToBytes(key), para); - } finally { - returnResource(jedis); - } - } - - /** - * 返回哈希表 key 中给定域 field 的值。 - */ - @Override - @SuppressWarnings("unchecked") - public T hget(Object key, Object field) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.hget(keyToBytes(key), valueToBytes(field))); - } finally { - returnResource(jedis); - } - } - - /** - * 返回哈希表 key 中,一个或多个给定域的值。 - * 如果给定的域不存在于哈希表,那么返回一个 nil 值。 - * 因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。 - */ - @Override - @SuppressWarnings("rawtypes") - public List hmget(Object key, Object... fields) { - Jedis jedis = getJedis(); - try { - List data = jedis.hmget(keyToBytes(key), valuesToBytesArray(fields)); - return valueListFromBytesList(data); - } finally { - returnResource(jedis); - } - } - - /** - * 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。 - */ - @Override - public Long hdel(Object key, Object... fields) { - Jedis jedis = getJedis(); - try { - return jedis.hdel(keyToBytes(key), valuesToBytesArray(fields)); - } finally { - returnResource(jedis); - } - } - - /** - * 查看哈希表 key 中,给定域 field 是否存在。 - */ - @Override - public boolean hexists(Object key, Object field) { - Jedis jedis = getJedis(); - try { - return jedis.hexists(keyToBytes(key), valueToBytes(field)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回哈希表 key 中,所有的域和值。 - * 在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。 - */ - @Override - @SuppressWarnings("rawtypes") - public Map hgetAll(Object key) { - Jedis jedis = getJedis(); - try { - Map data = jedis.hgetAll(keyToBytes(key)); - Map result = new HashMap(); - for (Entry e : data.entrySet()) - result.put(valueFromBytes(e.getKey()), valueFromBytes(e.getValue())); - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 返回哈希表 key 中所有域的值。 - */ - @Override - @SuppressWarnings("rawtypes") - public List hvals(Object key) { - Jedis jedis = getJedis(); - try { - List data = jedis.hvals(keyToBytes(key)); - return valueListFromBytesList(data); - } finally { - returnResource(jedis); - } - } - - /** - * 返回哈希表 key 中的所有域。 - * 底层实现此方法取名为 hfields 更为合适,在此仅为与底层保持一致 - */ - @Override - public Set hkeys(Object key) { - Jedis jedis = getJedis(); - try { - Set fieldSet = jedis.hkeys(keyToBytes(key)); - Set result = new HashSet(); - fieldSetFromBytesSet(fieldSet, result); - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 返回哈希表 key 中域的数量。 - */ - @Override - public Long hlen(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.hlen(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 为哈希表 key 中的域 field 的值加上增量 increment 。 - * 增量也可以为负数,相当于对给定域进行减法操作。 - * 如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。 - * 如果域 field 不存在,那么在执行命令前,域的值被初始化为 0 。 - * 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。 - * 本操作的值被限制在 64 位(bit)有符号数字表示之内。 - */ - @Override - public Long hincrBy(Object key, Object field, long value) { - Jedis jedis = getJedis(); - try { - return jedis.hincrBy(keyToBytes(key), valueToBytes(field), value); - } finally { - returnResource(jedis); - } - } - - /** - * 为哈希表 key 中的域 field 加上浮点数增量 increment 。 - * 如果哈希表中没有域 field ,那么 HINCRBYFLOAT 会先将域 field 的值设为 0 ,然后再执行加法操作。 - * 如果键 key 不存在,那么 HINCRBYFLOAT 会先创建一个哈希表,再创建域 field ,最后再执行加法操作。 - * 当以下任意一个条件发生时,返回一个错误: - * 1:域 field 的值不是字符串类型(因为 redis 中的数字和浮点数都以字符串的形式保存,所以它们都属于字符串类型) - * 2:域 field 当前的值或给定的增量 increment 不能解释(parse)为双精度浮点数(double precision floating point number) - * HINCRBYFLOAT 命令的详细功能和 INCRBYFLOAT 命令类似,请查看 INCRBYFLOAT 命令获取更多相关信息。 - */ - @Override - public Double hincrByFloat(Object key, Object field, double value) { - Jedis jedis = getJedis(); - try { - return jedis.hincrByFloat(keyToBytes(key), valueToBytes(field), value); - } finally { - returnResource(jedis); - } - } - - /** - * 返回列表 key 中,下标为 index 的元素。 - * 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 - * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 - * 如果 key 不是列表类型,返回一个错误。 - */ - @SuppressWarnings("unchecked") - - /** - * 返回列表 key 中,下标为 index 的元素。 - * 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素, - * 以 1 表示列表的第二个元素,以此类推。 - * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 - * 如果 key 不是列表类型,返回一个错误。 - */ - @Override - public T lindex(Object key, long index) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.lindex(keyToBytes(key), index)); - } finally { - returnResource(jedis); - } - } - - - /** - * 返回列表 key 的长度。 - * 如果 key 不存在,则 key 被解释为一个空列表,返回 0 . - * 如果 key 不是列表类型,返回一个错误。 - */ - @Override - public Long llen(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.llen(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 移除并返回列表 key 的头元素。 - */ - @Override - @SuppressWarnings("unchecked") - public T lpop(Object key) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.lpop(keyToBytes(key))); - } finally { - returnResource(jedis); - } - } - - /** - * 将一个或多个值 value 插入到列表 key 的表头 - * 如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说, - * 对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a , - * 这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。 - * 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 - * 当 key 存在但不是列表类型时,返回一个错误。 - */ - @Override - public Long lpush(Object key, Object... values) { - Jedis jedis = getJedis(); - try { - return jedis.lpush(keyToBytes(key), valuesToBytesArray(values)); - } finally { - returnResource(jedis); - } - } - - /** - * 将列表 key 下标为 index 的元素的值设置为 value 。 - * 当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。 - * 关于列表下标的更多信息,请参考 LINDEX 命令。 - */ - @Override - public String lset(Object key, long index, Object value) { - Jedis jedis = getJedis(); - try { - return jedis.lset(keyToBytes(key), index, valueToBytes(value)); - } finally { - returnResource(jedis); - } - } - - /** - * 根据参数 count 的值,移除列表中与参数 value 相等的元素。 - * count 的值可以是以下几种: - * count 大于 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。 - * count 小于 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。 - * count 等于 0 : 移除表中所有与 value 相等的值。 - */ - @Override - public Long lrem(Object key, long count, Object value) { - Jedis jedis = getJedis(); - try { - return jedis.lrem(keyToBytes(key), count, valueToBytes(value)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。 - * 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 - * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 - *
-     * 例子:
-     * 获取 list 中所有数据:cache.lrange(listKey, 0, -1);
-     * 获取 list 中下标 1 到 3 的数据: cache.lrange(listKey, 1, 3);
-     * 
- */ - @Override - @SuppressWarnings("rawtypes") - public List lrange(Object key, long start, long end) { - Jedis jedis = getJedis(); - try { - List data = jedis.lrange(keyToBytes(key), start, end); - if (data != null) { - return valueListFromBytesList(data); - } else { - return new ArrayList(0); - } - } finally { - returnResource(jedis); - } - } - - /** - * 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 - * 举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。 - * 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 - * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 - * 当 key 不是列表类型时,返回一个错误。 - */ - @Override - public String ltrim(Object key, long start, long end) { - Jedis jedis = getJedis(); - try { - return jedis.ltrim(keyToBytes(key), start, end); - } finally { - returnResource(jedis); - } - } - - /** - * 移除并返回列表 key 的尾元素。 - */ - @Override - @SuppressWarnings("unchecked") - public T rpop(Object key) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.rpop(keyToBytes(key))); - } finally { - returnResource(jedis); - } - } - - /** - * 命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作: - * 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。 - * 将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。 - */ - @Override - @SuppressWarnings("unchecked") - public T rpoplpush(Object srcKey, Object dstKey) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.rpoplpush(keyToBytes(srcKey), keyToBytes(dstKey))); - } finally { - returnResource(jedis); - } - } - - /** - * 将一个或多个值 value 插入到列表 key 的表尾(最右边)。 - * 如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾:比如 - * 对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c , - * 等同于执行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。 - * 如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。 - * 当 key 存在但不是列表类型时,返回一个错误。 - */ - @Override - public Long rpush(Object key, Object... values) { - Jedis jedis = getJedis(); - try { - return jedis.rpush(keyToBytes(key), valuesToBytesArray(values)); - } finally { - returnResource(jedis); - } - } - - /** - * BLPOP 是列表的阻塞式(blocking)弹出原语。 - * 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 - * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。 - */ - @Override - @SuppressWarnings("rawtypes") - public List blpop(Object... keys) { - Jedis jedis = getJedis(); - try { - List data = jedis.blpop(this.config.getTimeout(),keysToBytesArray(keys)); - return valueListFromBytesList(data); - } finally { - returnResource(jedis); - } - } - - /** - * BLPOP 是列表的阻塞式(blocking)弹出原语。 - * 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 - * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。 - */ - @Override - @SuppressWarnings("rawtypes") - public List blpop(Integer timeout, Object... keys) { - Jedis jedis = getJedis(); - try { - - //这里注意:第一个为key,第二个为value - List data = jedis.blpop(timeout, keysToBytesArray(keys)); - - if (data != null && data.size() == 2) { - List objects = new ArrayList<>(); - objects.add(new String(data.get(0))); - objects.add(valueFromBytes(data.get(1))); - return objects; - } - - //其他情况 - return null; - } finally { - returnResource(jedis); - } - } - - /** - * BRPOP 是列表的阻塞式(blocking)弹出原语。 - * 它是 RPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 - * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 - * 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。 - */ - @Override - @SuppressWarnings("rawtypes") - public List brpop(Object... keys) { - Jedis jedis = getJedis(); - try { - List data = jedis.brpop(this.config.getTimeout(),keysToBytesArray(keys)); - return valueListFromBytesList(data); - } finally { - returnResource(jedis); - } - } - - /** - * BRPOP 是列表的阻塞式(blocking)弹出原语。 - * 它是 RPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 - * 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 - * 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。 - */ - @Override - @SuppressWarnings("rawtypes") - public List brpop(Integer timeout, Object... keys) { - Jedis jedis = getJedis(); - try { - List data = jedis.brpop(timeout, keysToBytesArray(keys)); - return valueListFromBytesList(data); - } finally { - returnResource(jedis); - } - } - - /** - * 使用客户端向 JbootRedis 服务器发送一个 PING ,如果服务器运作正常的话,会返回一个 PONG 。 - * 通常用于测试与服务器的连接是否仍然生效,或者用于测量延迟值。 - */ - @Override - public String ping() { - Jedis jedis = getJedis(); - try { - return jedis.ping(); - } finally { - returnResource(jedis); - } - } - - /** - * 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。 - * 假如 key 不存在,则创建一个只包含 member 元素作成员的集合。 - * 当 key 不是集合类型时,返回一个错误。 - */ - @Override - public Long sadd(Object key, Object... members) { - Jedis jedis = getJedis(); - try { - return jedis.sadd(keyToBytes(key), valuesToBytesArray(members)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回集合 key 的基数(集合中元素的数量)。 - */ - @Override - public Long scard(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.scard(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 移除并返回集合中的一个随机元素。 - * 如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用 SRANDMEMBER 命令。 - */ - @Override - @SuppressWarnings("unchecked") - public T spop(Object key) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.spop(keyToBytes(key))); - } finally { - returnResource(jedis); - } - } - - /** - * 返回集合 key 中的所有成员。 - * 不存在的 key 被视为空集合。 - */ - @Override - @SuppressWarnings("rawtypes") - public Set smembers(Object key) { - Jedis jedis = getJedis(); - try { - Set data = jedis.smembers(keyToBytes(key)); - Set result = new HashSet(); - valueSetFromBytesSet(data, result); - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 判断 member 元素是否集合 key 的成员。 - */ - @Override - public boolean sismember(Object key, Object member) { - Jedis jedis = getJedis(); - try { - return jedis.sismember(keyToBytes(key), valueToBytes(member)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回多个集合的交集,多个集合由 keys 指定 - */ - @Override - @SuppressWarnings("rawtypes") - public Set sinter(Object... keys) { - Jedis jedis = getJedis(); - try { - Set data = jedis.sinter(keysToBytesArray(keys)); - Set result = new HashSet(); - valueSetFromBytesSet(data, result); - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 返回集合中的一个随机元素。 - */ - @Override - @SuppressWarnings("unchecked") - public T srandmember(Object key) { - Jedis jedis = getJedis(); - try { - return (T) valueFromBytes(jedis.srandmember(keyToBytes(key))); - } finally { - returnResource(jedis); - } - } - - /** - * 返回集合中的 count 个随机元素。 - * 从 JbootRedis 2.6 版本开始, SRANDMEMBER 命令接受可选的 count 参数: - * 如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。 - * 如果 count 大于等于集合基数,那么返回整个集合。 - * 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。 - * 该操作和 SPOP 相似,但 SPOP 将随机元素从集合中移除并返回,而 SRANDMEMBER 则仅仅返回随机元素,而不对集合进行任何改动。 - */ - @Override - @SuppressWarnings("rawtypes") - public List srandmember(Object key, int count) { - Jedis jedis = getJedis(); - try { - List data = jedis.srandmember(keyToBytes(key), count); - return valueListFromBytesList(data); - } finally { - returnResource(jedis); - } - } - - /** - * 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。 - */ - @Override - public Long srem(Object key, Object... members) { - Jedis jedis = getJedis(); - try { - return jedis.srem(keyToBytes(key), valuesToBytesArray(members)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回多个集合的并集,多个集合由 keys 指定 - * 不存在的 key 被视为空集。 - */ - @Override - @SuppressWarnings("rawtypes") - public Set sunion(Object... keys) { - Jedis jedis = getJedis(); - try { - Set data = jedis.sunion(keysToBytesArray(keys)); - Set result = new HashSet(); - valueSetFromBytesSet(data, result); - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 返回一个集合的全部成员,该集合是所有给定集合之间的差集。 - * 不存在的 key 被视为空集。 - */ - @Override - @SuppressWarnings("rawtypes") - public Set sdiff(Object... keys) { - Jedis jedis = getJedis(); - try { - Set data = jedis.sdiff(keysToBytesArray(keys)); - Set result = new HashSet(); - valueSetFromBytesSet(data, result); - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。 - * 如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值, - * 并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。 - */ - @Override - public Long zadd(Object key, double score, Object member) { - Jedis jedis = getJedis(); - try { - return jedis.zadd(keyToBytes(key), score, valueToBytes(member)); - } finally { - returnResource(jedis); - } - } - - @Override - public Long zadd(Object key, Map scoreMembers) { - Jedis jedis = getJedis(); - try { - Map para = new HashMap<>(); - for (Entry e : scoreMembers.entrySet()) { - para.put(valueToBytes(e.getKey()), e.getValue()); // valueToBytes is important - } - return jedis.zadd(keyToBytes(key), para); - } finally { - returnResource(jedis); - } - } - - /** - * 返回有序集 key 的基数。 - */ - @Override - public Long zcard(Object key) { - Jedis jedis = getJedis(); - try { - return jedis.zcard(keyToBytes(key)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。 - * 关于参数 min 和 max 的详细使用方法,请参考 ZRANGEBYSCORE 命令。 - */ - @Override - public Long zcount(Object key, double min, double max) { - Jedis jedis = getJedis(); - try { - return jedis.zcount(keyToBytes(key), min, max); - } finally { - returnResource(jedis); - } - } - - /** - * 为有序集 key 的成员 member 的 score 值加上增量 increment 。 - */ - @Override - public Double zincrby(Object key, double score, Object member) { - Jedis jedis = getJedis(); - try { - return jedis.zincrby(keyToBytes(key), score, valueToBytes(member)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回有序集 key 中,指定区间内的成员。 - * 其中成员的位置按 score 值递增(从小到大)来排序。 - * 具有相同 score 值的成员按字典序(lexicographical order )来排列。 - * 如果你需要成员按 score 值递减(从大到小)来排列,请使用 ZREVRANGE 命令。 - */ - @Override - @SuppressWarnings("rawtypes") - public List zrange(Object key, long start, long end) { - Jedis jedis = getJedis(); - try { - List data = jedis.zrange(keyToBytes(key), start, end); - List result = data.stream().map(d->valueFromBytes(d)).collect(Collectors.toList()); // 有序集合必须 LinkedHashSet - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 返回有序集 key 中,指定区间内的成员。 - * 其中成员的位置按 score 值递减(从大到小)来排列。 - * 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。 - * 除了成员按 score 值递减的次序排列这一点外, ZREVRANGE 命令的其他方面和 ZRANGE 命令一样。 - */ - @Override - @SuppressWarnings("rawtypes") - public List zrevrange(Object key, long start, long end) { - Jedis jedis = getJedis(); - try { - List data = jedis.zrevrange(keyToBytes(key), start, end); - List result = data.stream().map(d->valueFromBytes(d)).collect(Collectors.toList()); // 有序集合必须 LinkedHashSet - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。 - * 有序集成员按 score 值递增(从小到大)次序排列。 - */ - @Override - @SuppressWarnings("rawtypes") - public List zrangeByScore(Object key, double min, double max) { - Jedis jedis = getJedis(); - try { - List data = jedis.zrangeByScore(keyToBytes(key), min, max); - List result = data.stream().map(d->valueFromBytes(d)).collect(Collectors.toList()); // 有序集合必须 LinkedHashSet - return result; - } finally { - returnResource(jedis); - } - } - - /** - * 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。 - * 排名以 0 为底,也就是说, score 值最小的成员排名为 0 。 - * 使用 ZREVRANK 命令可以获得成员按 score 值递减(从大到小)排列的排名。 - */ - @Override - public Long zrank(Object key, Object member) { - Jedis jedis = getJedis(); - try { - return jedis.zrank(keyToBytes(key), valueToBytes(member)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。 - * 排名以 0 为底,也就是说, score 值最大的成员排名为 0 。 - * 使用 ZRANK 命令可以获得成员按 score 值递增(从小到大)排列的排名。 - */ - @Override - public Long zrevrank(Object key, Object member) { - Jedis jedis = getJedis(); - try { - return jedis.zrevrank(keyToBytes(key), valueToBytes(member)); - } finally { - returnResource(jedis); - } - } - - /** - * 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。 - * 当 key 存在但不是有序集类型时,返回一个错误。 - */ - @Override - public Long zrem(Object key, Object... members) { - Jedis jedis = getJedis(); - try { - return jedis.zrem(keyToBytes(key), valuesToBytesArray(members)); - } finally { - returnResource(jedis); - } - } - - /** - * 返回有序集 key 中,成员 member 的 score 值。 - * 如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 。 - */ - @Override - public Double zscore(Object key, Object member) { - Jedis jedis = getJedis(); - try { - return jedis.zscore(keyToBytes(key), valueToBytes(member)); - } finally { - returnResource(jedis); - } - } - - /** - * 发布 - * - * @param channel - * @param message - */ - @Override - public void publish(String channel, String message) { - Jedis jedis = getJedis(); - try { - jedis.publish(channel, message); - } finally { - returnResource(jedis); - } - } - - /** - * 发布 - * - * @param channel - * @param message - */ - @Override - public void publish(byte[] channel, byte[] message) { - Jedis jedis = getJedis(); - try { - jedis.publish(channel, message); - } finally { - returnResource(jedis); - } - } - - - /** - * 订阅 - * - * @param listener - * @param channels - */ - @Override - public void subscribe(JedisPubSub listener, final String... channels) { - /** - * https://github.com/xetorthio/jedis/wiki/AdvancedUsage - * Note that subscribe is a blocking operation because it will poll JbootRedis for responses on the thread that calls subscribe. - * A single JedisPubSub instance can be used to subscribe to multiple channels. - * You can call subscribe or psubscribe on an existing JedisPubSub instance to change your subscriptions. - */ - new Thread("jboot-redis-subscribe-JedisPubSub") { - @Override - public void run() { - while (true) { - Jedis jedis = getJedis(); - try { - // subscribe 方法是阻塞的,不用担心会走到returnResource,除非异常 - jedis.subscribe(listener, channels); - LOG.warn("Disconnect to redis channels : " + Arrays.toString(channels)); - break; - } catch (JedisConnectionException e) { - LOG.warn("Failed connect to redis, reconnect it.", e); - QuietlyUtil.sleepQuietly(1000); - } finally { - returnResource(jedis); - } - } - - - } - }.start(); - } - - /** - * 订阅 - * - * @param binaryListener - * @param channels - */ - @Override - public void subscribe(BinaryJedisPubSub binaryListener, final byte[]... channels) { - /** - * https://github.com/xetorthio/jedis/wiki/AdvancedUsage - * Note that subscribe is a blocking operation because it will poll JbootRedis for responses on the thread that calls subscribe. - * A single JedisPubSub instance can be used to subscribe to multiple channels. - * You can call subscribe or psubscribe on an existing JedisPubSub instance to change your subscriptions. - */ - new Thread("jboot-redis-subscribe-BinaryJedisPubSub") { - @Override - public void run() { - //订阅线程断开连接,需要进行重连 - while (!isClose()) { - Jedis jedis = null; - try { - jedis = jedisPool.getResource(); - // subscribe 方法是阻塞的,不用担心会走到returnResource,除非异常 - jedis.subscribe(binaryListener, channels); - LOG.warn("Disconnect to redis channel in subscribe binaryListener!"); - break; - } catch (Throwable e) { - LOG.warn("Failed connect to redis, reconnect it.", e); - QuietlyUtil.sleepQuietly(1000); - } finally { - if (jedis != null) { - returnResource(jedis); - } - } - } - } - }.start(); - } - - @Override - public RedisScanResult scan(String pattern, String cursor, int scanCount) { - ScanParams params = new ScanParams(); - params.match(pattern).count(scanCount); - try (Jedis jedis = getJedis()) { - ScanResult scanResult = jedis.scan(cursor, params); - return new RedisScanResult<>(scanResult.getCursor(), scanResult.getResult()); - } - } - - @Override - public Object eval(String script, int keyCount, String... params) { - Jedis jedis = getJedis(); - try { - return jedis.eval(script, keyCount, params); - } finally { - returnResource(jedis); - } - } - - public Jedis getJedis() { - try { - return jedisPool.getResource(); - } catch (JedisConnectionException e) { - throw new JbootIllegalConfigException("can not connect to redis host " + config.getHost() + ":" + config.getPort() + " ," + - " cause : " + e.toString(), e); - } - } - - public JedisPool getJedisPool() { - return jedisPool; - } - - public void returnResource(Jedis jedis) { - if (jedis != null) { - jedis.close(); - } - } + protected JedisPool jedisPool; + protected JbootRedisConfig config; + protected static ExecutorService executorService = NamedThreadPools.newCachedThreadPool("jboot-redis-subscribe-BinaryJedisPubSub-pool"); + + private static final Log LOG = Log.getLog(JbootJedisImpl.class); + + public JbootJedisImpl(JbootRedisConfig config) { + super(config); + + this.config = config; + + String host = config.getHost(); + Integer port = config.getPort(); + Integer timeout = config.getTimeout(); + String password = config.getPassword(); + Integer database = config.getDatabase(); + String clientName = config.getClientName(); + + if (host.contains(":")) { + port = Integer.valueOf(host.split(":")[1]); + } + + JedisPoolConfig poolConfig = new JedisPoolConfig(); + + if (StrUtil.isNotBlank(config.getTestWhileIdle())) { + poolConfig.setTestWhileIdle(config.getTestWhileIdle()); + } + + if (StrUtil.isNotBlank(config.getTestOnBorrow())) { + poolConfig.setTestOnBorrow(config.getTestOnBorrow()); + } + + if (StrUtil.isNotBlank(config.getTestOnCreate())) { + poolConfig.setTestOnCreate(config.getTestOnCreate()); + } + + if (StrUtil.isNotBlank(config.getTestOnReturn())) { + poolConfig.setTestOnReturn(config.getTestOnReturn()); + } + + if (StrUtil.isNotBlank(config.getMinEvictableIdleTimeMillis())) { + poolConfig.setMinEvictableIdleTimeMillis(config.getMinEvictableIdleTimeMillis()); + } + + if (StrUtil.isNotBlank(config.getTimeBetweenEvictionRunsMillis())) { + poolConfig.setTimeBetweenEvictionRunsMillis(config.getTimeBetweenEvictionRunsMillis()); + } + + if (StrUtil.isNotBlank(config.getNumTestsPerEvictionRun())) { + poolConfig.setNumTestsPerEvictionRun(config.getNumTestsPerEvictionRun()); + } + + if (StrUtil.isNotBlank(config.getMaxTotal())) { + poolConfig.setMaxTotal(config.getMaxTotal()); + } + + if (StrUtil.isNotBlank(config.getMaxIdle())) { + poolConfig.setMaxIdle(config.getMaxIdle()); + } + + if (StrUtil.isNotBlank(config.getMinIdle())) { + poolConfig.setMinIdle(config.getMinIdle()); + } + + if (StrUtil.isNotBlank(config.getMaxWaitMillis())) { + poolConfig.setMaxWaitMillis(config.getMaxWaitMillis()); + } + + this.jedisPool = new JedisPool(poolConfig, host, port, timeout, timeout, password, database, clientName); + } + + + public JbootJedisImpl(JedisPool jedisPool) { + super(null); + this.jedisPool = jedisPool; + } + + /** + * 存放 key value 对到 redis 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。 对于某个原本带有生存时间(TTL)的键来说, 当 SET 命令成功在这个键上执行时, 这个键原有的 TTL 将被清除。 + */ + @Override + public String set(Object key, Object value) { + Jedis jedis = getJedis(); + try { + return jedis.set(keyToBytes(key), valueToBytes(value)); + } finally { + returnResource(jedis); + } + } + + + @Override + public Long setnx(Object key, Object value) { + Jedis jedis = getJedis(); + try { + return jedis.setnx(keyToBytes(key), valueToBytes(value)); + } finally { + returnResource(jedis); + } + } + + /** + * 存放 key value 对到 redis 如果 key 已经持有其他值, SET 就覆写旧值,无视类型。 此方法用了修改 incr 等的值 + */ + @Override + public String setWithoutSerialize(Object key, Object value) { + Jedis jedis = getJedis(); + try { + return jedis.set(keyToBytes(key), value.toString().getBytes()); + } finally { + returnResource(jedis); + } + } + + + /** + * 存放 key value 对到 redis,并将 key 的生存时间设为 seconds (以秒为单位)。 如果 key 已经存在, SETEX 命令将覆写旧值。 + */ + @Override + public String setex(Object key, int seconds, Object value) { + Jedis jedis = getJedis(); + try { + return jedis.setex(keyToBytes(key), seconds, valueToBytes(value)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回 key 所关联的 value 值 如果 key 不存在那么返回特殊值 nil 。 + */ + @Override + @SuppressWarnings("unchecked") + public T get(Object key) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.get(keyToBytes(key))); + } finally { + returnResource(jedis); + } + } + + @Override + public String getWithoutSerialize(Object key) { + Jedis jedis = getJedis(); + try { + byte[] bytes = jedis.get(keyToBytes(key)); + if (bytes == null || bytes.length == 0) { + return null; + } + return new String(bytes); + } finally { + returnResource(jedis); + } + } + + + /** + * 删除给定的一个 key 不存在的 key 会被忽略。 + */ + @Override + public Long del(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.del(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 删除给定的多个 key 不存在的 key 会被忽略。 + */ + @Override + public Long del(Object... keys) { + if (keys == null || keys.length == 0) { + return 0L; + } + Jedis jedis = getJedis(); + try { + return jedis.del(keysToBytesArray(keys)); + } finally { + returnResource(jedis); + } + } + + /** + * 查找所有符合给定模式 pattern 的 key 。 KEYS * 匹配数据库中所有 key 。 KEYS h?llo 匹配 hello , hallo 和 hxllo 等。 KEYS h*llo 匹配 hllo 和 heeeeello 等。 KEYS h[ae]llo 匹配 hello 和 hallo ,但不匹配 hillo 。 特殊符号用 \ 隔开 + */ + @Override + public Set keys(String pattern) { + Jedis jedis = getJedis(); + try { + return jedis.keys(pattern); + } finally { + returnResource(jedis); + } + } + + /** + * 同时设置一个或多个 key-value 对。 如果某个给定 key 已经存在,那么 MSET 会用新值覆盖原来的旧值,如果这不是你所希望的效果,请考虑使用 MSETNX 命令:它只会在所有给定 key 都不存在的情况下进行设置操作。 MSET 是一个原子性(atomic)操作,所有给定 key 都会在同一时间内被设置,某些给定 key 被更新而另一些给定 key 没有改变的情况,不可能发生。 + *
+	 * 例子:
+	 * Cache cache = RedisKit.use();			// 使用 JbootRedis 的 cache
+	 * cache.mset("k1", "v1", "k2", "v2");		// 放入多个 key value 键值对
+	 * List list = cache.mget("k1", "k2");		// 利用多个键值得到上面代码放入的值
+	 * 
+ */ + @Override + public String mset(Object... keysValues) { + if (keysValues.length % 2 != 0) { + throw new IllegalArgumentException("wrong number of arguments for met, keysValues length can not be odd"); + } + Jedis jedis = getJedis(); + try { + byte[][] kv = new byte[keysValues.length][]; + for (int i = 0; i < keysValues.length; i++) { + if (i % 2 == 0) { + kv[i] = keyToBytes(keysValues[i]); + } else { + kv[i] = valueToBytes(keysValues[i]); + } + } + return jedis.mset(kv); + } finally { + returnResource(jedis); + } + } + + /** + * 返回所有(一个或多个)给定 key 的值。 如果给定的 key 里面,有某个 key 不存在,那么这个 key 返回特殊值 nil 。因此,该命令永不失败。 + */ + @Override + @SuppressWarnings("rawtypes") + public List mget(Object... keys) { + Jedis jedis = getJedis(); + try { + byte[][] keysBytesArray = keysToBytesArray(keys); + List data = jedis.mget(keysBytesArray); + return valueListFromBytesList(data); + } finally { + returnResource(jedis); + } + } + + /** + * 将 key 中储存的数字值减一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。 关于递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。 + */ + @Override + public Long decr(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.decr(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 将 key 所储存的值减去减量 decrement 。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY 操作。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。 关于更多递增(increment) / 递减(decrement)操作的更多信息,请参见 INCR 命令。 + */ + @Override + public Long decrBy(Object key, long longValue) { + Jedis jedis = getJedis(); + try { + return jedis.decrBy(keyToBytes(key), longValue); + } finally { + returnResource(jedis); + } + } + + /** + * 将 key 中储存的数字值增一。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。 + */ + @Override + public Long incr(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.incr(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 将 key 所储存的值加上增量 increment 。 如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 命令。 如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。 本操作的值限制在 64 位(bit)有符号数字表示之内。 关于递增(increment) / 递减(decrement)操作的更多信息,参见 INCR 命令。 + */ + @Override + public Long incrBy(Object key, long longValue) { + Jedis jedis = getJedis(); + try { + return jedis.incrBy(keyToBytes(key), longValue); + } finally { + returnResource(jedis); + } + } + + /** + * 检查给定 key 是否存在。 + */ + @Override + public boolean exists(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.exists(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 从当前数据库中随机返回(不删除)一个 key 。 + */ + @Override + public String randomKey() { + Jedis jedis = getJedis(); + try { + return jedis.randomKey(); + } finally { + returnResource(jedis); + } + } + + /** + * 将 key 改名为 newkey 。 当 key 和 newkey 相同,或者 key 不存在时,返回一个错误。 当 newkey 已经存在时, RENAME 命令将覆盖旧值。 + */ + @Override + public String rename(Object oldkey, Object newkey) { + Jedis jedis = getJedis(); + try { + return jedis.rename(keyToBytes(oldkey), keyToBytes(newkey)); + } finally { + returnResource(jedis); + } + } + + /** + * 将当前数据库的 key 移动到给定的数据库 db 当中。 如果当前数据库(源数据库)和给定数据库(目标数据库)有相同名字的给定 key ,或者 key 不存在于当前数据库,那么 MOVE 没有任何效果。 因此,也可以利用这一特性,将 MOVE 当作锁(locking)原语(primitive)。 + */ + @Override + public Long move(Object key, int dbIndex) { + Jedis jedis = getJedis(); + try { + return jedis.move(keyToBytes(key), dbIndex); + } finally { + returnResource(jedis); + } + } + + /** + * 将 key 原子性地从当前实例传送到目标实例的指定数据库上,一旦传送成功, key 保证会出现在目标实例上,而当前实例上的 key 会被删除。 + */ + @Override + public String migrate(String host, int port, Object key, int destinationDb, int timeout) { + Jedis jedis = getJedis(); + try { + return jedis.migrate(host, port, keyToBytes(key), destinationDb, timeout); + } finally { + returnResource(jedis); + } + } + + /** + * 切换到指定的数据库,数据库索引号 index 用数字值指定,以 0 作为起始索引值。 默认使用 0 号数据库。 注意:在 Jedis 对象被关闭时,数据库又会重新被设置为初始值,所以本方法 select(...) 正常工作需要使用如下方式之一: 1:使用 RedisInterceptor,在本线程内共享同一个 Jedis 对象 2:使用 JbootRedis.call(ICallback) 进行操作 3:自行获取 Jedis 对象进行操作 + */ + @Override + public String select(int databaseIndex) { + Jedis jedis = getJedis(); + try { + return jedis.select(databaseIndex); + } finally { + returnResource(jedis); + } + } + + /** + * 为给定 key 设置生存时间,当 key 过期时(生存时间为 0 ),它会被自动删除。 在 JbootRedis 中,带有生存时间的 key 被称为『易失的』(volatile)。 + */ + @Override + public Long expire(Object key, int seconds) { + Jedis jedis = getJedis(); + try { + return jedis.expire(keyToBytes(key), seconds); + } finally { + returnResource(jedis); + } + } + + /** + * EXPIREAT 的作用和 EXPIRE 类似,都用于为 key 设置生存时间。不同在于 EXPIREAT 命令接受的时间参数是 UNIX 时间戳(unix timestamp)。 + */ + @Override + public Long expireAt(Object key, long unixTime) { + Jedis jedis = getJedis(); + try { + return jedis.expireAt(keyToBytes(key), unixTime); + } finally { + returnResource(jedis); + } + } + + /** + * 这个命令和 EXPIRE 命令的作用类似,但是它以毫秒为单位设置 key 的生存时间,而不像 EXPIRE 命令那样,以秒为单位。 + */ + @Override + public Long pexpire(Object key, long milliseconds) { + Jedis jedis = getJedis(); + try { + return jedis.pexpire(keyToBytes(key), milliseconds); + } finally { + returnResource(jedis); + } + } + + /** + * 这个命令和 EXPIREAT 命令类似,但它以毫秒为单位设置 key 的过期 unix 时间戳,而不是像 EXPIREAT 那样,以秒为单位。 + */ + @Override + public Long pexpireAt(Object key, long millisecondsTimestamp) { + Jedis jedis = getJedis(); + try { + return jedis.pexpireAt(keyToBytes(key), millisecondsTimestamp); + } finally { + returnResource(jedis); + } + } + + /** + * 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 当 key 存在但不是字符串类型时,返回一个错误。 + */ + @Override + @SuppressWarnings("unchecked") + public T getSet(Object key, Object value) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.getSet(keyToBytes(key), valueToBytes(value))); + } finally { + returnResource(jedis); + } + } + + /** + * 移除给定 key 的生存时间,将这个 key 从『易失的』(带生存时间 key )转换成『持久的』(一个不带生存时间、永不过期的 key )。 + */ + @Override + public Long persist(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.persist(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回 key 所储存的值的类型。 + */ + @Override + public String type(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.type(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 以秒为单位,返回给定 key 的剩余生存时间(TTL, time to live)。 + */ + @Override + public Long ttl(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.ttl(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 这个命令类似于 TTL 命令,但它以毫秒为单位返回 key 的剩余生存时间,而不是像 TTL 命令那样,以秒为单位。 + */ + @Override + public Long pttl(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.pttl(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 对象被引用的数量 + */ + @Override + public Long objectRefcount(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.objectRefcount(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 对象没有被访问的空闲时间 + */ + @Override + public Long objectIdletime(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.objectIdletime(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 将哈希表 key 中的域 field 的值设为 value 。 如果 key 不存在,一个新的哈希表被创建并进行 HSET 操作。 如果域 field 已经存在于哈希表中,旧值将被覆盖。 + */ + @Override + public Long hset(Object key, Object field, Object value) { + Jedis jedis = getJedis(); + try { + return jedis.hset(keyToBytes(key), valueToBytes(field), valueToBytes(value)); + } finally { + returnResource(jedis); + } + } + + /** + * 同时将多个 field-value (域-值)对设置到哈希表 key 中。 此命令会覆盖哈希表中已存在的域。 如果 key 不存在,一个空哈希表被创建并执行 HMSET 操作。 + */ + @Override + public String hmset(Object key, Map hash) { + Jedis jedis = getJedis(); + try { + Map para = new HashMap(); + for (Entry e : hash.entrySet()) { + para.put(valueToBytes(e.getKey()), valueToBytes(e.getValue())); + } + return jedis.hmset(keyToBytes(key), para); + } finally { + returnResource(jedis); + } + } + + /** + * 返回哈希表 key 中给定域 field 的值。 + */ + @Override + @SuppressWarnings("unchecked") + public T hget(Object key, Object field) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.hget(keyToBytes(key), valueToBytes(field))); + } finally { + returnResource(jedis); + } + } + + /** + * 返回哈希表 key 中,一个或多个给定域的值。 如果给定的域不存在于哈希表,那么返回一个 nil 值。 因为不存在的 key 被当作一个空哈希表来处理,所以对一个不存在的 key 进行 HMGET 操作将返回一个只带有 nil 值的表。 + */ + @Override + @SuppressWarnings("rawtypes") + public List hmget(Object key, Object... fields) { + Jedis jedis = getJedis(); + try { + List data = jedis.hmget(keyToBytes(key), valuesToBytesArray(fields)); + return valueListFromBytesList(data); + } finally { + returnResource(jedis); + } + } + + /** + * 删除哈希表 key 中的一个或多个指定域,不存在的域将被忽略。 + */ + @Override + public Long hdel(Object key, Object... fields) { + Jedis jedis = getJedis(); + try { + return jedis.hdel(keyToBytes(key), valuesToBytesArray(fields)); + } finally { + returnResource(jedis); + } + } + + /** + * 查看哈希表 key 中,给定域 field 是否存在。 + */ + @Override + public boolean hexists(Object key, Object field) { + Jedis jedis = getJedis(); + try { + return jedis.hexists(keyToBytes(key), valueToBytes(field)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回哈希表 key 中,所有的域和值。 在返回值里,紧跟每个域名(field name)之后是域的值(value),所以返回值的长度是哈希表大小的两倍。 + */ + @Override + @SuppressWarnings("rawtypes") + public Map hgetAll(Object key) { + Jedis jedis = getJedis(); + try { + Map data = jedis.hgetAll(keyToBytes(key)); + Map result = new HashMap(); + for (Entry e : data.entrySet()) { + result.put(valueFromBytes(e.getKey()), valueFromBytes(e.getValue())); + } + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 返回哈希表 key 中所有域的值。 + */ + @Override + @SuppressWarnings("rawtypes") + public List hvals(Object key) { + Jedis jedis = getJedis(); + try { + List data = jedis.hvals(keyToBytes(key)); + return valueListFromBytesList(data); + } finally { + returnResource(jedis); + } + } + + /** + * 返回哈希表 key 中的所有域。 底层实现此方法取名为 hfields 更为合适,在此仅为与底层保持一致 + */ + @Override + public Set hkeys(Object key) { + Jedis jedis = getJedis(); + try { + Set fieldSet = jedis.hkeys(keyToBytes(key)); + Set result = new HashSet(); + fieldSetFromBytesSet(fieldSet, result); + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 返回哈希表 key 中域的数量。 + */ + @Override + public Long hlen(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.hlen(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 为哈希表 key 中的域 field 的值加上增量 increment 。 增量也可以为负数,相当于对给定域进行减法操作。 如果 key 不存在,一个新的哈希表被创建并执行 HINCRBY 命令。 如果域 field 不存在,那么在执行命令前,域的值被初始化为 0 。 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。 本操作的值被限制在 64 位(bit)有符号数字表示之内。 + */ + @Override + public Long hincrBy(Object key, Object field, long value) { + Jedis jedis = getJedis(); + try { + return jedis.hincrBy(keyToBytes(key), valueToBytes(field), value); + } finally { + returnResource(jedis); + } + } + + /** + * 为哈希表 key 中的域 field 加上浮点数增量 increment 。 如果哈希表中没有域 field ,那么 HINCRBYFLOAT 会先将域 field 的值设为 0 ,然后再执行加法操作。 如果键 key 不存在,那么 HINCRBYFLOAT 会先创建一个哈希表,再创建域 field ,最后再执行加法操作。 当以下任意一个条件发生时,返回一个错误: 1:域 field 的值不是字符串类型(因为 redis 中的数字和浮点数都以字符串的形式保存,所以它们都属于字符串类型) 2:域 field 当前的值或给定的增量 increment 不能解释(parse)为双精度浮点数(double precision floating point number) HINCRBYFLOAT + * 命令的详细功能和 INCRBYFLOAT 命令类似,请查看 INCRBYFLOAT 命令获取更多相关信息。 + */ + @Override + public Double hincrByFloat(Object key, Object field, double value) { + Jedis jedis = getJedis(); + try { + return jedis.hincrByFloat(keyToBytes(key), valueToBytes(field), value); + } finally { + returnResource(jedis); + } + } + + /** + * 返回列表 key 中,下标为 index 的元素。 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 如果 key 不是列表类型,返回一个错误。 + */ + @SuppressWarnings("unchecked") + + /** + * 返回列表 key 中,下标为 index 的元素。 + * 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素, + * 以 1 表示列表的第二个元素,以此类推。 + * 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 + * 如果 key 不是列表类型,返回一个错误。 + */ + @Override + public T lindex(Object key, long index) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.lindex(keyToBytes(key), index)); + } finally { + returnResource(jedis); + } + } + + + /** + * 返回列表 key 的长度。 如果 key 不存在,则 key 被解释为一个空列表,返回 0 . 如果 key 不是列表类型,返回一个错误。 + */ + @Override + public Long llen(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.llen(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 移除并返回列表 key 的头元素。 + */ + @Override + @SuppressWarnings("unchecked") + public T lpop(Object key) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.lpop(keyToBytes(key))); + } finally { + returnResource(jedis); + } + } + + /** + * 将一个或多个值 value 插入到列表 key 的表头 如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表头: 比如说, 对空列表 mylist 执行命令 LPUSH mylist a b c ,列表的值将是 c b a , 这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。 如果 key 不存在,一个空列表会被创建并执行 LPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。 + */ + @Override + public Long lpush(Object key, Object... values) { + Jedis jedis = getJedis(); + try { + return jedis.lpush(keyToBytes(key), valuesToBytesArray(values)); + } finally { + returnResource(jedis); + } + } + + /** + * 将列表 key 下标为 index 的元素的值设置为 value 。 当 index 参数超出范围,或对一个空列表( key 不存在)进行 LSET 时,返回一个错误。 关于列表下标的更多信息,请参考 LINDEX 命令。 + */ + @Override + public String lset(Object key, long index, Object value) { + Jedis jedis = getJedis(); + try { + return jedis.lset(keyToBytes(key), index, valueToBytes(value)); + } finally { + returnResource(jedis); + } + } + + /** + * 根据参数 count 的值,移除列表中与参数 value 相等的元素。 count 的值可以是以下几种: count 大于 0 : 从表头开始向表尾搜索,移除与 value 相等的元素,数量为 count 。 count 小于 0 : 从表尾开始向表头搜索,移除与 value 相等的元素,数量为 count 的绝对值。 count 等于 0 : 移除表中所有与 value 相等的值。 + */ + @Override + public Long lrem(Object key, long count, Object value) { + Jedis jedis = getJedis(); + try { + return jedis.lrem(keyToBytes(key), count, valueToBytes(value)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回列表 key 中指定区间内的元素,区间以偏移量 start 和 stop 指定。 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 + *
+	 * 例子:
+	 * 获取 list 中所有数据:cache.lrange(listKey, 0, -1);
+	 * 获取 list 中下标 1 到 3 的数据: cache.lrange(listKey, 1, 3);
+	 * 
+ */ + @Override + @SuppressWarnings("rawtypes") + public List lrange(Object key, long start, long end) { + Jedis jedis = getJedis(); + try { + List data = jedis.lrange(keyToBytes(key), start, end); + if (data != null) { + return valueListFromBytesList(data); + } else { + return new ArrayList(0); + } + } finally { + returnResource(jedis); + } + } + + /** + * 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 举个例子,执行命令 LTRIM list 0 2 ,表示只保留列表 list 的前三个元素,其余元素全部删除。 下标(index)参数 start 和 stop 都以 0 为底,也就是说,以 0 表示列表的第一个元素,以 1 表示列表的第二个元素,以此类推。 你也可以使用负数下标,以 -1 表示列表的最后一个元素, -2 表示列表的倒数第二个元素,以此类推。 当 key 不是列表类型时,返回一个错误。 + */ + @Override + public String ltrim(Object key, long start, long end) { + Jedis jedis = getJedis(); + try { + return jedis.ltrim(keyToBytes(key), start, end); + } finally { + returnResource(jedis); + } + } + + /** + * 移除并返回列表 key 的尾元素。 + */ + @Override + @SuppressWarnings("unchecked") + public T rpop(Object key) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.rpop(keyToBytes(key))); + } finally { + returnResource(jedis); + } + } + + /** + * 命令 RPOPLPUSH 在一个原子时间内,执行以下两个动作: 将列表 source 中的最后一个元素(尾元素)弹出,并返回给客户端。 将 source 弹出的元素插入到列表 destination ,作为 destination 列表的的头元素。 + */ + @Override + @SuppressWarnings("unchecked") + public T rpoplpush(Object srcKey, Object dstKey) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.rpoplpush(keyToBytes(srcKey), keyToBytes(dstKey))); + } finally { + returnResource(jedis); + } + } + + /** + * 将一个或多个值 value 插入到列表 key 的表尾(最右边)。 如果有多个 value 值,那么各个 value 值按从左到右的顺序依次插入到表尾:比如 对一个空列表 mylist 执行 RPUSH mylist a b c ,得出的结果列表为 a b c , 等同于执行命令 RPUSH mylist a 、 RPUSH mylist b 、 RPUSH mylist c 。 如果 key 不存在,一个空列表会被创建并执行 RPUSH 操作。 当 key 存在但不是列表类型时,返回一个错误。 + */ + @Override + public Long rpush(Object key, Object... values) { + Jedis jedis = getJedis(); + try { + return jedis.rpush(keyToBytes(key), valuesToBytesArray(values)); + } finally { + returnResource(jedis); + } + } + + /** + * BLPOP 是列表的阻塞式(blocking)弹出原语。 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。 + */ + @Override + @SuppressWarnings("rawtypes") + public List blpop(Object... keys) { + Jedis jedis = getJedis(); + try { + List data = jedis.blpop(this.config.getTimeout(), keysToBytesArray(keys)); + return valueListFromBytesList(data); + } finally { + returnResource(jedis); + } + } + + /** + * BLPOP 是列表的阻塞式(blocking)弹出原语。 它是 LPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BLPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的头元素。 + */ + @Override + @SuppressWarnings("rawtypes") + public List blpop(Integer timeout, Object... keys) { + Jedis jedis = getJedis(); + try { + + //这里注意:第一个为key,第二个为value + List data = jedis.blpop(timeout, keysToBytesArray(keys)); + + if (data != null && data.size() == 2) { + List objects = new ArrayList<>(); + objects.add(new String(data.get(0))); + objects.add(valueFromBytes(data.get(1))); + return objects; + } + + //其他情况 + return null; + } finally { + returnResource(jedis); + } + } + + /** + * BRPOP 是列表的阻塞式(blocking)弹出原语。 它是 RPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。 + */ + @Override + @SuppressWarnings("rawtypes") + public List brpop(Object... keys) { + Jedis jedis = getJedis(); + try { + List data = jedis.brpop(this.config.getTimeout(), keysToBytesArray(keys)); + return valueListFromBytesList(data); + } finally { + returnResource(jedis); + } + } + + /** + * BRPOP 是列表的阻塞式(blocking)弹出原语。 它是 RPOP 命令的阻塞版本,当给定列表内没有任何元素可供弹出的时候,连接将被 BRPOP 命令阻塞,直到等待超时或发现可弹出元素为止。 当给定多个 key 参数时,按参数 key 的先后顺序依次检查各个列表,弹出第一个非空列表的尾部元素。 关于阻塞操作的更多信息,请查看 BLPOP 命令, BRPOP 除了弹出元素的位置和 BLPOP 不同之外,其他表现一致。 + */ + @Override + @SuppressWarnings("rawtypes") + public List brpop(Integer timeout, Object... keys) { + Jedis jedis = getJedis(); + try { + List data = jedis.brpop(timeout, keysToBytesArray(keys)); + return valueListFromBytesList(data); + } finally { + returnResource(jedis); + } + } + + /** + * 使用客户端向 JbootRedis 服务器发送一个 PING ,如果服务器运作正常的话,会返回一个 PONG 。 通常用于测试与服务器的连接是否仍然生效,或者用于测量延迟值。 + */ + @Override + public String ping() { + Jedis jedis = getJedis(); + try { + return jedis.ping(); + } finally { + returnResource(jedis); + } + } + + /** + * 将一个或多个 member 元素加入到集合 key 当中,已经存在于集合的 member 元素将被忽略。 假如 key 不存在,则创建一个只包含 member 元素作成员的集合。 当 key 不是集合类型时,返回一个错误。 + */ + @Override + public Long sadd(Object key, Object... members) { + Jedis jedis = getJedis(); + try { + return jedis.sadd(keyToBytes(key), valuesToBytesArray(members)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回集合 key 的基数(集合中元素的数量)。 + */ + @Override + public Long scard(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.scard(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 移除并返回集合中的一个随机元素。 如果只想获取一个随机元素,但不想该元素从集合中被移除的话,可以使用 SRANDMEMBER 命令。 + */ + @Override + @SuppressWarnings("unchecked") + public T spop(Object key) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.spop(keyToBytes(key))); + } finally { + returnResource(jedis); + } + } + + /** + * 返回集合 key 中的所有成员。 不存在的 key 被视为空集合。 + */ + @Override + @SuppressWarnings("rawtypes") + public Set smembers(Object key) { + Jedis jedis = getJedis(); + try { + Set data = jedis.smembers(keyToBytes(key)); + Set result = new HashSet(); + valueSetFromBytesSet(data, result); + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 判断 member 元素是否集合 key 的成员。 + */ + @Override + public boolean sismember(Object key, Object member) { + Jedis jedis = getJedis(); + try { + return jedis.sismember(keyToBytes(key), valueToBytes(member)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回多个集合的交集,多个集合由 keys 指定 + */ + @Override + @SuppressWarnings("rawtypes") + public Set sinter(Object... keys) { + Jedis jedis = getJedis(); + try { + Set data = jedis.sinter(keysToBytesArray(keys)); + Set result = new HashSet(); + valueSetFromBytesSet(data, result); + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 返回集合中的一个随机元素。 + */ + @Override + @SuppressWarnings("unchecked") + public T srandmember(Object key) { + Jedis jedis = getJedis(); + try { + return (T) valueFromBytes(jedis.srandmember(keyToBytes(key))); + } finally { + returnResource(jedis); + } + } + + /** + * 返回集合中的 count 个随机元素。 从 JbootRedis 2.6 版本开始, SRANDMEMBER 命令接受可选的 count 参数: 如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同。 如果 count 大于等于集合基数,那么返回整个集合。 如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为 count 的绝对值。 该操作和 SPOP 相似,但 SPOP 将随机元素从集合中移除并返回,而 SRANDMEMBER 则仅仅返回随机元素,而不对集合进行任何改动。 + */ + @Override + @SuppressWarnings("rawtypes") + public List srandmember(Object key, int count) { + Jedis jedis = getJedis(); + try { + List data = jedis.srandmember(keyToBytes(key), count); + return valueListFromBytesList(data); + } finally { + returnResource(jedis); + } + } + + /** + * 移除集合 key 中的一个或多个 member 元素,不存在的 member 元素会被忽略。 + */ + @Override + public Long srem(Object key, Object... members) { + Jedis jedis = getJedis(); + try { + return jedis.srem(keyToBytes(key), valuesToBytesArray(members)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回多个集合的并集,多个集合由 keys 指定 不存在的 key 被视为空集。 + */ + @Override + @SuppressWarnings("rawtypes") + public Set sunion(Object... keys) { + Jedis jedis = getJedis(); + try { + Set data = jedis.sunion(keysToBytesArray(keys)); + Set result = new HashSet(); + valueSetFromBytesSet(data, result); + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 返回一个集合的全部成员,该集合是所有给定集合之间的差集。 不存在的 key 被视为空集。 + */ + @Override + @SuppressWarnings("rawtypes") + public Set sdiff(Object... keys) { + Jedis jedis = getJedis(); + try { + Set data = jedis.sdiff(keysToBytesArray(keys)); + Set result = new HashSet(); + valueSetFromBytesSet(data, result); + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。 如果某个 member 已经是有序集的成员,那么更新这个 member 的 score 值, 并通过重新插入这个 member 元素,来保证该 member 在正确的位置上。 + */ + @Override + public Long zadd(Object key, double score, Object member) { + Jedis jedis = getJedis(); + try { + return jedis.zadd(keyToBytes(key), score, valueToBytes(member)); + } finally { + returnResource(jedis); + } + } + + @Override + public Long zadd(Object key, Map scoreMembers) { + Jedis jedis = getJedis(); + try { + Map para = new HashMap<>(); + for (Entry e : scoreMembers.entrySet()) { + para.put(valueToBytes(e.getKey()), e.getValue()); // valueToBytes is important + } + return jedis.zadd(keyToBytes(key), para); + } finally { + returnResource(jedis); + } + } + + /** + * 返回有序集 key 的基数。 + */ + @Override + public Long zcard(Object key) { + Jedis jedis = getJedis(); + try { + return jedis.zcard(keyToBytes(key)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回有序集 key 中, score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量。 关于参数 min 和 max 的详细使用方法,请参考 ZRANGEBYSCORE 命令。 + */ + @Override + public Long zcount(Object key, double min, double max) { + Jedis jedis = getJedis(); + try { + return jedis.zcount(keyToBytes(key), min, max); + } finally { + returnResource(jedis); + } + } + + /** + * 为有序集 key 的成员 member 的 score 值加上增量 increment 。 + */ + @Override + public Double zincrby(Object key, double score, Object member) { + Jedis jedis = getJedis(); + try { + return jedis.zincrby(keyToBytes(key), score, valueToBytes(member)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回有序集 key 中,指定区间内的成员。 其中成员的位置按 score 值递增(从小到大)来排序。 具有相同 score 值的成员按字典序(lexicographical order )来排列。 如果你需要成员按 score 值递减(从大到小)来排列,请使用 ZREVRANGE 命令。 + */ + @Override + @SuppressWarnings("rawtypes") + public List zrange(Object key, long start, long end) { + Jedis jedis = getJedis(); + try { + List data = jedis.zrange(keyToBytes(key), start, end); + List result = data.stream().map(d -> valueFromBytes(d)).collect(Collectors.toList()); // 有序集合必须 LinkedHashSet + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 返回有序集 key 中,指定区间内的成员。 其中成员的位置按 score 值递减(从大到小)来排列。 具有相同 score 值的成员按字典序的逆序(reverse lexicographical order)排列。 除了成员按 score 值递减的次序排列这一点外, ZREVRANGE 命令的其他方面和 ZRANGE 命令一样。 + */ + @Override + @SuppressWarnings("rawtypes") + public List zrevrange(Object key, long start, long end) { + Jedis jedis = getJedis(); + try { + List data = jedis.zrevrange(keyToBytes(key), start, end); + List result = data.stream().map(d -> valueFromBytes(d)).collect(Collectors.toList()); // 有序集合必须 LinkedHashSet + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。 有序集成员按 score 值递增(从小到大)次序排列。 + */ + @Override + @SuppressWarnings("rawtypes") + public List zrangeByScore(Object key, double min, double max) { + Jedis jedis = getJedis(); + try { + List data = jedis.zrangeByScore(keyToBytes(key), min, max); + List result = data.stream().map(d -> valueFromBytes(d)).collect(Collectors.toList()); // 有序集合必须 LinkedHashSet + return result; + } finally { + returnResource(jedis); + } + } + + /** + * 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递增(从小到大)顺序排列。 排名以 0 为底,也就是说, score 值最小的成员排名为 0 。 使用 ZREVRANK 命令可以获得成员按 score 值递减(从大到小)排列的排名。 + */ + @Override + public Long zrank(Object key, Object member) { + Jedis jedis = getJedis(); + try { + return jedis.zrank(keyToBytes(key), valueToBytes(member)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回有序集 key 中成员 member 的排名。其中有序集成员按 score 值递减(从大到小)排序。 排名以 0 为底,也就是说, score 值最大的成员排名为 0 。 使用 ZRANK 命令可以获得成员按 score 值递增(从小到大)排列的排名。 + */ + @Override + public Long zrevrank(Object key, Object member) { + Jedis jedis = getJedis(); + try { + return jedis.zrevrank(keyToBytes(key), valueToBytes(member)); + } finally { + returnResource(jedis); + } + } + + /** + * 移除有序集 key 中的一个或多个成员,不存在的成员将被忽略。 当 key 存在但不是有序集类型时,返回一个错误。 + */ + @Override + public Long zrem(Object key, Object... members) { + Jedis jedis = getJedis(); + try { + return jedis.zrem(keyToBytes(key), valuesToBytesArray(members)); + } finally { + returnResource(jedis); + } + } + + /** + * 返回有序集 key 中,成员 member 的 score 值。 如果 member 元素不是有序集 key 的成员,或 key 不存在,返回 nil 。 + */ + @Override + public Double zscore(Object key, Object member) { + Jedis jedis = getJedis(); + try { + return jedis.zscore(keyToBytes(key), valueToBytes(member)); + } finally { + returnResource(jedis); + } + } + + /** + * 发布 + * + * @param channel + * @param message + */ + @Override + public void publish(String channel, String message) { + Jedis jedis = getJedis(); + try { + jedis.publish(channel, message); + } finally { + returnResource(jedis); + } + } + + /** + * 发布 + * + * @param channel + * @param message + */ + @Override + public void publish(byte[] channel, byte[] message) { + Jedis jedis = getJedis(); + try { + jedis.publish(channel, message); + } finally { + returnResource(jedis); + } + } + + + /** + * 订阅 + * + * @param listener + * @param channels + */ + @Override + public void subscribe(JedisPubSub listener, final String... channels) { + /** + * https://github.com/xetorthio/jedis/wiki/AdvancedUsage + * Note that subscribe is a blocking operation because it will poll JbootRedis for responses on the thread that calls subscribe. + * A single JedisPubSub instance can be used to subscribe to multiple channels. + * You can call subscribe or psubscribe on an existing JedisPubSub instance to change your subscriptions. + */ + Thread t = new Thread("jboot-redis-subscribe-JedisPubSub") { + @Override + public void run() { + while (true) { + Jedis jedis = getJedis(); + try { + // subscribe 方法是阻塞的,不用担心会走到returnResource,除非异常 + jedis.subscribe(listener, channels); + LOG.warn("Disconnect to redis channels : " + Arrays.toString(channels)); + break; + } catch (JedisConnectionException e) { + LOG.warn("Failed connect to redis, reconnect it.", e); + QuietlyUtil.sleepQuietly(1000); + } finally { + returnResource(jedis); + } + } + } + }; + executorService.execute(t); + } + + /** + * 订阅 + * + * @param binaryListener + * @param channels + */ + @Override + public void subscribe(BinaryJedisPubSub binaryListener, final byte[]... channels) { + /** + * https://github.com/xetorthio/jedis/wiki/AdvancedUsage + * Note that subscribe is a blocking operation because it will poll JbootRedis for responses on the thread that calls subscribe. + * A single JedisPubSub instance can be used to subscribe to multiple channels. + * You can call subscribe or psubscribe on an existing JedisPubSub instance to change your subscriptions. + */ + Thread t = new Thread("jboot-redis-subscribe-BinaryJedisPubSub") { + @Override + public void run() { + //订阅线程断开连接,需要进行重连 + while (!isClose()) { + Jedis jedis = null; + try { + jedis = jedisPool.getResource(); + // subscribe 方法是阻塞的,不用担心会走到returnResource,除非异常 + jedis.subscribe(binaryListener, channels); + LOG.warn("Disconnect to redis channel in subscribe binaryListener!"); + break; + } catch (Throwable e) { + LOG.warn("Failed connect to redis, reconnect it.", e); + QuietlyUtil.sleepQuietly(1000); + } finally { + if (jedis != null) { + returnResource(jedis); + } + } + } + } + }; + executorService.execute(t); + } + + @Override + public RedisScanResult scan(String pattern, String cursor, int scanCount) { + ScanParams params = new ScanParams(); + params.match(pattern).count(scanCount); + try (Jedis jedis = getJedis()) { + ScanResult scanResult = jedis.scan(cursor, params); + return new RedisScanResult<>(scanResult.getCursor(), scanResult.getResult()); + } + } + + @Override + public Object eval(String script, int keyCount, String... params) { + Jedis jedis = getJedis(); + try { + return jedis.eval(script, keyCount, params); + } finally { + returnResource(jedis); + } + } + + public Jedis getJedis() { + try { + return jedisPool.getResource(); + } catch (JedisConnectionException e) { + throw new JbootIllegalConfigException("can not connect to redis host " + config.getHost() + ":" + config.getPort() + " ," + + " cause : " + e.toString(), e); + } + } + + public JedisPool getJedisPool() { + return jedisPool; + } + + public void returnResource(Jedis jedis) { + if (jedis != null) { + jedis.close(); + } + } } diff --git a/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java b/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java index 3139e784..c0ae5f3d 100644 --- a/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java +++ b/src/main/java/io/jboot/support/sentinel/datasource/FileDataSource.java @@ -19,14 +19,16 @@ import com.alibaba.csp.sentinel.datasource.AbstractDataSource; import com.alibaba.csp.sentinel.datasource.Converter; import com.jfinal.kit.LogKit; import io.jboot.utils.FileUtil; +import io.jboot.utils.NamedThreadPools; import io.jboot.utils.QuietlyUtil; import java.io.File; +import java.util.concurrent.ExecutorService; public class FileDataSource extends AbstractDataSource { private File file; - + private static ExecutorService executorService = NamedThreadPools.newCachedThreadPool("jboot-file-dataSource-pool"); private boolean fileExists; private long fileLastModified = -1; private long fileLength = -1; @@ -44,7 +46,7 @@ public class FileDataSource extends AbstractDataSource { updateProperties(); - new Thread(() -> { + Thread t= new Thread(() -> { while (!isClosed) { try { doReadAndUpdateProperties(); @@ -53,7 +55,9 @@ public class FileDataSource extends AbstractDataSource { } QuietlyUtil.sleepQuietly(5000); } - }, "jboot-sentinel-file-reader").start(); + }, "jboot-sentinel-file-reader"); + executorService.execute(t); + } diff --git a/src/main/java/io/jboot/utils/NamedThreadPools.java b/src/main/java/io/jboot/utils/NamedThreadPools.java index 212edcaa..51508f8d 100644 --- a/src/main/java/io/jboot/utils/NamedThreadPools.java +++ b/src/main/java/io/jboot/utils/NamedThreadPools.java @@ -1,21 +1,22 @@ /** * Copyright (c) 2015-2022, Michael Yang 杨福海 (fuhai999@gmail.com). *

- * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at *

* http://www.apache.org/licenses/LICENSE-2.0 *

- * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package io.jboot.utils; -import java.util.concurrent.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; /** * @author michael yang (fuhai999@gmail.com) @@ -23,37 +24,37 @@ import java.util.concurrent.*; */ public class NamedThreadPools { - public static ExecutorService newFixedThreadPool(String prefix) { - int nThreads = Runtime.getRuntime().availableProcessors(); - return newFixedThreadPool(nThreads, prefix); - } + public static ExecutorService newFixedThreadPool(String prefix) { + int nThreads = Runtime.getRuntime().availableProcessors(); + return newFixedThreadPool(nThreads, prefix); + } - public static ExecutorService newFixedThreadPool(int nThreads, String name) { - return Executors.newFixedThreadPool(nThreads, new NamedThreadFactory(name)); - } + public static ExecutorService newFixedThreadPool(int nThreads, String name) { + return Executors.newFixedThreadPool(nThreads, new NamedThreadFactory(name)); + } - public static ExecutorService newCachedThreadPool(String name) { - return newCachedThreadPool(new NamedThreadFactory(name)); - } + public static ExecutorService newCachedThreadPool(String name) { + return newCachedThreadPool(new NamedThreadFactory(name)); + } - public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { - return new ThreadPoolExecutor(0, Integer.MAX_VALUE, - 60L, TimeUnit.SECONDS, - new SynchronousQueue(), - threadFactory); - } + public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) { + return new ThreadPoolExecutor(0, Runtime.getRuntime().availableProcessors(), + 60L, TimeUnit.SECONDS, + new SynchronousQueue(), + threadFactory); + } - public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, String name) { - return newScheduledThreadPool(corePoolSize, new NamedThreadFactory(name)); - } + public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, String name) { + return newScheduledThreadPool(corePoolSize, new NamedThreadFactory(name)); + } - public static ScheduledExecutorService newScheduledThreadPool( - int corePoolSize, ThreadFactory threadFactory) { - return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); - } + public static ScheduledExecutorService newScheduledThreadPool( + int corePoolSize, ThreadFactory threadFactory) { + return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory); + } } -- Gitee