org.redkalex.cache.redis.RedisCacheSource Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of redkale-plugins Show documentation
Show all versions of redkale-plugins Show documentation
Redkale-Plugins -- java framework
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package org.redkalex.cache.redis;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.net.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.*;
import org.redkale.annotation.*;
import org.redkale.annotation.AutoLoad;
import org.redkale.annotation.ResourceType;
import static org.redkale.boot.Application.RESNAME_APP_CLIENT_ASYNCGROUP;
import org.redkale.convert.Convert;
import org.redkale.convert.json.JsonConvert;
import org.redkale.inject.ResourceEvent;
import org.redkale.net.*;
import org.redkale.net.client.ClientAddress;
import org.redkale.net.client.ClientConnection;
import org.redkale.net.client.ClientFuture;
import org.redkale.net.client.ClientMessageListener;
import org.redkale.net.client.ClientResponse;
import org.redkale.service.Local;
import org.redkale.source.*;
import org.redkale.util.*;
import static org.redkale.util.Utility.*;
import static org.redkalex.cache.redis.RedisCacheRequest.BYTES_COUNT;
import static org.redkalex.cache.redis.RedisCacheRequest.BYTES_MATCH;
/**
* 详情见: https://redkale.org
*
* @author zhangjx
*/
@Local
@AutoLoad(false)
@ResourceType(CacheSource.class)
@SourceType(RedisSource.class)
public final class RedisCacheSource extends RedisSource {
// System.getProperty("os.name").contains("Window") || System.getProperty("os.name").contains("Mac");
static final boolean debug = false;
protected static final byte[] NX = "NX".getBytes();
protected static final byte[] EX = "EX".getBytes();
protected static final byte[] PX = "PX".getBytes();
protected static final byte[] CHANNELS = "CHANNELS".getBytes();
private final Logger logger = Logger.getLogger(getClass().getSimpleName());
@Resource(name = RESNAME_APP_CLIENT_ASYNCGROUP, required = false)
private AsyncGroup clientAsyncGroup;
private RedisCacheClient client;
private InetSocketAddress address;
private RedisCacheConnection pubSubConn;
private final ReentrantLock pubSubLock = new ReentrantLock();
private ScheduledThreadPoolExecutor pubSubScheduler;
private final ReentrantLock schedulerLock = new ReentrantLock();
// key: topic
private final Map>> pubSubListeners =
new ConcurrentHashMap<>();
@Override
public void init(AnyValue conf) {
super.init(conf);
if (conf == null) {
conf = AnyValue.create();
}
initClient(conf);
}
private void initClient(AnyValue conf) {
RedisConfig config = RedisConfig.create(conf);
if (config.getAddresses().size() != 1) {
throw new RedkaleException(
"Only one address supported for " + getClass().getSimpleName());
}
String oneAddr = config.getAddresses().get(0);
if (oneAddr.contains("://")) {
URI uri = URI.create(oneAddr);
address = new InetSocketAddress(uri.getHost(), uri.getPort() > 0 ? uri.getPort() : 6379);
} else {
int pos = oneAddr.indexOf(':');
address = new InetSocketAddress(
pos < 0 ? oneAddr : oneAddr.substring(0, pos),
pos < 0 ? 6379 : Integer.parseInt(oneAddr.substring(pos + 1)));
}
AsyncGroup ioGroup = clientAsyncGroup;
if (clientAsyncGroup == null) {
String f = "Redkalex-Redis-IOThread-" + resourceName() + "-%s";
ioGroup = AsyncGroup.create(
f,
workExecutor,
ByteBufferPool.DEFAULT_BUFFER_TCP_CAPACITY,
ByteBufferPool.DEFAULT_BUFFER_POOL_SIZE)
.start();
}
RedisCacheClient old = this.client;
this.client = new RedisCacheClient(
appName,
resourceName(),
ioGroup,
resourceName() + "." + config.getDb(),
new ClientAddress(address),
config.getMaxconns(Utility.cpus()),
config.getPipelines(),
isEmpty(config.getPassword()) ? null : new RedisCacheReqAuth(config.getPassword()),
config.getDb() < 1 ? null : new RedisCacheReqDB(config.getDb()));
if (this.pubSubConn != null) {
this.pubSubConn.dispose(null);
this.pubSubConn = null;
}
if (old != null) {
old.close();
}
if (!pubSubListeners.isEmpty()) {
pubSubConn().join();
}
// if (logger.isLoggable(Level.FINE)) {
// logger.log(Level.FINE, RedisCacheSource.class.getSimpleName() + ": addr=" + address + ", db=" +
// db);
// }
}
@Override
@ResourceChanged
public void onResourceChange(ResourceEvent[] events) {
if (Utility.isEmpty(events)) {
return;
}
StringBuilder sb = new StringBuilder();
for (ResourceEvent event : events) {
sb.append("CacheSource(name=")
.append(resourceName())
.append(") change '")
.append(event.name())
.append("' to '")
.append(event.coverNewValue())
.append("'\r\n");
}
initClient(this.conf);
if (sb.length() > 0) {
logger.log(Level.INFO, sb.toString());
}
}
@Override
public final String getType() {
return "redis";
}
@Override
public String toString() {
return getClass().getSimpleName() + "{name=" + resourceName() + ", addrs=" + this.address + ", db=" + this.db
+ "}";
}
@Override
public void destroy(AnyValue conf) {
super.destroy(conf);
if (client != null) {
client.close();
}
if (pubSubScheduler != null) {
pubSubScheduler.shutdown();
}
}
protected ClientMessageListener createMessageListener(long startTime) {
return new ClientMessageListener() {
@Override
public void onMessage(ClientConnection conn, ClientResponse resp) {
if (resp.getCause() == null) {
RedisCacheResult result = (RedisCacheResult) resp.getMessage();
if (result.getFrameValue() == null) {
List events = result.getListValue(null, null, byte[].class);
String type = new String(events.get(0), StandardCharsets.UTF_8);
if (events.size() == 3 && "message".equals(type)) {
String channel = new String(events.get(1), StandardCharsets.UTF_8);
Set> set = pubSubListeners.get(channel);
if (set != null) {
byte[] msg = events.get(2);
for (CacheEventListener item : set) {
pubSubExecutor().execute(() -> {
try {
item.onMessage(channel, msg);
} catch (Throwable t) {
logger.log(
Level.SEVERE,
"CacheSource subscribe message error, topic: " + channel,
t);
}
});
}
}
} else {
RedisCacheRequest request = ((RedisCacheCodec) conn.getCodec()).nextRequest();
ClientFuture respFuture =
((RedisCacheConnection) conn).pollRespFuture(request.getRequestid());
respFuture.complete(result);
}
} else {
RedisCacheRequest request = ((RedisCacheCodec) conn.getCodec()).nextRequest();
ClientFuture respFuture = ((RedisCacheConnection) conn).pollRespFuture(request.getRequestid());
respFuture.complete(result);
}
} else {
RedisCacheRequest request = ((RedisCacheCodec) conn.getCodec()).nextRequest();
ClientFuture respFuture = ((RedisCacheConnection) conn).pollRespFuture(request.getRequestid());
respFuture.completeExceptionally(resp.getCause());
}
}
public void onClose(ClientConnection conn) {
pubSubConn = null;
retryConnectPubSub(startTime);
}
};
}
protected void retryConnectPubSub(long startTime) {
if (!closed) {
logger.log(Level.INFO, getClass().getSimpleName() + " (name = " + name + ") retry new pubSub connection");
long delay = System.currentTimeMillis() - startTime;
if (delay > PUBSUB_RETRY_DELAY_MILLS) {
pubSubConn();
} else {
if (pubSubScheduler == null) {
schedulerLock.lock();
try {
if (pubSubScheduler == null) {
pubSubScheduler = new ScheduledThreadPoolExecutor(
1, Utility.newThreadFactory("Redkale-PubSub-Connect-Thread-%s"));
pubSubScheduler.setRemoveOnCancelPolicy(true);
}
} finally {
schedulerLock.unlock();
}
}
pubSubScheduler.schedule(this::pubSubConn, PUBSUB_RETRY_DELAY_MILLS - delay, TimeUnit.MILLISECONDS);
}
}
}
protected CompletableFuture pubSubConn() {
RedisCacheConnection conn = this.pubSubConn;
if (conn != null) {
return CompletableFuture.completedFuture(conn);
}
long startTime = System.currentTimeMillis();
return client.newConnection()
.thenApply(r -> {
pubSubLock.lock();
try {
if (pubSubConn == null) {
pubSubConn = r;
r.getCodec().withMessageListener(createMessageListener(startTime));
// 重连时重新订阅
if (!pubSubListeners.isEmpty()) {
final Map, HashSet> listeners = new HashMap<>();
pubSubListeners.forEach((t, s) -> {
s.forEach(l -> listeners
.computeIfAbsent(l, x -> new HashSet<>())
.add(t));
});
listeners.forEach((listener, topics) -> {
subscribeAsync(listener, topics.toArray(Creator.funcStringArray()));
});
}
}
return pubSubConn;
} finally {
pubSubLock.unlock();
}
})
.whenComplete((t, e) -> {
if (e != null) {
retryConnectPubSub(startTime);
}
});
}
@Override
public CompletableFuture isOpenAsync() {
return CompletableFuture.completedFuture(client != null);
}
// ------------------------ 订阅发布 SUB/PUB ------------------------
@Override
public CompletableFuture> pubsubChannelsAsync(@Nullable String pattern) {
CompletableFuture future = pattern == null
? sendAsync(RedisCommand.PUBSUB, "CHANNELS", CHANNELS)
: sendAsync(RedisCommand.PUBSUB, "CHANNELS", CHANNELS, pattern.getBytes(StandardCharsets.UTF_8));
return future.thenApply(v -> v.getListValue("CHANNELS", null, String.class));
}
@Override
public CompletableFuture subscribeAsync(CacheEventListener listener, String... topics) {
Objects.requireNonNull(listener);
if (Utility.isEmpty(topics)) {
throw new RedkaleException("topics is empty");
}
RedisCacheRequest req = RedisCacheRequest.create(RedisCommand.SUBSCRIBE, null, keysArgs(topics));
return pubSubConn().thenCompose(conn -> conn.writeRequest(req).thenApply(v -> {
for (String topic : topics) {
pubSubListeners
.computeIfAbsent(topic, y -> new CopyOnWriteArraySet<>())
.add(listener);
}
return null;
}));
}
@Override
public CompletableFuture unsubscribeAsync(CacheEventListener listener, String... topics) {
if (listener == null) { // 清掉指定topic的所有订阅者
Set delTopics = new HashSet<>();
if (Utility.isEmpty(topics)) {
delTopics.addAll(pubSubListeners.keySet());
} else {
delTopics.addAll(Arrays.asList(topics));
}
List> futures = new ArrayList<>();
delTopics.forEach(topic -> {
futures.add(pubSubConn().thenCompose(conn -> conn.writeRequest(RedisCacheRequest.create(
RedisCommand.UNSUBSCRIBE, topic, topic.getBytes(StandardCharsets.UTF_8)))
.thenApply(r -> {
pubSubListeners.remove(topic);
return null;
})));
});
return returnFutureSize(futures);
} else { // 清掉指定topic的指定订阅者
List> futures = new ArrayList<>();
for (String topic : topics) {
CopyOnWriteArraySet> listens = pubSubListeners.get(topic);
if (listens == null) {
continue;
}
listens.remove(listener);
if (listens.isEmpty()) {
futures.add(pubSubConn().thenCompose(conn -> conn.writeRequest(RedisCacheRequest.create(
RedisCommand.UNSUBSCRIBE, topic, topic.getBytes(StandardCharsets.UTF_8)))
.thenApply(r -> {
pubSubListeners.remove(topic);
return null;
})));
}
}
return returnFutureSize(futures);
}
}
@Override
public CompletableFuture publishAsync(String topic, byte[] message) {
Objects.requireNonNull(topic);
Objects.requireNonNull(message);
return sendAsync(RedisCommand.PUBLISH, topic, topic.getBytes(StandardCharsets.UTF_8), message)
.thenApply(v -> v.getIntValue(0));
}
// --------------------- exists ------------------------------
@Override
public CompletableFuture existsAsync(String key) {
return sendAsync(RedisCommand.EXISTS, key, keyArgs(key)).thenApply(v -> v.getIntValue(0) > 0);
}
// --------------------- get ------------------------------
@Override
public CompletableFuture getAsync(String key, Type type) {
return sendAsync(RedisCommand.GET, key, keyArgs(key)).thenApply(v -> v.getObjectValue(key, cryptor, type));
}
// --------------------- getex ------------------------------
@Override
public CompletableFuture getexAsync(String key, int expireSeconds, final Type type) {
return sendAsync(RedisCommand.GETEX, key, keyArgs(key, "EX", expireSeconds))
.thenApply(v -> v.getObjectValue(key, cryptor, type));
}
@Override
public CompletableFuture msetAsync(final Serializable... keyVals) {
if (keyVals.length % 2 != 0) {
throw new RedkaleException("key value must be paired");
}
return sendAsync(RedisCommand.MSET, keyVals[0].toString(), keymArgs(keyVals))
.thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture msetAsync(final Map map) {
if (isEmpty(map)) {
return CompletableFuture.completedFuture(null);
}
return sendAsync(
RedisCommand.MSET,
map.keySet().stream().findFirst().orElse("").toString(),
keymArgs(map))
.thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture msetnxAsync(final Serializable... keyVals) {
if (keyVals.length % 2 != 0) {
throw new RedkaleException("key value must be paired");
}
return sendAsync(RedisCommand.MSETNX, keyVals[0].toString(), keymArgs(keyVals))
.thenApply(v -> v.getBoolValue());
}
@Override
public CompletableFuture msetnxAsync(final Map map) {
if (isEmpty(map)) {
return CompletableFuture.completedFuture(null);
}
return sendAsync(
RedisCommand.MSETNX,
map.keySet().stream().findFirst().orElse("").toString(),
keymArgs(map))
.thenApply(v -> v.getBoolValue());
}
// --------------------- setex ------------------------------
@Override
public CompletableFuture setAsync(String key, Convert convert, final Type type, T value) {
return sendAsync(RedisCommand.SET, key, keyArgs(key, convert, type, value))
.thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture setnxAsync(String key, Convert convert, final Type type, T value) {
return sendAsync(RedisCommand.SET, key, keyArgs(key, NX, convert, type, value))
.thenApply(v -> v.getBoolValue());
}
@Override
public CompletableFuture getSetAsync(String key, Convert convert, final Type type, T value) {
return sendAsync(RedisCommand.GETSET, key, keyArgs(key, convert, type, value))
.thenApply(v -> v.getObjectValue(key, cryptor, type));
}
@Override
public CompletableFuture getDelAsync(String key, final Type type) {
return sendAsync(RedisCommand.GETDEL, key, keyArgs(key)).thenApply(v -> v.getObjectValue(key, cryptor, type));
}
// --------------------- setex ------------------------------
@Override
public CompletableFuture setexAsync(
String key, int expireSeconds, Convert convert, final Type type, T value) {
return sendAsync(RedisCommand.SET, key, keyArgs(key, expireSeconds, EX, convert, type, value))
.thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture psetexAsync(
String key, long milliSeconds, Convert convert, final Type type, T value) {
return sendAsync(RedisCommand.SET, key, keyArgs(key, milliSeconds, PX, convert, type, value))
.thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture setnxexAsync(
String key, int expireSeconds, Convert convert, final Type type, T value) {
return sendAsync(RedisCommand.SET, key, keyArgs(key, expireSeconds, NX, EX, convert, type, value))
.thenApply(v -> v.getBoolValue());
}
@Override
public CompletableFuture setnxpxAsync(
String key, long milliSeconds, Convert convert, final Type type, T value) {
return sendAsync(RedisCommand.SET, key, keyArgs(key, milliSeconds, NX, PX, convert, type, value))
.thenApply(v -> v.getBoolValue());
}
// --------------------- expire ------------------------------
@Override
public CompletableFuture expireAsync(String key, int expireSeconds) {
return sendAsync(RedisCommand.EXPIRE, key, keyArgs(key, expireSeconds)).thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture pexpireAsync(String key, long milliSeconds) {
return sendAsync(RedisCommand.PEXPIRE, key, keyArgs(key, milliSeconds)).thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture expireAtAsync(String key, long secondsTime) {
return sendAsync(RedisCommand.EXPIREAT, key, keyArgs(key, secondsTime)).thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture pexpireAtAsync(String key, long milliTime) {
return sendAsync(RedisCommand.PEXPIREAT, key, keyArgs(key, milliTime)).thenApply(v -> v.getVoidValue());
}
// --------------------- ttl ------------------------------
@Override
public CompletableFuture ttlAsync(String key) {
return sendAsync(RedisCommand.TTL, key, keyArgs(key)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture pttlAsync(String key) {
return sendAsync(RedisCommand.PTTL, key, keyArgs(key)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture expireTimeAsync(String key) {
return sendAsync(RedisCommand.EXPIRETIME, key, keyArgs(key)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture pexpireTimeAsync(String key) {
return sendAsync(RedisCommand.PEXPIRETIME, key, keyArgs(key)).thenApply(v -> v.getLongValue(0L));
}
// --------------------- persist ------------------------------
@Override
public CompletableFuture persistAsync(String key) {
return sendAsync(RedisCommand.PERSIST, key, keyArgs(key)).thenApply(v -> v.getBoolValue());
}
// --------------------- rename ------------------------------
@Override
public CompletableFuture renameAsync(String oldKey, String newKey) {
return sendAsync(RedisCommand.RENAME, oldKey, keysArgs(oldKey, newKey)).thenApply(v -> v.getBoolValue());
}
@Override
public CompletableFuture renamenxAsync(String oldKey, String newKey) {
return sendAsync(RedisCommand.RENAMENX, oldKey, keysArgs(oldKey, newKey))
.thenApply(v -> v.getBoolValue());
}
@Override
public CompletableFuture evalAsync(Type type, String script, List keys, String... args) {
String key = keys == null || keys.isEmpty() ? null : keys.get(0);
return sendAsync(RedisCommand.EVAL, key, keysArgs(script, keys, args)).thenApply(v -> {
if (type == long.class) {
return (T) v.getLongValue(0L);
} else if (type == Long.class) {
return (T) v.getLongValue(null);
} else if (type == int.class) {
return (T) v.getIntValue(0);
} else if (type == Integer.class) {
return (T) v.getIntValue(null);
} else if (type == double.class) {
return (T) v.getDoubleValue(0.0);
} else if (type == Double.class) {
return (T) v.getDoubleValue(null);
} else if (type == float.class) {
return (T) (Number) v.getDoubleValue(0.0).floatValue();
} else if (type == Float.class) {
Double d = v.getDoubleValue(0.0);
return d == null ? null : (T) (Number) d.floatValue();
} else if (type == String.class) {
return v.getObjectValue(key, cryptor, type);
} else {
Class t = TypeToken.typeToClass(type);
if (List.class.isAssignableFrom(t)) {
Type componentType = type instanceof ParameterizedType
? ((ParameterizedType) type).getActualTypeArguments()[0]
: String.class;
return (T) v.getListValue(key, cryptor, componentType);
} else if (Set.class.isAssignableFrom(t)) {
Type componentType = type instanceof ParameterizedType
? ((ParameterizedType) type).getActualTypeArguments()[0]
: String.class;
return (T) v.getSetValue(key, cryptor, componentType);
} else if (Map.class.isAssignableFrom(t)) {
Type componentType = type instanceof ParameterizedType
? ((ParameterizedType) type).getActualTypeArguments()[1]
: String.class;
return (T) v.getMapValue(key, cryptor, componentType);
}
return v.getObjectValue(key, cryptor, type);
}
});
}
// --------------------- del ------------------------------
@Override
public CompletableFuture delAsync(String... keys) {
if (keys.length == 0) {
return CompletableFuture.completedFuture(0L);
}
return sendAsync(RedisCommand.DEL, keys[0], keysArgs(keys)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture delexAsync(String key, String expectedValue) {
if (key == null) {
return CompletableFuture.completedFuture(0L);
}
return sendAsync(RedisCommand.EVAL, key, keyArgs(SCRIPT_DELEX, 1, key, expectedValue))
.thenApply(v -> v.getLongValue(0L));
}
// --------------------- incrby ------------------------------
@Override
public CompletableFuture incrAsync(final String key) {
return sendAsync(RedisCommand.INCR, key, keyArgs(key)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture incrbyAsync(final String key, long num) {
return sendAsync(RedisCommand.INCRBY, key, keyArgs(key, num)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture incrbyFloatAsync(final String key, double num) {
return sendAsync(RedisCommand.INCRBYFLOAT, key, keyArgs(key, num)).thenApply(v -> v.getDoubleValue(0.d));
}
// --------------------- decrby ------------------------------
@Override
public CompletableFuture decrAsync(final String key) {
return sendAsync(RedisCommand.DECR, key, keyArgs(key)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture decrbyAsync(final String key, long num) {
return sendAsync(RedisCommand.DECRBY, key, keyArgs(key, num)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture hdelAsync(final String key, String... fields) {
return sendAsync(RedisCommand.HDEL, key, keysArgs(key, fields)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture hlenAsync(final String key) {
return sendAsync(RedisCommand.HLEN, key, keyArgs(key)).thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture> hkeysAsync(final String key) {
return sendAsync(RedisCommand.HKEYS, key, keyArgs(key))
.thenApply(v -> (List) v.getListValue(key, cryptor, String.class));
}
@Override
public CompletableFuture hincrAsync(final String key, String field) {
return hincrbyAsync(key, field, 1);
}
@Override
public CompletableFuture hincrbyAsync(final String key, String field, long num) {
return sendAsync(RedisCommand.HINCRBY, key, keysArgs(key, field, String.valueOf(num)))
.thenApply(v -> v.getLongValue(0L));
}
@Override
public CompletableFuture hincrbyFloatAsync(final String key, String field, double num) {
return sendAsync(RedisCommand.HINCRBYFLOAT, key, keysArgs(key, field, String.valueOf(num)))
.thenApply(v -> v.getDoubleValue(0.d));
}
@Override
public CompletableFuture hdecrAsync(final String key, String field) {
return hincrbyAsync(key, field, -1);
}
@Override
public CompletableFuture hdecrbyAsync(final String key, String field, long num) {
return hincrbyAsync(key, field, -num);
}
@Override
public CompletableFuture hexistsAsync(final String key, String field) {
return sendAsync(RedisCommand.HEXISTS, key, keysArgs(key, field)).thenApply(v -> v.getIntValue(0) > 0);
}
@Override
public CompletableFuture hsetAsync(
final String key, final String field, final Convert convert, final Type type, final T value) {
if (value == null) {
return CompletableFuture.completedFuture(null);
}
return sendAsync(RedisCommand.HSET, key, keyArgs(key, field, convert, type, value))
.thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture hsetnxAsync(
final String key, final String field, final Convert convert, final Type type, final T value) {
if (value == null) {
return CompletableFuture.completedFuture(null);
}
return sendAsync(RedisCommand.HSETNX, key, keyArgs(key, field, convert, type, value))
.thenApply(v -> v.getBoolValue());
}
@Override
public CompletableFuture hmsetAsync(final String key, final Serializable... values) {
return sendAsync(RedisCommand.HMSET, key, keyMapArgs(key, values)).thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture hmsetAsync(final String key, final Map map) {
if (isEmpty(map)) {
return CompletableFuture.completedFuture(null);
}
return sendAsync(RedisCommand.HMSET, key, keyMapArgs(key, map)).thenApply(v -> v.getVoidValue());
}
@Override
public CompletableFuture> hmgetAsync(final String key, final Type type, final String... fields) {
return sendAsync(RedisCommand.HMGET, key, keysArgs(key, fields))
.thenApply(v -> (List) v.getListValue(key, cryptor, type));
}
@Override
public CompletableFuture
© 2015 - 2024 Weber Informatics LLC | Privacy Policy