Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,24 @@ public enum RedisOpType {
LREM(false, 4),
LSET(false, 4),
LTRIM(false, 4),
BLMPOP(true,-5),
LMPOP(true,-4),

// Hash single
HDEL(false, -3),
HINCRBY(false, 4),
HINCRBYFLOAT(false, 4),
HMSET(false, -4),
HSET(false, -4),
HSETEX(false, -4),
HSETNX(false, 4),
HSETEX(false, true,-6),
HEXPIREAT(false,true,-6),
HPEXPIREAT(false,true,-6),
HEXPIRE(false,true,-6),
HPEXPIRE(false,true,-6),
HGETDEL(false,true,-5),
HPERSIST(false,true,-5),
HGETEX(false,true,-5),

// Set single
SADD(false, -3),
Expand All @@ -62,13 +71,18 @@ public enum RedisOpType {
ZREMRANGEBYLEX(false, 4),
ZREMRANGEBYRANK(false, 4),
ZREMRANGEBYSCORE(false, 4),
ZMPOP(true,-4),
BZMPOP(true,-5),

// Stream single
XADD(false, -5),
XDEL(false, -3),
XSETID(false, 3),
XGROUP(false, -2),
XCLAIM(false, -6),
XDELEX(false,-6),
XACKDEL(false,-7),


// TTL single
EXPIRE(false, 3),
Expand All @@ -83,6 +97,8 @@ public enum RedisOpType {

// Bit single
SETBIT(false, 4),
BITCOUNT(false, 4),


// String multi
DEL(true, -2),
Expand Down Expand Up @@ -154,6 +170,8 @@ public enum RedisOpType {

private RedisOpCrdtTransfer transfer;

private boolean fields;

private static final Map<String, RedisOpType> NAME_CACHE = new HashMap<>();
static {
for (RedisOpType op : values()) {
Expand All @@ -175,6 +193,12 @@ public enum RedisOpType {
this(multiKey, arity, swallow, null);
}

RedisOpType(boolean multiKey, boolean fields, int artiy) {
this.supportMultiKey = multiKey;
this.fields = fields;
this.arity = artiy;
}

RedisOpType(boolean multiKey, int arity, RedisOpCrdtTransfer transfer) {
this(multiKey, arity, false, transfer);
}
Expand All @@ -198,6 +222,8 @@ public boolean isSwallow() {
return swallow;
}

public boolean isFields() {return fields;}

public Pair<RedisOpType, byte[][]> transfer(RedisOpType redisOpType, byte[][] args) {
if (null == transfer) {
return Pair.of(redisOpType, args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ private RedisOpItemParser() {
public static RedisOpItem parse(RedisOpParser redisOpParser, Object[] payload) {
RedisOpItem redisOpItem = new RedisOpItem();
try {
RedisOp redisOp = redisOpParser.parse(payload);
RedisOp redisOp = redisOpParser.parse(payload);
redisOpItem.setRedisOpType(redisOp.getOpType());
redisOpItem.setGtid(redisOp.getOpGtid());
redisOpItem.setDbId(redisOp.getDbId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ public enum RedisOpMultiKeysEnum {
MSETNX(RedisOpType.MSETNX, 1, 2),
DEL(RedisOpType.DEL, 1, 1),
UNLINK(RedisOpType.UNLINK, 1, 1),
LMPOP(RedisOpType.LMPOP, 2, 1),
BLMPOP(RedisOpType.BLMPOP, 3, 1),
ZMPOP(RedisOpType.ZMPOP, 2, 1),
BZMPOP(RedisOpType.BZMPOP, 3, 1),

//crdt,
CRDT_DEL_REG(RedisOpType.CRDT_DEL_REG, 1, 1),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@
import com.ctrip.xpipe.redis.core.redis.operation.RedisOpType;
import com.ctrip.xpipe.redis.core.redis.operation.op.RedisOpMultiKVs;
import com.ctrip.xpipe.tuple.Pair;
import org.checkerframework.checker.units.qual.A;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
Expand All @@ -17,6 +19,12 @@
*/
public class RedisOpMultiKeysParser extends AbstractRedisOpParser implements RedisOpParser {

private static final byte[] MIN_BYTES = "MIN".getBytes();
private static final byte[] MIN_BYTES_LOWER = "min".getBytes();

private static final byte[] MAX_BYTES = "MAX".getBytes();
private static final byte[] MAX_BYTES_LOWER = "max".getBytes();

private RedisOpType redisOpType;
private Integer keyStartIndex;
private Integer kvNum;
Expand All @@ -37,7 +45,17 @@ public RedisOp parse(byte[][] args) {
List<Pair<RedisKey, byte[]>> kvs = new ArrayList<>();

int i = keyStartIndex;
while (i < args.length) {
int argLen = args.length;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

扫一下8.0注册了getkeyrequests_proc的命令,如果都符合可以用这种写法;否则可以参考下Redis的写法,对于特殊的key获取定义方法注册到命令定义枚举

if(keyStartIndex>1){
int numKeys;
try {
numKeys = Integer.parseInt(new String(args[keyStartIndex-1]));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("Invalid numkeys for " + redisOpType);
}
argLen = i+numKeys;
}
while (i < argLen) {
RedisKey key = new RedisKey(args[i++]);
kvs.add(kvNum == 1 ? Pair.of(key, null) : Pair.of(key, args[i++]));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ public enum RedisOpWithSubKeysEnum {
HINCRBYFLOAT(RedisOpType.HINCRBYFLOAT, 1, 2,false),
HMSET(RedisOpType.HMSET, 1, 2,false),
HSET(RedisOpType.HSET, 1, 2,false),
HSETEX(RedisOpType.HSET, 1, 2,false),
HSETEX(RedisOpType.HSETEX, 1, 2,false),
HGETEX(RedisOpType.HGETEX, 1, 1,false),
HGETDEL(RedisOpType.HGETDEL, 1, 1,false),
HEXPIRE(RedisOpType.HEXPIRE, 1, 1,false),
HEXPIREAT(RedisOpType.HEXPIREAT, 1, 1,false),
HPEXPIRE(RedisOpType.HPEXPIRE, 1, 1,false),
HPEXPIREAT(RedisOpType.HPEXPIREAT, 1, 1,false),
HPERSIST(RedisOpType.HPERSIST, 1, 1,false),
HSETNX(RedisOpType.HSETNX, 1, 2,false),

ZADD(RedisOpType.ZADD, 1, 2,true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
import com.ctrip.xpipe.redis.core.redis.operation.RedisOp;
import com.ctrip.xpipe.redis.core.redis.operation.RedisOpParser;
import com.ctrip.xpipe.redis.core.redis.operation.RedisOpType;
import com.ctrip.xpipe.redis.core.redis.operation.op.RedisOpMultiKVs;
import com.ctrip.xpipe.redis.core.redis.operation.op.RedisOpMultiSubKey;
import com.ctrip.xpipe.tuple.Pair;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.nio.ByteBuffer;
import java.util.*;

/**
* @author tb
Expand All @@ -24,12 +22,28 @@ public class RedisOpWithSubKeysParser extends AbstractRedisOpParser implements R
private Integer kvNum;
private boolean kvReverse;

private byte[] NX_BYTES = new byte[]{'N','X'};
private byte[] XX_BYTES = new byte[]{'X','X'};
private byte[] GT_BYTES = new byte[]{'G','T'};
private byte[] LT_BYTES = new byte[]{'L','T'};
private byte[] CH_BYTES = new byte[]{'C','H'};
private byte[] INCR_BYTES = new byte[]{'I','N','C','R'};
private static final Set<ByteBuffer> NON_KEY_COMMANDS = new HashSet<>();
private static final Set<ByteBuffer> NON_KEY_COMMANDS_WITH_ARGS = new HashSet<>();

private static final byte[] FIELDS_BYTES = "FIELDS".getBytes();
private static final byte[] FIELDS_BYTES_LOWER = "fields".getBytes();

static {
// 无参数关键字
String[] noArgCmds = {
"NX", "nx", "XX", "xx", "GT", "gt", "LT", "lt", "CH", "ch",
"INCR", "incr", "KEEPTTL", "keepttl", "FNX", "fnx", "FXX", "fxx", "PERSIST", "persist"
};
for (String cmd : noArgCmds) {
NON_KEY_COMMANDS.add(ByteBuffer.wrap(cmd.getBytes()));
}
// 带一个参数的关键字
String[] oneArgCmds = { "EX", "ex", "PX", "px", "EXAT", "exat", "PXAT", "pxat" };
for (String cmd : oneArgCmds) {
NON_KEY_COMMANDS_WITH_ARGS.add(ByteBuffer.wrap(cmd.getBytes()));
NON_KEY_COMMANDS.add(ByteBuffer.wrap(cmd.getBytes())); // 也在无参数集合中,因为需要先识别
}
}

public RedisOpWithSubKeysParser(RedisOpType redisOpType, int keyStartIndex, int kvNum,boolean kvReverse) {
this.redisOpType = redisOpType;
Expand All @@ -43,50 +57,100 @@ public RedisOp parse(byte[][] args) {
Pair<RedisOpType, byte[][]> pair = redisOpType.transfer(redisOpType, args);
args = pair.getValue();

// 计算容量 - 主键 + 子键数量
int subKeyCount = (args.length - keyStartIndex-1) / kvNum;
int capacity = 1 + subKeyCount;
List<RedisKey> subKeys = new ArrayList<>(capacity);

int i = keyStartIndex;
RedisKey key = new RedisKey(args[i++]);
// 处理子键
while (i < args.length) {
RedisKey subKey = null;

if (!kvReverse) {
// 正常顺序:子键在前
subKey = new RedisKey(args[i]);
i += kvNum; // 跳过值部分
} else {
RedisKey key = new RedisKey(args[keyStartIndex]);
int dataStart = keyStartIndex + 1; // 主键之后的数据起始位置
int subKeyStart;
int subKeyCount;

if (redisOpType.isFields()) {
// 1. 跳过 FIELDS 之前的所有标志
int idx = dataStart;
while (idx < args.length) {
if (isFieldsKeyword(args[idx])) {
idx++;
break;
}
int skipped = skipNonKeyTokenIfPresent(args, idx);
if (skipped > idx) {
idx = skipped;
} else {
idx++;
}
}
if (idx >= args.length) throw new IllegalArgumentException("Missing numfields");
subKeyCount = Integer.parseInt(new String(args[idx++]));
subKeyStart = idx;
} else {
// 2. 普通命令:跳过开头的非键标志(如 ZADD 的 NX/EX 等)
subKeyStart = skipLeadingNonKeyTokens(args, dataStart);
subKeyCount = (args.length - subKeyStart) / kvNum;
}

// 3. 共用同一套子键提取逻辑
List<RedisKey> subKeys = extractSubKeys(args, subKeyStart, subKeyCount);
RedisOpType finalOpType = redisOpType.isFields() ? redisOpType : pair.getKey();
return new RedisOpMultiSubKey(finalOpType, args, key, subKeys);
}

/**
* 统一子键提取:从 start 开始,每次取一个子键,然后按 kvNum 跳过值部分。
* 支持 kvReverse(反向模式)和 ZADD 非键标志跳过。
*/
private List<RedisKey> extractSubKeys(byte[][] args, int start, int count) {
List<RedisKey> subKeys = new ArrayList<>(count);
int i = start;
for (int k = 0; k < count; k++) {
if (kvReverse) {
// 反向顺序:值在前,子键在后
if (kvNum == 2) {
if(RedisOpType.ZADD == redisOpType && nonKey(args[i])){
i++;
if (kvNum == 2 && RedisOpType.ZADD == redisOpType) {
int skipped = skipNonKeyTokenIfPresent(args, i);
if (skipped > i) {
i = skipped;
k--; // 未消耗子键,重新处理当前子键
continue;
}
subKey = new RedisKey(args[i + 1]); // 跳过第一个值,取第二个作为子键
i += 2;
} else if (kvNum == 3) {
subKey = new RedisKey(args[i + 2]); // 跳过前两个值,取第三个作为子键
i += 3;
}
int keyOffset = kvNum - 1; // kvNum=2 → offset=1, kvNum=3 → offset=2
subKeys.add(new RedisKey(args[i + keyOffset]));
i += kvNum;
} else {
subKeys.add(new RedisKey(args[i]));
i += kvNum;
}
}
return subKeys;
}

subKeys.add(subKey);
/** 跳过从 index 开始的所有连续非键标志,返回第一个非标志的索引 */
private int skipLeadingNonKeyTokens(byte[][] args, int index) {
while (index < args.length) {
int skipped = skipNonKeyTokenIfPresent(args, index);
if (skipped == index) break;
index = skipped;
}
return index;
}

/** 跳过单个非键标志(及其可能的参数),返回新的索引 */
private int skipNonKeyTokenIfPresent(byte[][] args, int index) {
if (index >= args.length || !nonKey(args[index])) return index;
index++;
if (index < args.length && NON_KEY_COMMANDS_WITH_ARGS.contains(ByteBuffer.wrap(args[index - 1]))) {
index++;
}
return index;
}

return new RedisOpMultiSubKey(pair.getKey(), args, key,subKeys);
private boolean isFieldsKeyword(byte[] arg) {
return Arrays.equals(arg, FIELDS_BYTES) || Arrays.equals(arg, FIELDS_BYTES_LOWER);
}

@Override
public int getOrder() {
return 0;
}

private boolean nonKey(byte[] args){
return Arrays.equals(NX_BYTES,args) || Arrays.equals(XX_BYTES,args)
|| Arrays.equals(GT_BYTES,args) || Arrays.equals(LT_BYTES,args)
|| Arrays.equals(CH_BYTES,args) || Arrays.equals(INCR_BYTES,args);
private boolean nonKey(byte[] arg) {
return NON_KEY_COMMANDS.contains(ByteBuffer.wrap(arg));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ private RdbConstant() {
public static final short REDIS_RDB_TYPE_HASH_ZIPLIST = 13;
public static final short REDIS_RDB_TYPE_LIST_QUICKLIST = 14;
public static final short REDIS_RDB_TYPE_STREAM_LISTPACKS = 15;
// public static final short REDIS_RDB_TYPE_BITMAP = 16;
public static final short REDIS_RDB_TYPE_BITMAP = 192;
public static final short REDIS_RDB_TYPE_BITMAP_9 = 16;
public static final short REDIS_RDB_TYPE_CRDT = 200;

// Redis 8.x 新增操作码 (rdb.h)
Expand Down
Loading
Loading