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函数中,感兴趣的可以自己去看一下。

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

  1. MongoDB源码分析——mongo主程序入口分析

    Edit   源码版本为MongoDB 2.6分支 mongo主程序入口分析 mongo是MongoDB提供的一个执行JavaScript脚本的客户端工具,可以用来和服务端交互,2.6版本的Mongo ...

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

    <ifconfig源码分析之与内核交互数据>本文档的Copyleft归rosetta所有,使用GPL发布,可以自由拷贝.转载,转载时请保持文档的完整性.参考资料:<Linux设备驱动 ...

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

    源码版本为MongoDB 2.6分支 Edit mongod数据查询操作 在mongod的初始化过程中说过,服务端接收到客户端消息后调用MyMessageHandler::process函数处理消息. ...

  4. MongoDB源码分析——mongod程序源码入口分析

    Edit 说明:第一次写笔记,之前都是看别人写的,觉得很简单,开始写了之后才发现真的很难,不知道该怎么分析,这篇文章也参考了很多前辈对MongoDB源码的分析,也有一些自己的理解,后续将会继续分析其他 ...

  5. mongodb 数据块迁移的源码分析

    1. 简介 上一篇我们聊到了mongodb数据块的基本概念,和数据块迁移的主要流程,这篇文章我们聊聊源码实现部分. 2. 迁移序列图 数据块迁移的请求是从配置服务器(config server)发给( ...

  6. ABP源码分析二十九:ABP.MongoDb

    这个Module通过建立一个MongoDbRepositoryBase<TEntity> 基类,封装了对MongoDb数据库的操作. 这个module通过引用MongoDB.Driver, ...

  7. Fresco 源码分析(二) Fresco客户端与服务端交互(3) 前后台打通

    4.2.1.2.4 PipelineDraweeControllerBuilder.obtainController()源码分析 续 上节中我们提到两个核心的步骤 obtainDataSourceSu ...

  8. Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题

    4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...

  9. JavaScript 模块化及 SeaJs 源码分析

    网页的结构越来越复杂,简直可以看做一个简单APP,如果还像以前那样把所有的代码都放到一个文件里面会有一些问题: 全局变量互相影响 JavaScript文件变大,影响加载速度 结构混乱.很难维护 和后端 ...

随机推荐

  1. HW4.44

    public class Solution { public static void main(String[] args) { double randX; double randY; int hit ...

  2. HDOJ-ACM1071(JAVA) 定积分

    这道题做起来有点无奈,定积分已经忘得差不多了~还可恶的去搜索了抛物线的解析式的求法~哈哈 不过求出来的结果不对...等有时间再去研究这个数学问题吧 - - 以下是JAVA实现: import java ...

  3. 如何把.rar文件隐藏在一个图片内

    首先假设我们要隐藏的.rar文件叫a.rar,图片叫a.jpg.先把他俩放到同一个目录下,然后通过“cmd”进入windows命令行,进入目标目录下,使用以下命令进行隐藏: copy/B  a.jpg ...

  4. 库不存在的排查方法:ImportError: No module named selenium2Library

    解决办法: 把selenium2Library改成Selenium2Library   安装下面四个: python-2.7.13.amd64.msi robotframework-ride-1.5. ...

  5. java 实现视频转换通用工具类:获取视频元数据信息(一)

    java 做视频转换主要用到开源的ffmpeg或者mencoder,还要有MP4Box. 注:由于平时都没有时间写博客,所以思路我就不写了,有问题问我,不一定马上回复. 详细介绍: ffmpeg:ht ...

  6. Derby使用2—C/S模式

    零.回顾 这部分先来回顾一下上一篇博客中的主要内容.上一篇博客中主要简单介绍了Derby数据的历史,特点,安装以及使用的两种模式.这篇文章主要介绍这两种模式中的一种模式 一.启动服务端程序 第一部分主 ...

  7. [Javascript] Drawing Styles on HTML5 Canvas

    window.onload = function() { var canvas = document.getElementById("canvas"), context = can ...

  8. 【Linq递归查找系列】

    Linq递归查找: public IEnumerable<MenuInfo> GetTree(int id, IEnumerable<MenuInfo> lst) { var ...

  9. Linux 汇编语言开发指南

    http://www.ibm.com/developerworks/cn/linux/l-assembly/

  10. [原理][源代码解析]spring中@Transactional,Propagation.SUPPORTS,以及 Hibernate Session,以及jdbc Connection关系---转载

    问题: 一. 1. Spring 如何处理propagation=Propagation.SUPPORTS? 2. Spring 何时生成HibernateSession ? 3. propagati ...