mirror of
https://github.com/joyieldInc/predixy.git
synced 2025-12-24 22:46:41 +08:00
Merge pull request #28 from yoonian/master
Add support for custom commands
This commit is contained in:
commit
4b8f578a2f
94
conf/command.conf
Normal file
94
conf/command.conf
Normal file
@ -0,0 +1,94 @@
|
||||
## Custom Command
|
||||
## CustomCommand {
|
||||
## command { #command string, must be lowercase
|
||||
## [Mode read|write|admin[|[keyAt2|keyAt3]] #default write, default key position is 1
|
||||
## [MinArgs [2-]] #default 2, including command itself
|
||||
## [MaxArgs [2-]] #default 2, must be MaxArgs >= MinArgs
|
||||
## }...
|
||||
## }
|
||||
|
||||
## Currently support maximum 16 custom commands
|
||||
|
||||
## Example:
|
||||
#CustomCommand {
|
||||
##------------------------------------------------------------------------
|
||||
# custom.ttl {
|
||||
# Mode keyAt2
|
||||
# MinArgs 3
|
||||
# MaxArgs 3
|
||||
# }
|
||||
#### custom.ttl miliseconds key
|
||||
#### Mode = write|keyAt2, MinArgs/MaxArgs = 3 = command + miliseconds + key
|
||||
##------------------------------------------------------------------------
|
||||
## from redis source src/modules/hello.c
|
||||
# hello.push.native {
|
||||
# MinArgs 3
|
||||
# MaxArgs 3
|
||||
# }
|
||||
#### hello.push.native key value
|
||||
#### Mode = write, MinArgs/MaxArgs = 3 = command) + key + value
|
||||
##------------------------------------------------------------------------
|
||||
# hello.repl2 {
|
||||
# }
|
||||
#### hello.repl2 <list-key>
|
||||
#### Mode = write, MinArgs/MaxArgs = 2 = command + list-key
|
||||
##------------------------------------------------------------------------
|
||||
# hello.toggle.case {
|
||||
# }
|
||||
#### hello.toggle.case key
|
||||
#### Mode = write, MinArgs/MaxArgs = 2 = command + key
|
||||
##------------------------------------------------------------------------
|
||||
# hello.more.expire {
|
||||
# MinArgs 3
|
||||
# MaxArgs 3
|
||||
# }
|
||||
#### hello.more.expire key milliseconds
|
||||
#### Mode = write, MinArgs/MaxArgs = 3 = command + key + milliseconds
|
||||
##------------------------------------------------------------------------
|
||||
# hello.zsumrange {
|
||||
# MinArgs 4
|
||||
# MaxArgs 4
|
||||
# Mode read
|
||||
# }
|
||||
#### hello.zsumrange key startscore endscore
|
||||
#### Mode = read, MinArgs/MaxArgs = 4 = command + key + startscore + endscore
|
||||
##------------------------------------------------------------------------
|
||||
# hello.lexrange {
|
||||
# MinArgs 6
|
||||
# MaxArgs 6
|
||||
# Mode read
|
||||
# }
|
||||
#### hello.lexrange key min_lex max_lex min_age max_age
|
||||
#### Mode = read, MinArgs/MaxArgs = 6 = command + key + min_lex + max_lex + min_age + max_age
|
||||
##------------------------------------------------------------------------
|
||||
# hello.hcopy {
|
||||
# MinArgs 4
|
||||
# MaxArgs 4
|
||||
# }
|
||||
#### hello.hcopy key srcfield dstfield
|
||||
#### Mode = write, MinArgs/MaxArgs = 4 = command + key + srcfield) + dstfield
|
||||
##------------------------------------------------------------------------
|
||||
## from redis source src/modules/hellotype.c
|
||||
# hellotype.insert {
|
||||
# MinArgs 3
|
||||
# MaxArgs 3
|
||||
# }
|
||||
#### hellotype.insert key value
|
||||
#### Mode = write, MinArgs/MaxArgs = 3 = command + key + value
|
||||
##------------------------------------------------------------------------
|
||||
# hellotype.range {
|
||||
# MinArgs 4
|
||||
# MaxArgs 4
|
||||
# Mode read
|
||||
# }
|
||||
#### hellotype.range key first count
|
||||
#### Mode = read, MinArgs/MaxArgs = 4 = command + key + first + count
|
||||
##------------------------------------------------------------------------
|
||||
# hellotype.len {
|
||||
# Mode read
|
||||
# }
|
||||
#### hellotype.len key
|
||||
#### Mode = read, MinArgs/MaxArgs = 2 = command + key
|
||||
##------------------------------------------------------------------------
|
||||
#}
|
||||
|
||||
@ -93,6 +93,10 @@ Include try.conf
|
||||
# Include dc.conf
|
||||
|
||||
|
||||
################################### COMMAND ####################################
|
||||
## Custom command define, see command.conf
|
||||
#Include command.conf
|
||||
|
||||
################################### LATENCY ####################################
|
||||
## Latency monitor define, see latency.conf
|
||||
Include latency.conf
|
||||
|
||||
@ -9,8 +9,9 @@
|
||||
#include <map>
|
||||
#include "String.h"
|
||||
#include "Command.h"
|
||||
#include "Conf.h"
|
||||
|
||||
const Command Command::CmdPool[Sentinel] = {
|
||||
Command Command::CmdPool[AvailableCommands] = {
|
||||
{None, "", 0, MaxArgs, Read},
|
||||
{Ping, "ping", 1, 2, Read},
|
||||
{PingServ, "ping", 1, 2, Inner},
|
||||
@ -171,11 +172,14 @@ const Command Command::CmdPool[Sentinel] = {
|
||||
{SubMsg, "\000SubMsg", 0, 0, Admin}
|
||||
};
|
||||
|
||||
int Command::Sentinel = Command::MaxCommands;
|
||||
Command::CommandMap Command::CmdMap;
|
||||
|
||||
void Command::init()
|
||||
{
|
||||
int type = 0;
|
||||
for (auto& i : CmdPool) {
|
||||
for (auto j = 0; j < MaxCommands; j++) {
|
||||
const auto& i = CmdPool[j];
|
||||
if (i.type != type) {
|
||||
Throw(InitFail, "command %s unmatch the index in commands table", i.name);
|
||||
}
|
||||
@ -187,3 +191,19 @@ void Command::init()
|
||||
}
|
||||
}
|
||||
|
||||
void Command::addCustomCommand(const CustomCommandConf& ccc) {
|
||||
if (Sentinel >= AvailableCommands) {
|
||||
Throw(InitFail, "too many custom commands(>%d)", MaxCustomCommands);
|
||||
}
|
||||
if (nullptr != find(ccc.name)) {
|
||||
Throw(InitFail, "custom command %s is duplicated", ccc.name);
|
||||
}
|
||||
auto* p = &CmdPool[Sentinel];
|
||||
p->name = ccc.name.c_str();
|
||||
p->minArgs = ccc.minArgs;
|
||||
p->maxArgs = ccc.maxArgs;
|
||||
p->mode = ccc.mode;
|
||||
p->type = (Command::Type)Sentinel++;
|
||||
CmdMap[ccc.name] = p;
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,8 @@
|
||||
#include "Exception.h"
|
||||
#include "HashFunc.h"
|
||||
|
||||
struct CustomCommandConf;
|
||||
|
||||
class Command
|
||||
{
|
||||
public:
|
||||
@ -189,7 +191,9 @@ public:
|
||||
Unsubscribe,
|
||||
SubMsg,
|
||||
|
||||
Sentinel
|
||||
MaxCommands,
|
||||
MaxCustomCommands = 16,
|
||||
AvailableCommands = MaxCommands + MaxCustomCommands,
|
||||
};
|
||||
enum Mode
|
||||
{
|
||||
@ -249,9 +253,11 @@ public:
|
||||
auto it = CmdMap.find(cmd);
|
||||
return it == CmdMap.end() ? nullptr : it->second;
|
||||
}
|
||||
static void addCustomCommand(const CustomCommandConf& pc);
|
||||
static int Sentinel;
|
||||
private:
|
||||
static const int MaxArgs = 100000000;
|
||||
static const Command CmdPool[Sentinel];
|
||||
static Command CmdPool[];
|
||||
class H
|
||||
{
|
||||
public:
|
||||
|
||||
95
src/Conf.cpp
95
src/Conf.cpp
@ -6,6 +6,7 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <fstream>
|
||||
#include "LogFileSink.h"
|
||||
#include "ServerPool.h"
|
||||
@ -32,6 +33,13 @@ bool ServerConf::parse(ServerConf& s, const char* str)
|
||||
return !s.addr.empty();
|
||||
}
|
||||
|
||||
void CustomCommandConf::init(CustomCommandConf&c, const char* name) {
|
||||
c.name = name;
|
||||
c.minArgs = 2;
|
||||
c.maxArgs = 2;
|
||||
c.mode = Command::Write;
|
||||
}
|
||||
|
||||
Conf::Conf():
|
||||
mBind("0.0.0.0:7617"),
|
||||
mWorkerThreads(1),
|
||||
@ -119,6 +127,7 @@ void Conf::setGlobal(const ConfParser::Node* node)
|
||||
const ConfParser::Node* clusterServerPool = nullptr;
|
||||
const ConfParser::Node* sentinelServerPool = nullptr;
|
||||
const ConfParser::Node* dataCenter = nullptr;
|
||||
std::vector<const ConfParser::Node*> latencyMonitors;
|
||||
for (auto p = node; p; p = p->next) {
|
||||
if (setStr(mName, "Name", p)) {
|
||||
} else if (setStr(mBind, "Bind", p)) {
|
||||
@ -147,8 +156,7 @@ void Conf::setGlobal(const ConfParser::Node* node)
|
||||
} else if (setInt(mLogSample[LogLevel::Warn], "LogWarnSample", p)) {
|
||||
} else if (setInt(mLogSample[LogLevel::Error], "LogErrorSample", p)) {
|
||||
} else if (strcasecmp(p->key.c_str(), "LatencyMonitor") == 0) {
|
||||
mLatencyMonitors.push_back(LatencyMonitorConf{});
|
||||
setLatencyMonitor(mLatencyMonitors.back(), p);
|
||||
latencyMonitors.push_back(p);
|
||||
} else if (strcasecmp(p->key.c_str(), "Authority") == 0) {
|
||||
authority = p;
|
||||
} else if (strcasecmp(p->key.c_str(), "ClusterServerPool") == 0) {
|
||||
@ -157,6 +165,8 @@ void Conf::setGlobal(const ConfParser::Node* node)
|
||||
sentinelServerPool = p;
|
||||
} else if (strcasecmp(p->key.c_str(), "DataCenter") == 0) {
|
||||
dataCenter = p;
|
||||
} else if (strcasecmp(p->key.c_str(), "CustomCommand") == 0) {
|
||||
setCustomCommand(p);
|
||||
} else {
|
||||
Throw(UnknownKey, "%s:%d unknown key %s", p->file, p->line, p->key.c_str());
|
||||
}
|
||||
@ -178,6 +188,10 @@ void Conf::setGlobal(const ConfParser::Node* node)
|
||||
if (dataCenter) {
|
||||
setDataCenter(dataCenter);
|
||||
}
|
||||
for (auto& latencyMonitor : latencyMonitors) {
|
||||
mLatencyMonitors.push_back(LatencyMonitorConf{});
|
||||
setLatencyMonitor(mLatencyMonitors.back(), latencyMonitor);
|
||||
}
|
||||
}
|
||||
|
||||
static void setKeyPrefix(std::vector<std::string>& dat, const std::string& v)
|
||||
@ -358,6 +372,83 @@ void Conf::setDataCenter(const ConfParser::Node* node)
|
||||
}
|
||||
}
|
||||
|
||||
void Conf::setCustomCommand(const ConfParser::Node* node)
|
||||
{
|
||||
if (!node->sub) {
|
||||
Throw(InvalidValue, "%s:%d CustomCommand require scope value", node->file, node->line);
|
||||
}
|
||||
for (auto p = node->sub; p; p = p->next) {
|
||||
mCustomCommands.push_back(CustomCommandConf{});
|
||||
auto& cc = mCustomCommands.back();
|
||||
CustomCommandConf::init(cc, p->key.c_str());
|
||||
auto s = p->sub;
|
||||
for (;s ; s = s->next) {
|
||||
if (setInt(cc.minArgs, "MinArgs", s, 2)) {
|
||||
} else if (setInt(cc.maxArgs, "MaxArgs", s, 2, 9999)) {
|
||||
} else if (setCommandMode(cc.mode, "Mode", s)) {
|
||||
} else {
|
||||
Throw(UnknownKey, "%s:%d unknown key %s", s->file, s->line, s->key.c_str());
|
||||
}
|
||||
}
|
||||
if (cc.maxArgs < cc.minArgs) {
|
||||
Throw(InvalidValue, "%s:%d must be MaxArgs >= MinArgs", p->file, p->line);
|
||||
}
|
||||
}
|
||||
for (const auto& cc : mCustomCommands) {
|
||||
Command::addCustomCommand(cc);
|
||||
}
|
||||
}
|
||||
|
||||
bool Conf::setCommandMode(int& mode, const char* name, const ConfParser::Node* n, const int defaultMode)
|
||||
{
|
||||
if (strcasecmp(name, n->key.c_str()) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (n->val.size() == 0) {
|
||||
mode = defaultMode;
|
||||
} else {
|
||||
mode = 0;
|
||||
std::string mask;
|
||||
std::istringstream is(n->val);
|
||||
while (std::getline(is, mask, '|')) {
|
||||
if ((strcasecmp(mask.c_str(), "write") == 0)) {
|
||||
mode |= Command::Write;
|
||||
} else if ((strcasecmp(mask.c_str(), "read") == 0)) {
|
||||
mode |= Command::Read;
|
||||
} else if ((strcasecmp(mask.c_str(), "admin") == 0)) {
|
||||
mode |= Command::Admin;
|
||||
} else if ((strcasecmp(mask.c_str(), "keyAt2") == 0)) {
|
||||
mode |= Command::KeyAt2;
|
||||
} else if ((strcasecmp(mask.c_str(), "keyAt3") == 0)) {
|
||||
mode |= Command::KeyAt3;
|
||||
} else {
|
||||
Throw(InvalidValue, "%s:%d unknown mode %s", n->file, n->line, mask.c_str());
|
||||
}
|
||||
}
|
||||
switch (mode & Command::KeyMask) {
|
||||
case 0:
|
||||
case Command::KeyAt2:
|
||||
case Command::KeyAt3:
|
||||
break;
|
||||
default:
|
||||
Throw(InvalidValue, "%s:%d %s require exclusive key pos", n->file, n->line, name);
|
||||
}
|
||||
switch (mode & Command::AuthMask) {
|
||||
case 0:
|
||||
mode |= Command::Write;
|
||||
break;
|
||||
case Command::Read:
|
||||
case Command::Write:
|
||||
case Command::Admin:
|
||||
break;
|
||||
default:
|
||||
Throw(InvalidValue, "%s:%d %s require exclusive mode", n->file, n->line, name);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Conf::setDC(DCConf& dc, const ConfParser::Node* node)
|
||||
{
|
||||
if (!node->sub) {
|
||||
|
||||
16
src/Conf.h
16
src/Conf.h
@ -19,6 +19,7 @@
|
||||
#include "Distribution.h"
|
||||
#include "ConfParser.h"
|
||||
#include "Auth.h"
|
||||
#include "Command.h"
|
||||
|
||||
struct AuthConf
|
||||
{
|
||||
@ -89,10 +90,20 @@ struct DCConf
|
||||
struct LatencyMonitorConf
|
||||
{
|
||||
std::string name;
|
||||
std::bitset<Command::Sentinel> cmds;
|
||||
std::bitset<Command::AvailableCommands> cmds;
|
||||
std::vector<long> timeSpan;//us
|
||||
};
|
||||
|
||||
struct CustomCommandConf
|
||||
{
|
||||
std::string name;
|
||||
int minArgs;
|
||||
int maxArgs;
|
||||
int mode;
|
||||
|
||||
static void init(CustomCommandConf &c, const char* name);
|
||||
};
|
||||
|
||||
class Conf
|
||||
{
|
||||
public:
|
||||
@ -201,6 +212,8 @@ private:
|
||||
void setDC(DCConf& dc, const ConfParser::Node* n);
|
||||
void setReadPolicy(ReadPolicyConf& c, const ConfParser::Node* n);
|
||||
void setLatencyMonitor(LatencyMonitorConf& m, const ConfParser::Node* n);
|
||||
void setCustomCommand(const ConfParser::Node* n);
|
||||
bool setCommandMode(int& mode, const char* name, const ConfParser::Node* n, const int defaultMode = Command::Write);
|
||||
private:
|
||||
std::string mName;
|
||||
std::string mBind;
|
||||
@ -220,6 +233,7 @@ private:
|
||||
std::vector<DCConf> mDCConfs;
|
||||
std::string mLocalDC;
|
||||
std::vector<LatencyMonitorConf> mLatencyMonitors;
|
||||
std::vector<CustomCommandConf> mCustomCommands;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@ public:
|
||||
Buffer* output(Buffer* buf) const;
|
||||
private:
|
||||
String mName;
|
||||
const std::bitset<Command::Sentinel>* mCmds;
|
||||
const std::bitset<Command::AvailableCommands>* mCmds;
|
||||
std::vector<TimeSpan> mTimeSpan;
|
||||
TimeSpan mLast;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user