MongoDB源码分析——mongo与JavaScript交互

mongo与JavaScript交互

源码版本为MongoDB 2.6分支

 

 

  之前已经说过mongo是MongoDB提供的一个执行JavaScript脚本的客户端工具,执行js其实就是一个js和c++互相调用的过程,当然,因为mongo采用了Google V8 JS引擎,所以调用的实现的核心都由V8实现了,本篇只是分析了mongo是如何使用V8实现功能,对V8的源码不做分析。

  为了了解mongo是如何使用js,我们首先从connect说起:

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() );

之前已经分析过,newScope后会回调initScope函数,下面来看一下initScope的实现:

void initScope( Scope &scope ) {

// Need to define this method before JSFiles::utils is executed.

scope. injectNative( "_useWriteCommandsDefault", useWriteCommandsDefault);

scope. injectNative( "_writeMode", writeMode);

scope. externalSetup();

mongo:: shell_utils:: installShellUtils( scope );

scope. execSetup( JSFiles:: servers);

scope. execSetup( JSFiles:: shardingtest);

scope. execSetup( JSFiles:: servers_misc);

scope. execSetup( JSFiles:: replsettest);

scope. execSetup( JSFiles:: replsetbridge);

scope. installBenchRun();

if ( ! _dbConnect. empty() ) {

uassert( 12513, "connect failed", scope. exec( _dbConnect , "(connect)" , false , true , false ) );

}

if ( ! _dbAuth. empty() ) {

uassert( 12514, "login failed", scope. exec( _dbAuth , "(auth)" , true , true , false ) );

}

}

在解释上面的代码之前,需要先对V8的一些概念有个大概了解,建议先看一下这篇文章(http://blog.csdn.net/f6991/article/details/9292033),现在来分析一个比较重要的函数,V8要执行js函数首先得找到函数的定义,这个过程就是使用 scope. execSetup( JSFiles:: servers); 来实现的。

//每个JSFile对象代表了一个JS文件。

struct JSFile {

const char* name;          //js文件

const StringData& source;  //js内容

};

在execSetup函数内部会调用下面这个函数,对JSFile对应的js文件进行编译,将对应的JS内容记录下来,之后只要执行这个JS文件里面的函数时V8就能找到对应的函数了。

//code对应JSFile:: source

v8::Handle<v8::Script> script =

v8:: Script:: Compile( v8:: String:: New( code. rawData(), code. size()),

v8:: String:: New( name. c_str(), name. length()));

现在已经知道V8是如何找到JS函数了,那connect函数的实现又是在什么地方呢?

initScope( Scope & scope ) -> scope. externalSetup() -> execCoreFiles() -> execSetup(JSFiles ::mongo )

然后查看了mongo的定义:

const JSFile mongo = { "shell/mongo.js" , _jscode_raw_mongo };

在shell/mongo.js文件中搜索“connect = function”就能够找到connect函数,所以initScope函数中的scope. exec( _dbConnect , "(connect)" , false , true , false )执行的js connect函数的定义就在这个地方。

if (typeof Mongo == "undefined"){

Mongo = function( host){

this. init( host);

}

}

connect = function( url, user, pass) {

...

chatty("connecting to: " + url)

var db;

if (slash == -1)

db = new Mongo(). getDB( url);

else

db = new Mongo( url.substring(0, slash)). getDB( url.substring( slash + 1));

print("Called: new Mongo");

if (user && pass) {

if (! db. auth( user, pass)) {

throw Error( "couldn‘t login");

}

}

return db;

}

在mongo.js中首先定义了Mongo类,在connect中调用new Mongo()生成对象,那么在new Mongo()过程中又发生了什么呢?然我们继续回到initScope( Scope & scope ) -> scope. externalSetup()函数:

void V8Scope::externalSetup() {

...

// install the Mongo function object

_MongoFT = FTPtr::New(getMongoFunctionTemplate( this, false));

injectV8Function("Mongo", MongoFT(), _global);

...

}

注释中已经指出这里是初始化Mongo函数对象,没错,这个Mongo function object和mongo.js里面的Mongo对象就是同一个东西,我们来看一下V8是怎么将这两个东西“映射”起来的。

v8::Handle<v8::FunctionTemplate> getMongoFunctionTemplate(V8Scope * scope , bool local ) {

v8::Handle<v8::FunctionTemplate> mongo;

if (local)

mongo = scope-> createV8Function( mongoConsLocal);

else

mongo = scope-> createV8Function( mongoConsExternal);

mongo->InstanceTemplate()-> SetInternalFieldCount(1);

v8::Handle<v8::ObjectTemplate> proto = mongo-> PrototypeTemplate();

scope->injectV8Method("find", mongoFind, proto);

scope->injectV8Method("insert", mongoInsert, proto);

scope->injectV8Method("remove", mongoRemove, proto);

scope->injectV8Method("update", mongoUpdate, proto);

scope->injectV8Method("auth", mongoAuth, proto);

scope->injectV8Method("logout", mongoLogout, proto);

scope->injectV8Method("cursorFromId", mongoCursorFromId, proto);

fassert(16468, _mongoPrototypeManipulatorsFrozen);

for (size_t i = 0; i < _mongoPrototypeManipulators.size (); ++i )

_mongoPrototypeManipulators[i](scope , mongo );

return mongo;

}

首先,mongo = scope-> createV8Function( mongoConsExternal);创建mongo函数模版,这个地方注意“mongoConsExternal”参数,稍后会说明用处。创建mongo模版之后就是很多的scope-> injectV8Method( "find", mongoFind, proto); 这个地方关于函数模版和对象模版的相关东西大家可以看上面的链接。injectV8Method的作用就是将find函数和mongoFind函数“映射”,然后将find函数绑定到mongo函数模版上,实现的效果就是在js中调用Mongo对象的find函数时其实调用的是mongoFind函数。

上面的getMongoFunctionTemplate 函数生成一个Mongo函数模版,保存在_MongoFT中,调用MongoFT可获取该模版。

v8:: Handle< v8:: FunctionTemplate> MongoFT() const { return _MongoFT; }

下面再看一个非常重要的函数调用:

injectV8Function( "Mongo", MongoFT(), _global);

这个函数的作用和injectV8Method差不多,也就是说在js中new Mongo()的时候返回的就是上面的mongo模版,同时会回调“mongoConsExternal”函数,在该函数中连接到数据库,所以在new Mongo()过程中其实已经完成了服务端数据库连接。

v8::Handle<v8::Value> mongoConsExternal(V8Scope* scope, const v8:: Arguments& args) {

cout << "Call : v8::Handle<v8::Value> mongoConsExternal" << endl;

char host[255];

if (args.Length() > 0 && args[0]-> IsString()) {

uassert(16666, "string argument too long", args[0]-> ToString()-> Utf8Length() < 250);

args[0]-> ToString()-> WriteAscii( host);

}

else {

strcpy( host, "127.0.0.1");

}

// only allow function template to be used by a constructor

uassert(16859, "Mongo function is only usable as a constructor",

args. IsConstructCall());

verify(scope->MongoFT()->HasInstance(args.This()));

string errmsg;

ConnectionString cs = ConnectionString::parse(host, errmsg);

if (!cs.isValid()) {

return v8AssertionException( errmsg);

}

DBClientBase* conn;

conn = cs.connect(errmsg);

if (!conn) {

return v8AssertionException( errmsg);

}

v8::Persistent<v8::Object> self = v8:: Persistent< v8:: Object>:: New( args. This());

v8::Local<v8::External> connHandle = scope-> dbClientBaseTracker. track( self, conn);

ScriptEngine::runConnectCallback(*conn);

args.This()->SetInternalField(0, connHandle);

args.This()->ForceSet(scope->v8StringData("slaveOk"), v8:: Boolean:: New( false));

args.This()->ForceSet(scope->v8StringData("host"), scope-> v8StringData( host));

return v8::Undefined();

}

下面我们继续回到js connect函数:

connect = function( url, user, pass) {

...

chatty( "connecting to: " + url)

var db;

if ( slash == -1)

db = new Mongo(). getDB( url);

else

db = new Mongo( url.substring(0, slash)). getDB( url.substring( slash + 1));

print( "Called: new Mongo");

if ( user && pass) {

if (! db. auth( user, pass)) {

throw Error( "couldn‘t login");

}

}

return db;

}

在connect中会注意到一个认证过程(db. auth( user, pass))。 db是Mongo对象,那个在Mongo的函数模版创建过程中我们把auth函数映射到mongoAuth函数,那么此时我们调用的就是mongoAuth。

至此,mongo和JavaScript的交互流程已经可以看出来了,之后的DB、 DBQuery、 DBCollection对象的映射都在installDBAccess函数中,感兴趣的可以自己去看一下。

时间: 2024-10-16 12:12:16

MongoDB源码分析——mongo与JavaScript交互的相关文章

转:Mongodb源码分析之Replication模式

原文出处:http://www.cnblogs.com/daizhj/archive/2011/06/13/mongodb_sourcecode_rep mongodb中提供了复制(Replication)机制,通过该机制可以帮助我们很容易实现读写分离方案,并支持灾难恢复(服务器断电)等意外情况下的数据安全. 在老版本(1.6)中,Mongo提供了两种方式的复制:master-slave及replica pair模式(注:mongodb最新支持的replset复制集方式可看成是pair的升级版,

ifconfig源码分析之与内核交互数据

<ifconfig源码分析之与内核交互数据>本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝.转载,转载时请保持文档的完整性.参考资料:<Linux设备驱动程序 第三版>,scull源码,Linux内核源码来源:http://blog.csdn.net/rosetta/article/details/7563615 ifconifg是Linux提供的一个操作网络接口的应用层程序,虽然和设备驱动编写没什么联系,但分析它的部分核心代码有助于理解应用层和内核层交

MongoDB源码分析——mongod数据查询操作

源码版本为MongoDB 2.6分支 Edit mongod数据查询操作 在mongod的初始化过程中说过,服务端接收到客户端消息后调用MyMessageHandler::process函数处理消息. class MyMessageHandler : public MessageHandler { public: ... virtual void process( Message& m , AbstractMessagingPort* port , LastError * le) { while

MongoDB源码分析

http://www.cnblogs.com/daizhj/category/260889.html http://blog.csdn.net/yhjj0108/article/details/8252005

YII框架源码分析(百度PHP大牛创作-原版-无广告无水印)

                        YII 框架源码分析             百度联盟事业部--黄银锋   目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 2.2.YiiBase 静态类 5 2.3.组件 6 2.4.模块 9 2.5 .App 应用   10 2.6 .WebApp 应用   11 3.系统组件 13 3.1.日志路由组件  13 3.2.Url 管理组件  15 3.3.异常

Mongodb源码安装for centos6

本文源链接地址:https:www.93bok.com Mongodb简介 MongoDB是一个高性能,开源,无模式的文档型数据库,是当前NoSql数据库中比较热门的一种.MongoDB 是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.它在许多场景下可用于替代传统的关系型数据库或键/值存储方式.它是由C++语言编写的一个基于分布式文件存储的开源数据库系统,它的目的在于为WEB应用提供可扩展的高性能数据存储解决方案.它支持的数据结构非常松散,会将数据存

Backbone.js源码分析(珍藏版)

源码分析珍藏,方便下次阅读! // Backbone.js 0.9.2 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Backbone may be freely distributed under the MIT license. // For all details and documentation: // http://backbonejs.org (function () { // 创建一个全局对象, 在浏览器中表示为w

Spring Boot 揭秘与实战 源码分析 - 开箱即用,内藏玄机

文章目录 1. 开箱即用,内藏玄机 2. 总结 3. 源代码 Spring Boot提供了很多"开箱即用"的依赖模块,那么,Spring Boot 如何巧妙的做到开箱即用,自动配置的呢? 开箱即用,内藏玄机 Spring Boot提供了很多"开箱即用"的依赖模块,都是以spring-boot-starter-xx作为命名的.例如,之前提到的 spring-boot-starter-redis.spring-boot-starter-data-mongodb.spri

Backbone.js 0.9.2 源码分析收藏

Backbone 为复杂Javascript应用程序提供模型(models).集合(collections).视图(views)的结构.其中模型用于绑定键值数据和自定义事件:集合附有可枚举函数的丰富API: 视图可以声明事件处理函数,并通过RESRful JSON接口连接到应用程序. 源码分析转之网上它人的备注,特收藏一下,以免方便阅读. // Backbone.js 0.9.2 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Ba