diff --git a/pom.xml b/pom.xml
index cdc1e51..47346e5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
net.tccn
zhub-client-redkale
- 0.1.1.dev
+ x.22.0
17
@@ -18,7 +18,7 @@
org.redkale
redkale
- 2.8.0.dev
+ 2.2.0
compile
diff --git a/src/main/java/net/tccn/AbstractConsumer.java b/src/main/java/net/tccn/AbstractConsumer.java
index f126411..0844712 100644
--- a/src/main/java/net/tccn/AbstractConsumer.java
+++ b/src/main/java/net/tccn/AbstractConsumer.java
@@ -1,9 +1,9 @@
package net.tccn;
import org.redkale.convert.json.JsonConvert;
-import org.redkale.util.Resourcable;
import org.redkale.util.TypeToken;
+import javax.annotation.Resource;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -13,11 +13,12 @@ import java.util.function.Consumer;
* @author Liang
* @data 2020-09-05 23:18
*/
-public abstract class AbstractConsumer extends ZhubAgentProvider implements IConsumer, Resourcable {
+public abstract class AbstractConsumer implements IConsumer {
protected JsonConvert convert = JsonConvert.root();
- protected static String APP_NAME = "";
+ @Resource(name = "APP_NAME")
+ protected String APP_NAME = "";
private Map eventMap = new ConcurrentHashMap<>();
@@ -72,9 +73,4 @@ public abstract class AbstractConsumer extends ZhubAgentProvider implements ICon
}
// --------------
-
- @Override
- public String resourceName() {
- return super.getName();
- }
}
diff --git a/src/main/java/net/tccn/ZhubAgentProvider.java b/src/main/java/net/tccn/ZhubAgentProvider.java
deleted file mode 100644
index 5eff32b..0000000
--- a/src/main/java/net/tccn/ZhubAgentProvider.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package net.tccn;
-
-import org.redkale.boot.Application;
-import org.redkale.boot.NodeServer;
-import org.redkale.cluster.CacheClusterAgent;
-import org.redkale.cluster.ClusterAgent;
-import org.redkale.service.Service;
-import org.redkale.util.ResourceEvent;
-
-import java.net.InetSocketAddress;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.CompletableFuture;
-
-public abstract class ZhubAgentProvider extends ClusterAgent {
-
- @Override
- public void onResourceChange(ResourceEvent[] events) {
-
- }
-
- @Override
- public void register(Application application) {
-
- }
-
- @Override
- public void deregister(Application application) {
-
- }
-
- @Override
- public CompletableFuture> queryHttpAddress(String protocol, String module, String resname) {
- return null;
- }
-
- @Override
- public CompletableFuture> querySncpAddress(String protocol, String restype, String resname) {
- return null;
- }
-
- @Override
- protected CompletableFuture> queryAddress(ClusterEntry entry) {
- return null;
- }
-
- @Override
- protected ClusterEntry register(NodeServer ns, String protocol, Service service) {
- deregister(ns, protocol, service);
- ClusterEntry clusterEntry = new ClusterEntry(ns, protocol, service);
- CacheClusterAgent.AddressEntry entry = new CacheClusterAgent.AddressEntry();
- entry.addr = clusterEntry.address;
- entry.resname = clusterEntry.resourceName;
- entry.nodeid = this.nodeid;
- entry.time = System.currentTimeMillis();
- //source.hset(clusterEntry.serviceName, clusterEntry.serviceid, CacheClusterAgent.AddressEntry.class, entry);
- return clusterEntry;
- }
-
- @Override
- protected void deregister(NodeServer ns, String protocol, Service service) {
-
- }
-}
diff --git a/src/main/java/net/tccn/ZhubListener.java b/src/main/java/net/tccn/ZhubListener.java
new file mode 100644
index 0000000..dabda0e
--- /dev/null
+++ b/src/main/java/net/tccn/ZhubListener.java
@@ -0,0 +1,50 @@
+package net.tccn;
+
+import net.tccn.zhub.ZHubClient;
+import org.redkale.boot.Application;
+import org.redkale.boot.ApplicationListener;
+import org.redkale.service.Service;
+import org.redkale.util.AnyValue;
+import org.redkale.util.RedkaleClassLoader;
+import org.redkale.util.ResourceFactory;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * 服务监听
+ *
+ * @author: liangxy.
+ */
+public class ZhubListener implements ApplicationListener {
+
+ @Override
+ public void preStart(Application application) {
+
+ CompletableFuture.runAsync(() -> {
+ ResourceFactory resourceFactory = application.getResourceFactory();
+ RedkaleClassLoader classLoader = application.getClassLoader();
+
+ AnyValue appConfig = application.getAppConfig();
+ AnyValue zhubs = appConfig.getAnyValue("zhubs");
+ AnyValue[] values = zhubs.getAnyValues("zhub");
+ for (AnyValue zhub : values) {
+ String className = zhub.getValue("value", ZHubClient.class.getCanonicalName());
+ try {
+ Class> clazz = classLoader.loadClass(className);
+ Service obj = (Service) clazz.getDeclaredConstructor().newInstance();
+ application.getResourceFactory().inject(obj);
+ obj.init(zhub);
+ resourceFactory.register(zhub.get("name"), clazz, obj);
+ } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException | ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ }
+
+ @Override
+ public void preShutdown(Application application) {
+
+ }
+}
diff --git a/src/main/java/net/tccn/ZhubProvider.java b/src/main/java/net/tccn/ZhubProvider.java
deleted file mode 100644
index 737f557..0000000
--- a/src/main/java/net/tccn/ZhubProvider.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package net.tccn;
-
-import net.tccn.zhub.ZHubClient;
-import org.redkale.annotation.Priority;
-import org.redkale.cluster.ClusterAgent;
-import org.redkale.cluster.ClusterAgentProvider;
-import org.redkale.util.AnyValue;
-
-@Priority(1)
-public class ZhubProvider implements ClusterAgentProvider {
-
- @Override
- public boolean acceptsConf(AnyValue config) {
- return new ZHubClient().acceptsConf(config);
- }
-
- @Override
- public ClusterAgent createInstance() {
- return new ZHubClient();
- }
-}
diff --git a/src/main/java/net/tccn/zhub/ZHubClient.java b/src/main/java/net/tccn/zhub/ZHubClient.java
index bd8c86f..fea63eb 100644
--- a/src/main/java/net/tccn/zhub/ZHubClient.java
+++ b/src/main/java/net/tccn/zhub/ZHubClient.java
@@ -2,14 +2,9 @@ package net.tccn.zhub;
import net.tccn.*;
import net.tccn.timer.Timers;
-import org.redkale.annotation.AutoLoad;
-import org.redkale.annotation.ResourceType;
import org.redkale.service.Local;
import org.redkale.service.Service;
-import org.redkale.util.AnyValue;
-import org.redkale.util.Comment;
-import org.redkale.util.TypeToken;
-import org.redkale.util.Utility;
+import org.redkale.util.*;
import java.io.BufferedReader;
import java.io.IOException;
@@ -56,38 +51,8 @@ public class ZHubClient extends AbstractConsumer implements IConsumer, IProducer
private static Map mainHub = new HashMap<>(); // 127.0.0.1:1216 - ZHubClient
- public ZHubClient() {
-
- }
-
- public ZHubClient(String name, Map attr) {
- this.APP_NAME = name;
- this.addr = attr.get("addr");
- this.groupid = attr.get("groupid");
- this.auth = attr.get("auth");
-
- this.initClient(null);
- }
-
@Override
public void init(AnyValue config) {
- APP_NAME = application.getName();
- /*if (!preInit()) {
- return;
- }*/
-
- if (config == null) {
- initClient(null);
- } else {
- Map nodes = getNodes(config);
- for (String rsName : nodes.keySet()) {
- ZHubClient client = new ZHubClient().initClient(nodes.get(rsName));
- application.getResourceFactory().register(rsName, client);
- }
- }
- }
-
- private ZHubClient initClient(AnyValue config) {
// 自动注入
if (config != null) {
addr = config.getValue("addr", addr);
@@ -103,8 +68,10 @@ public class ZHubClient extends AbstractConsumer implements IConsumer, IProducer
}
// 设置第一个启动的 实例为主实例
- if (!mainHub.containsKey(addr)) { // 确保同步执行此 init 逻辑
- mainHub.put(addr, this);
+ synchronized (ZHubClient.class) {
+ if (!mainHub.containsKey(addr)) { // 确保同步执行此 init 逻辑
+ mainHub.put(addr, this);
+ }
}
CompletableFuture.runAsync(() -> {
@@ -272,18 +239,17 @@ public class ZHubClient extends AbstractConsumer implements IConsumer, IProducer
while (true) {
Event event = null;
try {
- event = rpcCallQueue.take();
- logger.info(String.format("rpc-call:[%s] %s", event.topic, event.value));
+ event = rpcBackQueue.take();
+ logger.info(String.format("rpc-back:[%s]", event.value));
- String topic = event.topic;
String value = event.value;
- executor.submit(() -> accept(topic, value)).get(5, TimeUnit.SECONDS);
+ executor.submit(() -> rpcAccept(value)).get(5, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException | TimeoutException e) {
if (e instanceof TimeoutException) {
executor = Executors.newSingleThreadExecutor();
- logger.log(Level.WARNING, "rpc-call TimeoutException, topic[" + event.topic + "], value[" + event.value + "]", e);
+ logger.log(Level.WARNING, "rpc-back TimeoutException, topic[" + event.topic + "], value[" + event.value + "]", e);
} else if (event != null) {
- logger.log(Level.WARNING, "rpc-call[" + event.topic + "] event accept error :" + event.value, e);
+ logger.log(Level.WARNING, "rpc-back[" + event.value + "] event accept error :" + event.value, e);
}
}
}
@@ -335,8 +301,6 @@ public class ZHubClient extends AbstractConsumer implements IConsumer, IProducer
}
}).start();
});
-
- return this;
}
public boolean acceptsConf(AnyValue config) {
@@ -621,8 +585,8 @@ public class ZHubClient extends AbstractConsumer implements IConsumer, IProducer
private static Map rpcRetType = new ConcurrentHashMap<>();
@Comment("rpc call")
- public RpcResult rpc(String topic, Object v) {
- return rpc(topic, v, null);
+ public RpcResult rpc(String topic, Object v) {
+ return rpc(topic, v, IType.STRING);
}
@Comment("rpc call")
@@ -671,8 +635,8 @@ public class ZHubClient extends AbstractConsumer implements IConsumer, IProducer
return rpc.getRpcResult();
}
- public CompletableFuture> rpcAsync(String topic, T v) {
- return CompletableFuture.supplyAsync(() -> rpc(topic, v, null));
+ public CompletableFuture> rpcAsync(String topic, T v) {
+ return CompletableFuture.supplyAsync(() -> rpc(topic, v, IType.STRING));
}
public CompletableFuture> rpcAsync(String topic, T v, TypeToken typeToken) {
diff --git a/src/main/java/org/redkalex/cache/redis/AbstractRedisSource.java b/src/main/java/org/redkalex/cache/redis/AbstractRedisSource.java
deleted file mode 100644
index 87c1f1b..0000000
--- a/src/main/java/org/redkalex/cache/redis/AbstractRedisSource.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Click nbfs://nbhost/SystemFileSystem/Templates/Licenses/license-default.txt to change this license
- * Click nbfs://nbhost/SystemFileSystem/Templates/Classes/Class.java to edit this template
- */
-package org.redkalex.cache.redis;
-
-import org.redkale.annotation.Resource;
-import org.redkale.convert.Convert;
-import org.redkale.convert.json.JsonConvert;
-import org.redkale.source.AbstractCacheSource;
-import org.redkale.util.*;
-
-import java.lang.reflect.Type;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.List;
-import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.function.Function;
-
-import static org.redkale.boot.Application.RESNAME_APP_EXECUTOR;
-import static org.redkale.boot.Application.RESNAME_APP_NAME;
-
-/**
- *
- * @author zhangjx
- *
- * @since 2.8.0
- */
-public abstract class AbstractRedisSource extends AbstractCacheSource {
-
- public static final String CACHE_SOURCE_CRYPTOR = "cryptor";
-
- protected String name;
-
- @Resource(name = RESNAME_APP_NAME, required = false)
- protected String appName = "";
-
- @Resource(required = false)
- protected ResourceFactory resourceFactory;
-
- @Resource(required = false)
- protected JsonConvert defaultConvert;
-
- @Resource(name = Resource.PARENT_NAME + "_convert", required = false)
- protected JsonConvert convert;
-
- protected int db;
-
- protected RedisCryptor cryptor;
-
- protected AnyValue conf;
-
- private ExecutorService subExecutor;
-
- private final ReentrantLock subExecutorLock = new ReentrantLock();
-
- @Resource(name = RESNAME_APP_EXECUTOR, required = false)
- protected ExecutorService workExecutor;
-
- @Override
- public void init(AnyValue conf) {
- this.conf = conf;
- super.init(conf);
- this.name = conf.getValue("name", "");
- if (this.convert == null) {
- this.convert = this.defaultConvert;
- }
- if (conf != null) {
- String cryptStr = conf.getValue(CACHE_SOURCE_CRYPTOR, "").trim();
- if (!cryptStr.isEmpty()) {
- try {
- Class cryptClass = (Class) getClass().getClassLoader().loadClass(cryptStr);
- RedkaleClassLoader.putReflectionPublicConstructors(cryptClass, cryptClass.getName());
- this.cryptor = cryptClass.getConstructor().newInstance();
- } catch (ReflectiveOperationException e) {
- throw new RedkaleException(e);
- }
- }
- }
- if (cryptor != null) {
- if (resourceFactory != null) {
- resourceFactory.inject(cryptor);
- }
- cryptor.init(conf);
- }
- }
-
- @Override
- public void destroy(AnyValue conf) {
- super.destroy(conf);
- if (cryptor != null) {
- cryptor.destroy(conf);
- }
- }
-
- public boolean acceptsConf(AnyValue config) {
- if (config == null) {
- return false;
- }
- return "redis".equalsIgnoreCase(config.getValue(CACHE_SOURCE_TYPE))
- || getClass().getName().equalsIgnoreCase(config.getValue(CACHE_SOURCE_TYPE))
- || config.getValue(CACHE_SOURCE_NODES, config.getValue("url", "")).startsWith("redis://")
- || config.getValue(CACHE_SOURCE_NODES, config.getValue("url", "")).startsWith("rediss://");
- }
-
- protected ExecutorService subExecutor() {
- ExecutorService executor = subExecutor;
- if (executor != null) {
- return executor;
- }
- subExecutorLock.lock();
- try {
- if (subExecutor == null) {
- String threadNameFormat = "CacheSource-" + resourceName() + "-SubThread-%s";
- Function func = Utility.virtualExecutorFunction();
- final AtomicInteger counter = new AtomicInteger();
- subExecutor = func == null ? Executors.newFixedThreadPool(Utility.cpus(), r -> {
- Thread t = new Thread(r);
- t.setDaemon(true);
- int c = counter.incrementAndGet();
- t.setName(String.format(threadNameFormat, "Virtual-" + (c < 10 ? ("00" + c) : (c < 100 ? ("0" + c) : c))));
- return t;
- }) : func.apply(threadNameFormat);
- }
- executor = subExecutor;
- } finally {
- subExecutorLock.unlock();
- }
- return executor;
- }
-
- protected String getNodes(AnyValue config) {
- return config.getValue(CACHE_SOURCE_NODES, config.getValue("url", ""));
- }
-
- @Override
- public void close() throws Exception { //在 Application 关闭时调用
- destroy(null);
- }
-
- @Override
- public String resourceName() {
- return name;
- }
-
- protected String decryptValue(String key, RedisCryptor cryptor, String value) {
- return cryptor != null ? cryptor.decrypt(key, value) : value;
- }
-
- protected T decryptValue(String key, RedisCryptor cryptor, Type type, byte[] bs) {
- return decryptValue(key, cryptor, convert, type, bs);
- }
-
- protected T decryptValue(String key, RedisCryptor cryptor, Convert c, Type type, byte[] bs) {
- if (bs == null) {
- return null;
- }
- if (type == byte[].class) {
- return (T) bs;
- }
- if (cryptor == null && type == String.class) {
- return (T) new String(bs, StandardCharsets.UTF_8);
- }
- if (cryptor == null || (type instanceof Class && (((Class) type).isPrimitive() || Number.class.isAssignableFrom((Class) type)))) {
- return (T) (c == null ? this.convert : c).convertFrom(type, bs);
- }
- String deval = cryptor.decrypt(key, new String(bs, StandardCharsets.UTF_8));
- if (type == String.class) {
- return (T) deval;
- }
- return deval == null ? null : (T) (c == null ? this.convert : c).convertFrom(type, deval.getBytes(StandardCharsets.UTF_8));
- }
-
- protected String encryptValue(String key, RedisCryptor cryptor, String value) {
- return cryptor != null ? cryptor.encrypt(key, value) : value;
- }
-
- protected byte[] encryptValue(String key, RedisCryptor cryptor, Convert c, T value) {
- return encryptValue(key, cryptor, null, c, value);
- }
-
- protected byte[] encryptValue(String key, RedisCryptor cryptor, Type type, Convert c, T value) {
- if (value == null) {
- return null;
- }
- Type t = type == null ? value.getClass() : type;
- if (cryptor == null && t == String.class) {
- return value.toString().getBytes(StandardCharsets.UTF_8);
- }
- byte[] bs = (c == null ? this.convert : c).convertToBytes(t, value);
- if (bs.length > 1 && t instanceof Class && !CharSequence.class.isAssignableFrom((Class) t)) {
- if (bs[0] == '"' && bs[bs.length - 1] == '"') {
- bs = Arrays.copyOfRange(bs, 1, bs.length - 1);
- }
- }
- return encryptValue(key, cryptor, t, bs);
- }
-
- protected byte[] encryptValue(String key, RedisCryptor cryptor, Type type, byte[] bs) {
- if (bs == null) {
- return null;
- }
- if (cryptor == null || (type instanceof Class && (((Class) type).isPrimitive() || Number.class.isAssignableFrom((Class) type)))) {
- return bs;
- }
- String enval = cryptor.encrypt(key, new String(bs, StandardCharsets.UTF_8));
- return enval == null ? null : enval.getBytes(StandardCharsets.UTF_8);
- }
-
- protected T decryptScore(Class scoreType, Double score) {
- if (score == null) {
- return null;
- }
- if (scoreType == int.class || scoreType == Integer.class) {
- return (T) (Number) score.intValue();
- } else if (scoreType == long.class || scoreType == Long.class) {
- return (T) (Number) score.longValue();
- } else if (scoreType == float.class || scoreType == Float.class) {
- return (T) (Number) score.floatValue();
- } else if (scoreType == double.class || scoreType == Double.class) {
- return (T) (Number) score;
- } else {
- return JsonConvert.root().convertFrom(scoreType, score.toString());
- }
- }
-
- protected CompletableFuture returnFutureSize(List> futures) {
- return futures == null || futures.isEmpty() ? CompletableFuture.completedFuture(0) : Utility.allOfFutures(futures).thenApply(v -> futures.size());
- }
-}
diff --git a/src/main/java/org/redkalex/cache/redis/MyRedisCacheSource.java b/src/main/java/org/redkalex/cache/redis/MyRedisCacheSource.java
index a81859e..a8cf91c 100644
--- a/src/main/java/org/redkalex/cache/redis/MyRedisCacheSource.java
+++ b/src/main/java/org/redkalex/cache/redis/MyRedisCacheSource.java
@@ -1,27 +1,299 @@
package org.redkalex.cache.redis;
-import org.redkale.annotation.AutoLoad;
-import org.redkale.annotation.ResourceType;
+
+import org.redkale.convert.Convert;
import org.redkale.service.Local;
import org.redkale.source.CacheSource;
-import org.redkale.util.AnyValue;
+import org.redkale.util.AutoLoad;
+import org.redkale.util.ResourceType;
import java.io.Serializable;
+import java.lang.reflect.Type;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CompletableFuture;
+import java.util.stream.Stream;
@Local
@AutoLoad(false)
@ResourceType(CacheSource.class)
-public class MyRedisCacheSource extends RedisCacheSource {
-
- @Override
- public void init(AnyValue conf) {
- super.init(conf);
+public class MyRedisCacheSource extends RedisCacheSource {
+ //--------------------- oth ------------------------------
+ public boolean setnx(String key, Object v) {
+ byte[][] bytes = Stream.of(key, v).map(x -> String.valueOf(x).getBytes(StandardCharsets.UTF_8)).toArray(byte[][]::new);
+ Serializable rs = send("SETNX", CacheEntryType.OBJECT, (Type) null, key, bytes).join();
+ return rs == null ? false : (long) rs == 1;
+ }
+ //--------------------- oth ------------------------------
+
+ //--------------------- bit ------------------------------
+ public boolean getBit(String key, int offset) {
+ byte[][] bytes = Stream.of(key, offset).map(x -> String.valueOf(x).getBytes(StandardCharsets.UTF_8)).toArray(byte[][]::new);
+ Serializable v = send("GETBIT", CacheEntryType.OBJECT, (Type) null, key, bytes).join();
+ return v == null ? false : (long) v == 1;
+ }
+
+ public void setBit(String key, int offset, boolean bool) {
+ byte[][] bytes = Stream.of(key, offset, bool ? 1 : 0).map(x -> String.valueOf(x).getBytes(StandardCharsets.UTF_8)).toArray(byte[][]::new);
+ send("SETBIT", CacheEntryType.OBJECT, (Type) null, key, bytes).join();
+ }
+
+ //--------------------- bit ------------------------------
+ //--------------------- lock ------------------------------
+ // 尝试加锁,成功返回0,否则返回上一锁剩余毫秒值
+ public int tryLock(String key, int millis) {
+ byte[][] bytes = Stream.of("" +
+ "if (redis.call('exists',KEYS[1]) == 0) then " +
+ "redis.call('psetex', KEYS[1], ARGV[1], 1) " +
+ "return 0; " +
+ "else " +
+ "return redis.call('PTTL', KEYS[1]); " +
+ "end; ", 1, key, millis).map(x -> String.valueOf(x).getBytes(StandardCharsets.UTF_8)).toArray(byte[][]::new);
+ int n = (int) send("EVAL", CacheEntryType.OBJECT, (Type) null, null, bytes).join();
+ return n;
+ }
+
+ // 加锁
+ public void lock(String key, int millis) {
+ int i;
+ do {
+ i = tryLock(key, millis);
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ } while (i > 0);
+ }
+
+ // 解锁
+ public void unlock(String key) {
+ remove(key);
+ }
+
+
+ //--------------------- key ------------------------------
+
+ public long getTtl(String key) {
+ return (long) send("TTL", CacheEntryType.OBJECT, (Type) null, key, key.getBytes(StandardCharsets.UTF_8)).join();
+ }
+
+ public long getPttl(String key) {
+ return (long) send("PTTL", CacheEntryType.OBJECT, (Type) null, key, key.getBytes(StandardCharsets.UTF_8)).join();
+ }
+
+ public int remove(String... keys) {
+ if (keys == null || keys.length == 0) {
+ return 0;
+ }
+ List para = new ArrayList<>();
+ para.add("" +
+ " local args = ARGV;" +
+ " local x = 0;" +
+ " for i,v in ipairs(args) do" +
+ " local inx = redis.call('del', v);" +
+ " if(inx > 0) then" +
+ " x = x + 1;" +
+ " end" +
+ " end" +
+ " return x;");
+
+ para.add("0");
+ for (Object field : keys) {
+ para.add(String.valueOf(field));
+ }
+ byte[][] bytes = para.stream().map(x -> String.valueOf(x).getBytes(StandardCharsets.UTF_8)).toArray(byte[][]::new);
+ return (int) send("EVAL", CacheEntryType.OBJECT, (Type) null, null, bytes).join();
+ }
+
+ //--------------------- hmget ------------------------------
+ public V getHm(String key, T field) {
+ // return (V) send("HMGET", CacheEntryType.OBJECT, (Type) null, key, key.getBytes(StandardCharsets.UTF_8), field.getBytes(StandardCharsets.UTF_8)).join();
+ Map