设为首页 收藏本站
查看: 1432|回复: 0

[经验分享] mongodb源码分析(三)mongo的启动

[复制链接]

尚未签到

发表于 2015-11-10 13:50:51 | 显示全部楼层 |阅读模式
  mongo是mongodb的一个C++写的javascript交互式的可执行客户端。为了分析mongod对于服务请求的响应,这里分析下mongo。先来看看mongo的启动,mongo的启动代码在mongo/shell/dbshell.cpp中。其支持两个javascript 引擎,因为用visual stdio2012调试时默认编译的是spidermonkey,那么就分析spidermonkey那边吧。
  当我们使用mongodb时最简单的一个保存命令是db.coll.save({a:1}),我们知道db是javascript对象,也是数据库名,coll是数据集collection,这里将{a:1}保存到coll的集合中,但是这里有一个疑问,javascript对象对于未定义的属性的访问不是通过db[coll]访问的吗,coll这里本应该是undefined,但是为什么结果成功的保存了呢。当我在阅读源码时就有这么个疑问,还好终于从源码中找到了答案,好了废话不多说了,直接分析。
  

    po::options_description shell_options( "options" );//同样是一大堆启动命令,具体含义不说了,自己可以看相关的文档
po::options_description hidden_options( "Hidden options" );
po::options_description cmdline_options( "Command line options" );
po::positional_options_description positional_options;
shell_options.add_options()
( "shell", "run the shell after executing files" )
( "nodb", "don't connect to mongod on startup - no 'db address' arg expected" )
( "norc", "will not run the \".mongorc.js\" file on start up" )
( "quiet", "be less chatty" )
( &quot;port&quot;, po::value<string>( &port ), &quot;port to connect to&quot; )
( &quot;host&quot;, po::value<string>( &dbhost ), &quot;server to connect to&quot; )
( &quot;eval&quot;, po::value<string>( &script ), &quot;evaluate javascript&quot; )
( &quot;username,u&quot;, po::value<string>(&username), &quot;username for authentication&quot; )
( &quot;password,p&quot;, new mongo::PasswordValue( &password ), &quot;password for authentication&quot; )
( &quot;help,h&quot;, &quot;show this usage information&quot; )
( &quot;version&quot;, &quot;show version information&quot; )
( &quot;verbose&quot;, &quot;increase verbosity&quot; )
( &quot;ipv6&quot;, &quot;enable IPv6 support (disabled by default)&quot; )
    if ( !nodb ) { // connect to db
//if ( ! mongo::cmdLine.quiet ) cout << &quot;url: &quot; << url << endl;
//如果启动时没有指定nodb,那么这里将生成一串javascript代码,用来连接mongod
stringstream ss;
if ( mongo::cmdLine.quiet )
ss << &quot;__quiet = true;&quot;;
ss << &quot;db = connect( \&quot;&quot; << fixHost( url , dbhost , port ) << &quot;\&quot;)&quot;;
mongo::shell_utils::_dbConnect = ss.str();
if ( params.count( &quot;password&quot; ) && password.empty() )
password = mongo::askPassword();
if ( username.size() && password.size()) {
stringstream ss;
ss << &quot;if ( ! db.auth( \&quot;&quot; << username << &quot;\&quot; , \&quot;&quot; << password << &quot;\&quot; ) ){ throw 'login failed'; }&quot;;
mongo::shell_utils::_dbAuth = ss.str();
}
}
    mongo::ScriptEngine::setConnectCallback( mongo::shell_utils::onConnect );//这个回调函数用来做连接成功后的记录工作
mongo::ScriptEngine::setup();
mongo::globalScriptEngine->setScopeInitCallback( mongo::shell_utils::initScope );
auto_ptr< mongo::Scope > scope( mongo::globalScriptEngine->newScope() );
shellMainScope = scope.get();

  现在来看看mongo::globalScriptEngine->newScope(),在mongo::ScriptEngine::setup()函数中globalScriptEngine=new SMEngine()。
  

        virtual Scope * newScope() {
Scope *s = createScope();
if ( s && _scopeInitCallback )//_scopeInitCallback对应上面的mongo::shell_utils::initScope
_scopeInitCallback( *s );
installGlobalUtils( *s );
return s;
}
createScope对应spideMonkey则为new SMScope()  
  

        SMScope() ://spideMonkey的初始化
_this( 0 ),
_reportError( true ),
_externalSetup( false ),
_localConnect( false ) {
smlock;
_context = JS_NewContext( globalSMEngine->_runtime , 8192 );
_convertor = new Convertor( _context );
massert( 10431 ,  &quot;JS_NewContext failed&quot; , _context );
JS_SetOptions( _context , JSOPTION_VAROBJFIX);
//JS_SetVersion( _context , JSVERSION_LATEST); TODO
JS_SetErrorReporter( _context , errorReporter );
_global = JS_NewObject( _context , &global_class, NULL, NULL);
massert( 10432 ,  &quot;JS_NewObject failed for global&quot; , _global );
JS_SetGlobalObject( _context , _global );
massert( 10433 ,  &quot;js init failed&quot; , JS_InitStandardClasses( _context , _global ) );
JS_SetOptions( _context , JS_GetOptions( _context ) | JSOPTION_VAROBJFIX );
JS_DefineFunctions( _context , _global , globalHelpers );//预先定义的几个函数
JS_DefineFunctions( _context , _convertor->getGlobalObject( &quot;Object&quot; ), objectHelpers );
//JS_SetGCCallback( _context , no_gc ); // this is useful for seeing if something is a gc problem
_postCreateHacks();
}
    JSFunctionSpec globalHelpers[] = {//这里定义的几个函数映射,javascript中执行相应的函数则会调用这里的本地函数。
{ &quot;print&quot; , &native_print , 0 , 0 , 0 } ,
{ &quot;nativeHelper&quot; , &native_helper , 1 , 0 , 0 } ,
{ &quot;load&quot; , &native_load , 1 , 0 , 0 } ,
{ &quot;gc&quot; , &native_gc , 1 , 0 , 0 } ,
{ &quot;UUID&quot;, &_UUID, 0, 0, 0 } ,
{ &quot;MD5&quot;, &_MD5, 0, 0, 0 } ,
{ &quot;HexData&quot;, &_HexData, 0, 0, 0 } ,
{ 0 , 0 , 0 , 0 , 0 }
};
继续到newScope函数,接下来调用了_scopeInitCallback()函数,其实就是之前设置的initScope回调函数

        void initScope( Scope &scope ) {
scope.externalSetup();
mongo::shell_utils::installShellUtils( scope );
scope.execSetup(JSFiles::servers);//执行相应的javascript文件,相当于初始化javascript环境,以后命令行中执行相应代码时
scope.execSetup(JSFiles::shardingtest);//就能正确的找到环境了。
scope.execSetup(JSFiles::servers_misc);
scope.execSetup(JSFiles::replsettest);
scope.execSetup(JSFiles::replsetbridge);
if ( !_dbConnect.empty() ) {//执行登录代码
uassert( 12513, &quot;connect failed&quot;, scope.exec( _dbConnect , &quot;(connect)&quot; , false , true , false ) );
if ( !_dbAuth.empty() ) {//用户账号登陆认证
installGlobalUtils( scope );
uassert( 12514, &quot;login failed&quot;, scope.exec( _dbAuth , &quot;(auth)&quot; , true , true , false ) );
}
}
}
initScope->externalSetup->initMongoJS

    void initMongoJS( SMScope * scope , JSContext * cx , JSObject * global , bool local ) {
//JS Class的初始化过程,几乎所有的mongodb使用到的class都在这里创建,所有new XXX对象的构建都是下面这里的函数完成的,比如说db,collection,dbquery
verify( JS_InitClass( cx , global , 0 , &mongo_class , local ? mongo_local_constructor : mongo_external_constructor , 0 , 0 , mongo_functions , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &object_id_class , object_id_constructor , 0 , 0 , 0 , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &db_class , db_constructor , 2 , 0 , 0 , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &db_collection_class , db_collection_constructor , 4 , 0 , 0 , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &internal_cursor_class , internal_cursor_constructor , 0 , 0 , internal_cursor_functions , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &dbquery_class , dbquery_constructor , 0 , 0 , 0 , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &dbpointer_class , dbpointer_constructor , 0 , 0 , dbpointer_functions , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &bindata_class , bindata_constructor , 0 , 0 , bindata_functions , 0 , 0 ) );
//        verify( JS_InitClass( cx , global , 0 , &uuid_class , uuid_constructor , 0 , 0 , uuid_functions , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , ×tamp_class , timestamp_constructor , 0 , 0 , 0 , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &numberlong_class , numberlong_constructor , 0 , 0 , numberlong_functions , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &numberint_class , numberint_constructor , 0 , 0 , numberint_functions , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &minkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &maxkey_class , 0 , 0 , 0 , 0 , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &map_class , map_constructor , 0 , 0 , map_functions , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &bson_ro_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
verify( JS_InitClass( cx , global , 0 , &bson_class , bson_cons , 0 , 0 , bson_functions , 0 , 0 ) );
static const char *dbrefName = &quot;DBRef&quot;;
dbref_class.name = dbrefName;
verify( JS_InitClass( cx , global , 0 , &dbref_class , dbref_constructor , 2 , 0 , bson_functions , 0 , 0 ) );
scope->execCoreFiles();
}

  这里local为false,使用的是mongo_external_constructor函数。先看看mongo_class:
  

    JSClass mongo_class = {//为映射javascript对象而设置的函数,当javascript中new Mongo对象时会调用mongo_external_constructor来构造一个对象
&quot;Mongo&quot;,                                        // class name
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,      // flags
JS_PropertyStub,                                // addProperty
JS_PropertyStub,                                // delProperty
JS_PropertyStub,                                // getProperty
JS_PropertyStub,                                // setProperty
JS_EnumerateStub,                               // enumerate
JS_ResolveStub,                                 // resolve
JS_ConvertStub,                                 // convert
mongo_finalize,                                 // finalize
JSCLASS_NO_OPTIONAL_MEMBERS                     // optional members
};
再看看  mongo_functions:  
  

    JSFunctionSpec mongo_functions[] = {//当在javascript中调用相应的函数时,这里对应的native函数将会被调用到,
{ &quot;auth&quot; , mongo_auth , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ &quot;logout&quot;, mongo_logout, 0, JSPROP_READONLY | JSPROP_PERMANENT, 0 },
{ &quot;find&quot; , mongo_find , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ &quot;update&quot; , mongo_update , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ &quot;insert&quot; , mongo_insert , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ &quot;remove&quot; , mongo_remove , 0 , JSPROP_READONLY | JSPROP_PERMANENT, 0 } ,
{ 0 }
};
我们继续看,现在是出来上面的问题了db.coll.save({a:1})为什么没有问题。  
  

    JSClass db_collection_class = {
&quot;DBCollection&quot;,                                 // class name
JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE,      // flags
JS_PropertyStub,                                // addProperty
JS_PropertyStub,                                // delProperty
JS_PropertyStub,                                // getProperty
JS_PropertyStub,                                // setProperty
JS_EnumerateStub,                               // enumerate
(JSResolveOp)db_collection_resolve,             // 注意这里,这是关键
JS_ConvertStub,                                 // convert
JS_FinalizeStub,                                // finalize
JSCLASS_NO_OPTIONAL_MEMBERS                     // optional members
};
    JSBool db_collection_resolve( JSContext *cx, JSObject *obj, jsval id, uintN flags, JSObject **objp ) {
try {
if ( flags & JSRESOLVE_ASSIGNING )
return JS_TRUE;
Convertor c( cx );
string collname = c.toString( id );
if ( isSpecialName( collname ) )
return JS_TRUE;
if ( obj == c.getGlobalPrototype( &quot;DBCollection&quot; ) )
return JS_TRUE;
JSObject * proto = JS_GetPrototype( cx , obj );
if ( c.hasProperty( obj , collname.c_str() ) || ( proto && c.hasProperty( proto , collname.c_str() )  ) )
return JS_TRUE;
string name = c.toString( c.getProperty( obj , &quot;_shortName&quot; ) );
name += &quot;.&quot;;
name += collname;
jsval db = c.getProperty( obj , &quot;_db&quot; );
if ( ! JSVAL_IS_OBJECT( db ) )
return JS_TRUE;
JSObject * coll = doCreateCollection( cx , JSVAL_TO_OBJECT( db ) , name );//这个地方创建了个集合,当调用db.coll.save()时,javascript调用
if ( ! coll )     //resolve函数时在这里将coll集合创建了出来,所以coll不为undefined
return JS_FALSE;                                                     //原来如此,spidermonkey的具体流程就不清楚了,有感兴趣的自己去看吧
c.setProperty( obj , collname.c_str() , OBJECT_TO_JSVAL( coll ) );
*objp = obj;
}
catch ( const AssertionException& e ) {
if ( ! JS_IsExceptionPending( cx ) ) {
JS_ReportError( cx, e.what() );
}
return JS_FALSE;
}
catch ( const std::exception& e ) {
log() << &quot;unhandled exception: &quot; << e.what() << &quot;, throwing Fatal Assertion&quot; << endl;
fassertFailed( 16303 );
}
return JS_TRUE;
}
继续回到上面initMongoJS最后一行:        scope->execCoreFiles();  
  

    void Scope::execCoreFiles() {//在javascript环境中执行一遍上面文件,然后javascript环境中就会留下那些文件的代码与数据,以后
// keeping same order as in SConstruct//在命令行中执行命令时就能正确执行相应代码了。
execSetup(JSFiles::utils);
execSetup(JSFiles::utils_sh);
execSetup(JSFiles::db);
execSetup(JSFiles::mongo);
execSetup(JSFiles::mr);
execSetup(JSFiles::query);
execSetup(JSFiles::collection);
}回到initScope:  
  

            if ( !_dbConnect.empty() ) {//执行登录代码
uassert( 12513, &quot;connect failed&quot;, scope.exec( _dbConnect , &quot;(connect)&quot; , false , true , false ) );
if ( !_dbAuth.empty() ) {//用户账号登陆认证
installGlobalUtils( scope );
uassert( 12514, &quot;login failed&quot;, scope.exec( _dbAuth , &quot;(auth)&quot; , true , true , false ) );
}
}

  

下面来看看登录:
        ss << &quot;db = connect( \&quot;&quot; << fixHost( url , dbhost , port ) << &quot;\&quot;)&quot;;
这里就是产生的javascript连接代码:
string fixHost( string url , string host , string port ) {//url传进来初始化值为test,这里就可以看出为啥默认连接到了test数据库了。
//cout << &quot;fixHost url: &quot; << url << &quot; host: &quot; << host << &quot; port: &quot; << port << endl;//若什么都没设置也知道为什么连接到了
//127.0.0.1:27017
if ( host.size() == 0 && port.size() == 0 ) {
if ( url.find( &quot;/&quot; ) == string::npos ) {
// check for ips
if ( url.find( &quot;.&quot; ) != string::npos )
return url + &quot;/test&quot;;
if ( url.rfind( &quot;:&quot; ) != string::npos &&
isdigit( url[url.rfind(&quot;:&quot;)+1] ) )
return url + &quot;/test&quot;;
}
return url;
}
if ( url.find( &quot;/&quot; ) != string::npos ) {
cerr << &quot;url can't have host or port if you specify them individually&quot; << endl;
::_exit(-1);
}
if ( host.size() == 0 )
host = &quot;127.0.0.1&quot;;
string newurl = host;
if ( port.size() > 0 )
newurl += &quot;:&quot; + port;
else if ( host.find(':') == string::npos ) {
// need to add port with IPv6 addresses
newurl += &quot;:27017&quot;;
}
newurl += &quot;/&quot; + url;
return newurl;
}
下面来看看javascript代码connectconnect = function( url , user , pass ){
chatty( &quot;connecting to: &quot; + url )
if ( user && ! pass )
throw &quot;you specified a user and not a password.  either you need a password, or you're using the old connect api&quot;;
var idx = url.lastIndexOf( &quot;/&quot; );
var db;
if ( idx < 0 )
db = new Mongo().getDB( url );//这里创建了Mongo对象,上面代码分析时已经提到New Mongo()会调用本地函数:mongo_external_constructor
else
db = new Mongo( url.substring( 0 , idx ) ).getDB( url.substring( idx + 1 ) );
if ( user && pass ){
if ( ! db.auth( user , pass ) ){
throw &quot;couldn't login&quot;;
}
}
return db;
}
我们回到C&#43;&#43;函数:mongo_external_constructor
    JSBool mongo_external_constructor( JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval ) {
try {
smuassert( cx ,  &quot;0 or 1 args to Mongo&quot; , argc <= 1 );
string host( &quot;127.0.0.1&quot; );
Convertor c( cx );
if ( argc > 0 )
host = c.toString( argv[0] );
string errmsg;//注意这里的ConnectionString,其形式可为:server   server:port   foo/server:port,server:port  server,server,server
ConnectionString cs = ConnectionString::parse( host , errmsg );//可同时配置多个IP,还记得在QQ群里有个人问,使用replset模式时如
if ( ! cs.isValid() ) {//果主服务器挂了,那么php中怎么配置呢,我不知道php怎么配置,
JS_ReportError( cx , errmsg.c_str() );                    //但是看到这里大家应该明白了,mongo的连接地址配置其实是可以是
return JS_FALSE;                                          //多地址的,其connection有几种模式具体参见:ConnectionString
}
shared_ptr< DBClientWithCommands > conn( cs.connect( errmsg ) );//这里通过了ConnectionString屏蔽了具体的连接过程
if ( ! conn ) {
JS_ReportError( cx , errmsg.c_str() );
return JS_FALSE;
}
try {
ScriptEngine::runConnectCallback( *conn );//连接成功调用上面说的函数mongo::shell_utils::onConnect记录连接
}
catch ( const AssertionException& e ){
// Can happen if connection goes down while we're starting up here
// Catch so that we don't get a hard-to-trace segfault from SM
JS_ReportError( cx, ((string)( str::stream() << &quot;Error during mongo startup.&quot; << causedBy( e ) )).c_str() );
return JS_FALSE;
}
catch ( const std::exception& e ) {
log() << &quot;unhandled exception: &quot; << e.what() << &quot;, throwing Fatal Assertion&quot; << endl;
fassertFailed( 16294 );
}
verify( JS_SetPrivate( cx , obj , (void*)( new shared_ptr< DBClientWithCommands >( conn ) ) ) );
jsval host_val = c.toval( host.c_str() );
verify( JS_SetProperty( cx , obj , &quot;host&quot; , &host_val ) );
}
catch ( const AssertionException& e ) {
if ( ! JS_IsExceptionPending( cx ) ) {
JS_ReportError( cx, e.what() );
}
return JS_FALSE;
}
catch ( const std::exception& e ) {
log() << &quot;unhandled exception: &quot; << e.what() << &quot;, throwing Fatal Assertion&quot; << endl;
fassertFailed( 16295 );
}
return JS_TRUE;
}
继续回到initScope,来看看认证过程。

                if ( !_dbAuth.empty() ) {//用户账号登陆认证
installGlobalUtils( scope );
uassert( 12514, &quot;login failed&quot;, scope.exec( _dbAuth , &quot;(auth)&quot; , true , true , false ) );
}
            ss << &quot;if ( ! db.auth( \&quot;&quot; << username << &quot;\&quot; , \&quot;&quot; << password << &quot;\&quot; ) ){ throw 'login failed'; }&quot;;
通过上面的分析可知这里直接调用了函数mongo_auth。这个函数调用DBClientWithCommands::auth函数执行具体的认证动作。
    bool DBClientWithCommands::auth(const string &dbname, const string &username, const string &password_text, string& errmsg, bool digestPassword, Auth::Level * level) {
string password = password_text;
if( digestPassword )
password = createPasswordDigest( username , password_text );//将用户名密码按照user:mongo:passwd做md5
if ( level != NULL )
*level = Auth::NONE;
BSONObj info;
string nonce;
if( !runCommand(dbname, getnoncecmdobj, info) ) {//执行命令{getnonce:1}从服务端得到一个nonce的值
errmsg = &quot;getnonce fails - connection problem?&quot;;
return false;
}
{
BSONElement e = info.getField(&quot;nonce&quot;);
verify( e.type() == String );
nonce = e.valuestr();
}
BSONObj authCmd;
BSONObjBuilder b;
{
b << &quot;authenticate&quot; << 1 << &quot;nonce&quot; << nonce << &quot;user&quot; << username;
md5digest d;
{
md5_state_t st;
md5_init(&st);
md5_append(&st, (const md5_byte_t *) nonce.c_str(), nonce.size() );
md5_append(&st, (const md5_byte_t *) username.data(), username.length());
md5_append(&st, (const md5_byte_t *) password.c_str(), password.size() );
md5_finish(&st, d);
}
b << &quot;key&quot; << digestToString( d );
authCmd = b.done();
}
if( runCommand(dbname, authCmd, info) ) {//执行命令authenticate验证用户,数据发送到服务端后服务端验证用户通过后会将其加入到
if ( level != NULL ) {//一个map中,其保存了认证的数据库,用户名,以及权限,权限只有读或者写
if ( info.getField(&quot;readOnly&quot;).trueValue() )
*level = Auth::READ;
else
*level = Auth::WRITE;
}
return true;
}
errmsg = info.toString();
return false;
}
到这里initscope执行完毕,javascript初始化完毕。进入读取用户输入执行用户输入的javascript代码的流程中,部分代码如下:
            bool wascmd = false;
{
string cmd = linePtr;
if ( cmd.find( &quot; &quot; ) > 0 )
cmd = cmd.substr( 0 , cmd.find( &quot; &quot; ) );
if ( cmd.find( &quot;\&quot;&quot; ) == string::npos ) {
try {//这里判断用户的输入是否是命令比如说use database,it这种命令
scope->exec( (string)&quot;__iscmd__ = shellHelper[\&quot;&quot; + cmd + &quot;\&quot;];&quot; , &quot;(shellhelp1)&quot; , false , true , true );
if ( scope->getBoolean( &quot;__iscmd__&quot; )  ) {//这里确实是命令,那么就去执行这些命令
scope->exec( (string)&quot;shellHelper( \&quot;&quot; + cmd + &quot;\&quot; , \&quot;&quot; + code.substr( cmd.size() ) + &quot;\&quot;);&quot; , &quot;(shellhelp2)&quot; , false , true , false );
wascmd = true;
}
}
catch ( std::exception& e ) {
cout << &quot;error2:&quot; << e.what() << endl;
wascmd = true;
}
}
}
if ( ! wascmd ) {
try {//不是确切的命令,按照一般javascript的代码规则执行
if ( scope->exec( code.c_str() , &quot;(shell)&quot; , false , true , false ) )
scope->exec( &quot;shellPrintHelper( __lastres__ );&quot; , &quot;(shell2)&quot; , true , true , false );//打印结果,比如说用户查询
}                                                                                            //结果就是这里打印出来的
catch ( std::exception& e ) {
cout << &quot;error:&quot; << e.what() << endl;
}
}
shellHistoryAdd( code.c_str() );//记录这个代码,下次可以使用,用户可以按上下键调出就像linux shell一样。
free( line );
}
shellHistoryDone();
}



至此mongo的启动流程就分析完了,具体来说可以分为:
1. javascript环境的初始化(包括了一些辅助函数的注入如native_print和mongodb对象的注入如collection,db,dbquery)。
2. 客户端的登录流程(需要注意的是配置服务端地址时可设置多服务端地址,具体可参考ConnectionString这个类的实现)。


原文链接:http://blog.iyunv.com/yhjj0108/article/details/8252958
作者:yhjj0108,杨浩


















  

版权声明:本文为博主原创文章,未经博主允许不得转载。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-137588-1-1.html 上篇帖子: mongodb 数据库安装 Windows服务注册 下篇帖子: mongodb源码分析(四)查询1之mongo的查询请求
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表