|
Command在Mongodb中是一类特殊操作,它提供了强大的管理及各项操作(比如建库,索引,删除集合等)。可以说通过Command可以完成几乎所有想做的事情。同时Mongodb开发者在Command上又做了非常清晰体系架构和设计,便于管理和高效执行各种类型的Command。
今天就专门用一篇篇幅来着重介绍一下其Command的体系架构,并用例子来介绍mongod是如何将Command引入其中的。
为了对其中大部分command对一个大致的了解,我们可以用下面指令来显示一个command列表:
1.mongod --dbpath d:\mongodb\db --port 27017 --rest
2.在浏览器上输入链接地址:http://localhost:28017/_commands
这里mongod就会为我们显示command列表,大约有90多个,这是显示截图:

上面90多个类中,按其使用场景可以为分如下几类,分别是:
dbcommand.cpp:一般数据库指令,如数据库,索引的创建,重建,打开/关闭等
dbcommands_admin.cpp:管理指令,如CleanCmd,JournalLatencyTestCmd,ValidateCmd,FSyncCommand
dbcommands_generic.cpp:常用指令,ListCommandsCmd,LogRotateCmd,PingCommand,CmdSet,CmdGet等
replset_commands.cpp:复制集指令,CmdReplSetTest,CmdReplSetGetStatus,CmdReplSetReconfig等
security_commands.cpp:安全指令,CmdGetNonce,CmdLogout,CmdAuthenticate
commands_admin.cpp:shard管理操作,因其位于mongos项目,这里暂不介绍
commands_public.cpp:shard公用操作,因其位于mongos项目,这里暂不介绍
下面是相关类图:

-----------------------------分割线--------------------------------

-----------------------------分割线--------------------------------
-----------------------------分割线--------------------------------
-----------------------------分割线--------------------------------
首先我们看一下在Command的基类,其用于定义子类要实现的方法及属性,自身也实现了一些通用方法,比如htmlHelp(用于以html方法显示该command的帮助信息),构造方法,findCommand(查询命令)等,其声明如下:
//commands.h
class Command {
public:
//执行当前Command时所使用的锁类型
enum LockType { READ = -1/*读*/ , NONE = 0 /*无锁*/, WRITE = 1 /*写*/};
const string name;
/* 运行指定的命令,需要子类实现
fromRepl - command is being invoked as part of replication syncing. In this situation you
normally do not want to log the command to the local oplog.
如执行成功返回true,否则为false, errmsg记录错误信息
*/
virtual bool run(const string& db, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) = 0;
/*
note: logTheTop() MUST be false if READ
if NONE, can't use Client::Context setup
use with caution
*/
virtual LockType locktype() const = 0;
/* 是否有管理特权才可运行该命令 has privileges to run this command. */
virtual bool adminOnly() const {
return false;
}
//html格式的帮助信息
void htmlHelp(stringstream&) const;
/* 与adminOnly相似,但更严格: 要么被验证,要么只运行在本地接口(local interface)
注:当本属性为true时,adminOnly()也必须为true.
*/
virtual bool localHostOnlyIfNoAuth(const BSONObj& cmdObj) { return false; }
/* 如果replication pair 的slaves可以运行命令的,则返回true
(the command directly from a client -- if fromRepl, always allowed).
*/
virtual bool slaveOk() const = 0;
/* 通过在查询命令中打开 'slaveok'选项,客户端强制在一个slave上运行一个命令时,返回true.
*/
virtual bool slaveOverrideOk() {
return false;
}
/* Override and return true to if true,log the operation (logOp()) to the replication log.
(not done if fromRepl of course)
Note if run() returns false, we do NOT log.
*/
virtual bool logTheOp() { return false; }
virtual void help( stringstream& help ) const;
/* Return true if authentication and security applies to the commands. Some commands
(e.g., getnonce, authenticate) can be done by anyone even unauthorized.
*/
virtual bool requiresAuth() { return true; }
/** @param webUI:在web上暴露当前command,形如 localhost:28017/
@param oldName: 旧选项,表示当前command的旧(已弃用)名称
*/
Command(const char *_name, bool webUI = false, const char *oldName = 0);
virtual ~Command() {}
protected:
BSONObj getQuery( const BSONObj& cmdObj ) {
if ( cmdObj["query"].type() == Object )
return cmdObj["query"].embeddedObject();
if ( cmdObj["q"].type() == Object )
return cmdObj["q"].embeddedObject();
return BSONObj();
}
static void logIfSlow( const Timer& cmdTimer, const string& msg);
//command map,其包含系统实现的所有command对象,以便findCommand查询时使用
//注意也包含该command的旧名称(构造方法中的oldName参数)所对应的对象,
static map * _commands;
//与上面形同,但不含旧名称的command map
static map * _commandsByBestName;
//将web类型的command放到该map中
static map * _webCommands;
public:
static const map* commandsByBestName() { return _commandsByBestName; }
static const map* webCommands() { return _webCommands; }
/** @return 返回是否找到或已执行command */
static bool runAgainstRegistered(const char *ns, BSONObj& jsobj, BSONObjBuilder& anObjBuilder);
static LockType locktype( const string& name );
//根据命令名称在集合中找到相应Command对象
static Command * findCommand( const string& name );
};
Command基类中提供了几个map类型的集合map,用于将系统实现的Command进行收集,以便后面findCommand进行便历查询时使用。如下:
//commands.cpp
Command* Command::findCommand( const string& name ) {
//从_commands map中找到指定name的Command对象
map::iterator i = _commands->find( name );
if ( i == _commands->end() )//如果已到结尾,表示未找到
return 0;
return i->second;//返回Command对象
}
看到上面代码中的_commands大家可能要问,该map是如何初始化并将系统实现的各个Command注册到其中呢?答案就在Command的构造方法中,如下:
//command.cpp
Command::Command(const char *_name, bool web, const char *oldName) : name(_name) {
// register ourself.
//如为空(系统刚启动时)则实例化_commands
if ( _commands == 0 )
_commands = new map;
//如为空(系统刚启动时)则实例化_commandsByBestName
if( _commandsByBestName == 0 )
_commandsByBestName = new map;
Command*& c = (*_commands)[name];//获取指定名称的command对象
if ( c )//如有,表示之前已注册了该command
log() nReturned = 1;
result.setData( qr.release(), true );//设置返回结果
}
else {
uasserted(13530, "bad or malformed command request?");
}
return 0;
}
.....
}
上面代码对传递来的查询消息QueryMessage(有关消息机制参见我的这篇文章)进行分析之后,如果发现其为command时,执行runCommands方法:
//query.cpp
bool runCommands(const char *ns, BSONObj& jsobj, CurOp& curop, BufBuilder &b, BSONObjBuilder& anObjBuilder, bool fromRepl, int queryOptions) {
try {
return _runCommands(ns, jsobj, b, anObjBuilder, fromRepl, queryOptions);
}
catch ( AssertionException& e ) {
e.getInfo().append( anObjBuilder , "assertion" , "assertionCode" );
}
curop.debug().str ensureStarted();
string dbname = nsToDatabase( ns );
if( logLevel >= 1 )
log() |
|