精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

MongoDB源碼分析--Command體系架構

運維 數據庫運維 其他數據庫 MongoDB
本文是用一篇專門介紹MongoDB中Command的體系架構的文章,并用例子來介紹mongod是如何將Command引入其中的。

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多個類中,按其使用場景可以為分如下幾類,分別是:

  1. dbcommand.cpp:一般數據庫指令,如數據庫,索引的創建,重建,打開/關閉等
  2. dbcommands_admin.cpp:管理指令,如CleanCmd,JournalLatencyTestCmd,ValidateCmd,FSyncCommand
  3. dbcommands_generic.cpp:常用指令,ListCommandsCmd,LogRotateCmd,PingCommand,CmdSet,CmdGet等
  4. replset_commands.cpp:復制集指令,CmdReplSetTest,CmdReplSetGetStatus,CmdReplSetReconfig等
  5. security_commands.cpp:安全指令,CmdGetNonce,CmdLogout,CmdAuthenticate

     

     

  1. commands_admin.cpp:shard管理操作,因其位于mongos項目,這里暫不介紹
  2. commands_public.cpp:shard公用操作,因其位于mongos項目,這里暫不介紹

下面是相關類圖:

-----------------------------分割線--------------------------------

-----------------------------分割線--------------------------------

 -----------------------------分割線--------------------------------

-----------------------------分割線--------------------------------

#p#

首先我們看一下在Command的基類,其用于定義子類要實現的方法及屬性,自身也實現了一些通用方法,比如htmlHelp(用于以html方法顯示該command的幫助信息),構造方法,findCommand(查詢命令)等,其聲明如下:

  1. //commands.h  
  2. class Command {  
  3. public:  
  4.    //執行當前Command時所使用的鎖類型  
  5.    enum LockType { READ = -1/*讀*/ , NONE = 0 /*無鎖*/, WRITE = 1 /*寫*/};  
  6.  
  7.    const string name;  
  8.  
  9.    /* 運行指定的命令,需要子類實現  
  10.    fromRepl - command is being invoked as part of replication syncing.  In this situation you  
  11.    normally do not want to log the command to the local oplog.  
  12.  
  13.    如執行成功返回true,否則為false, errmsg記錄錯誤信息  
  14.    */ 
  15.    virtual bool run(const string& db, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool fromRepl) = 0;  
  16.  
  17.    /*  
  18.    note: logTheTop() MUST be false if READ  
  19.    if NONE, can't use Client::Context setup  
  20.    use with caution  
  21.    */ 
  22.    virtual LockType locktype() const = 0;  
  23.  
  24.    /* 是否有管理特權才可運行該命令 has privileges to run this command. */ 
  25.    virtual bool adminOnly() const {  
  26.       return false;  
  27.    }  
  28.    //html格式的幫助信息  
  29.    void htmlHelp(stringstream&) const;  
  30.  
  31.    /* 與adminOnly相似,但更嚴格: 要么被驗證,要么只運行在本地接口(local interface)  
  32.    注:當本屬性為true時,adminOnly()也必須為true.  
  33.    */ 
  34.    virtual bool localHostOnlyIfNoAuth(const BSONObj& cmdObj) { return false; }  
  35.  
  36.    /* 如果replication pair 的slaves可以運行命令的,則返回true  
  37.    (the command directly from a client -- if fromRepl, always allowed).  
  38.    */ 
  39.    virtual bool slaveOk() const = 0;  
  40.  
  41.    /* 通過在查詢命令中打開 'slaveok'選項,客戶端強制在一個slave上運行一個命令時,返回true.  
  42.    */ 
  43.    virtual bool slaveOverrideOk() {  
  44.       return false;  
  45.    }  
  46.  
  47.    /* Override and return true to if true,log the operation (logOp()) to the replication log.  
  48.    (not done if fromRepl of course)  
  49.  
  50.    Note if run() returns false, we do NOT log.  
  51.    */ 
  52.    virtual bool logTheOp() { return false; }  
  53.  
  54.    virtual void help( stringstream& help ) const;  
  55.  
  56.    /* Return true if authentication and security applies to the commands.  Some commands  
  57.    (e.g., getnonce, authenticate) can be done by anyone even unauthorized.  
  58.    */ 
  59.    virtual bool requiresAuth() { return true; }  
  60.  
  61.    /** @param webUI:在web上暴露當前command,形如 localhost:28017/<name>  
  62.    @param oldName: 舊選項,表示當前command的舊(已棄用)名稱  
  63.    */ 
  64.    Command(const char *_name, bool webUI = falseconst char *oldName = 0);  
  65.  
  66.    virtual ~Command() {}  
  67.  
  68. protected:  
  69.    BSONObj getQuery( const BSONObj& cmdObj ) {  
  70.      if ( cmdObj["query"].type() == Object )  
  71.         return cmdObj["query"].embeddedObject();  
  72.      if ( cmdObj["q"].type() == Object )  
  73.         return cmdObj["q"].embeddedObject();  
  74.      return BSONObj();  
  75.    }  
  76.  
  77.    static void logIfSlow( const Timer& cmdTimer,  const string& msg);  
  78.    //command map,其包含系統實現的所有command對象,以便findCommand查詢時使用  
  79.    //注意也包含該command的舊名稱(構造方法中的oldName參數)所對應的對象,  
  80.    static map<string,Command*> * _commands;  
  81.    //與上面形同,但不含舊名稱的command map  
  82.    static map<string,Command*> * _commandsByBestName;  
  83.    //將web類型的command放到該map中  
  84.    static map<string,Command*> * _webCommands;  
  85.  
  86. public:  
  87.    static const map<string,Command*>* commandsByBestName() { return _commandsByBestName; }  
  88.    static const map<string,Command*>* webCommands() { return _webCommands; }  
  89.    /** @return 返回是否找到或已執行command */ 
  90.    static bool runAgainstRegistered(const char *ns, BSONObj& jsobj, BSONObjBuilder& anObjBuilder);  
  91.    static LockType locktype( const string& name );  
  92.    //根據命令名稱在集合中找到相應Command對象  
  93.    static Command * findCommand( const string& name );  
  94. }; 

Command基類中提供了幾個map<string,Command*>類型的集合map,用于將系統實現的Command進行收集,以便后面findCommand進行便歷查詢時使用。如下:

  1. //commands.cpp  
  2. Command* Command::findCommand( const string& name ) {  
  3.    //從_commands map中找到指定name的Command對象  
  4.    map<string,Command*>::iterator i = _commands->find( name );  
  5.    if ( i == _commands->end() )//如果已到結尾,表示未找到  
  6.       return 0;  
  7.    return i->second;//返回Command對象  
  8. }  

看到上面代碼中的_commands大家可能要問,該map是如何初始化并將系統實現的各個Command注冊到其中呢?答案就在Command的構造方法中,如下:

  1. //command.cpp  
  2. Command::Command(const char *_name, bool web, const char *oldName) : name(_name) {  
  3.    // register ourself.  
  4.    //如為空(系統剛啟動時)則實例化_commands  
  5.    if ( _commands == 0 )  
  6.       _commands = new map<string,Command*>;  
  7.    //如為空(系統剛啟動時)則實例化_commandsByBestName  
  8.    if( _commandsByBestName == 0 )  
  9.       _commandsByBestName = new map<string,Command*>;  
  10.    Command*& c = (*_commands)[name];//獲取指定名稱的command對象  
  11.    if ( c )//如有,表示之前已注冊了該command  
  12.       log() << "warning: 2 commands with name: " << _name << endl;  
  13.    //將當前command(this)賦值到map中相應name的command上  
  14.    c = this;  
  15.    //綁定到_commandsByBestName中的相應name上  
  16.    (*_commandsByBestName)[name] = this;  
  17.    //如果命令支持web方式  
  18.    if( web ) {  
  19.       //如為空(系統剛啟動時)則實例化_webCommands  
  20.       if( _webCommands == 0 )  
  21.          _webCommands = new map<string,Command*>;  
  22.       //綁定到_webCommands中的相應name上  
  23.       (*_webCommands)[name] = this;  
  24.    }  
  25.    //如有舊名稱,則也綁到_commands的oldName所指向的command  
  26.    if( oldName )  
  27.       (*_commands)[oldName] = this;  
  28. }  

有了這些還不夠,我們還要從90多個command子類中找出一個來實際分析其實現的方式,這里以最經常使用的count(獲取指定條件的記錄數)來分析其向map中注冊command的流程,參見下面代碼段:

  1. //dbcommands.cpp  
  2. /* select count(*) */ 
  3. class CmdCount : public Command {  
  4. public:  
  5.    virtual LockType locktype() const { return READ; }  
  6.    //調用基類的構造方法  
  7.    CmdCount() : Command("count") { }  
  8.  
  9.    virtual bool logTheOp() {  
  10.       return false;  
  11.    }  
  12.    virtual bool slaveOk() const {  
  13.       // ok on --slave setups, not ok for nonmaster of a repl pair (unless override)  
  14.       return replSettings.slave == SimpleSlave;  
  15.    }  
  16.    virtual bool slaveOverrideOk() {  
  17.       return true;  
  18.    }  
  19.    virtual bool adminOnly() const {  
  20.       return false;  
  21.    }  
  22.    virtual void help( stringstream& help ) const { help << "count objects in collection"; }  
  23.    virtual bool run(const string& dbname, BSONObj& cmdObj, string& errmsg, BSONObjBuilder& result, bool) {  
  24.       string ns = dbname + '.' + cmdObj.firstElement().valuestr();  
  25.       string err;  
  26.       long long n = runCount(ns.c_str(), cmdObj, err);//執行查詢  
  27.       long long nn = n;  
  28.       bool ok = true;  
  29.       if ( n == -1 ) {  
  30.          nn = 0;  
  31.          result.appendBool( "missing" , true );  
  32.       }  
  33.       else if ( n < 0 ) {  
  34.          nn = 0;  
  35.          ok = false;  
  36.          if ( !err.empty() )  
  37.             errmsg = err;  
  38.       }  
  39.       result.append("n", (double) nn);  
  40.       return ok;  
  41.    }  
  42. }  cmdCount; 

上面的CmdCount類即是在命令行模式下使用count指令時對應的代碼塊,其自身的構造函數就直接調用了基類(Command)的構造方法。但這里只是定義了還不夠,還需要一個定義類實例代碼(用于啟動構造函數),而這個任務就交給了該類定義的代碼結尾處的下面代碼來實現了:

  1. } cmdCount; 

可以看到,這里使用的是在類聲明后定義對象的方式來執行構造方法(這時并未使用new實例化方式來創建對象指針),進而注冊該command到map。當然繼承自Command的子類必須要實現其中的run()方法,因為只有它是具體command要執行的具體邏輯(可參見上面CmdCount的具體實現)。

到這里只能說mongod在系統啟動到實始化了相應的Command集合map信息,但mongod是如何將client發來的操作請求進行轉換并進而執行相應的command指令的呢?我們接下來繼續分析。

之前看過我的這篇文章的朋友可能還有印象,在mongod啟動之后,會循環偵聽指向端口上的用戶(client)請求,這些請求在mongod中被改裝成了message在各個功能類中傳遞。當用戶發送一個count指令操作時,其會在query.cpp中執行下面方法(以count查詢指令的執行流程為例來進行分析):

  1. //query.cpp  
  2. const char *runQuery(Message& m, QueryMessage& q, CurOp& curop, Message &result) {  
  3.    StringBuilder& ss = curop.debug().str;  
  4.    //構造ParsedQuery查詢對象,該對象包括查詢記錄數字,以及記錄跳轉偏移量等信息,  
  5.  
  6.    //這些值會在訪問磁盤查詢時使用,用法參見:query.cpp 662行的virtual void _init()方法  
  7.    shared_ptr<ParsedQuery> pq_shared( new ParsedQuery(q) );  
  8.    ParsedQuery& pq( *pq_shared );  
  9.    ......  
  10.    //對查詢命令判斷,指令形如abc.$cmd.findOne( { ismaster:1 } )  
  11.    if ( pq.couldBeCommand() ) {//_ns中包括$cmd字符串  
  12.   BufBuilder bb;  
  13.   bb.skip(sizeof(QueryResult));  
  14.   BSONObjBuilder cmdResBuf;  
  15.   //對查詢權限判斷,并執行相應查詢指令  
  16.   if ( runCommands(ns, jsobj, curop, bb, cmdResBuf, false, queryOptions) ) {  
  17.      ss << " command: ";  
  18.      jsobj.toString( ss );  
  19.      curop.markCommand();  
  20.      auto_ptr< QueryResult > qr;  
  21.      qr.reset( (QueryResult *) bb.buf() );  
  22.      bb.decouple();  
  23.      qr->setResultFlagsToOk();  
  24.      qr->len = bb.len();  
  25.      ss << " reslen:" << bb.len();  
  26.      qr->setOperation(opReply);  
  27.      qr->cursorId = 0;  
  28.      qr->startingFrom = 0;  
  29.      qr->nReturned = 1;  
  30.      result.setData( qr.release(), true );//設置返回結果  
  31.   }  
  32.   else {  
  33.      uasserted(13530, "bad or malformed command request?");  
  34.   }  
  35.   return 0;  
  36.    }  
  37. .....  

上面代碼對傳遞來的查詢消息QueryMessage進行分析之后,如果發現其為command時,執行runCommands方法:

  1. //query.cpp     
  2. bool runCommands(const char *ns, BSONObj& jsobj, CurOp& curop, BufBuilder &b, BSONObjBuilder& anObjBuilder, bool fromRepl, int queryOptions) {  
  3.    try {  
  4.       return _runCommands(ns, jsobj, b, anObjBuilder, fromRepl, queryOptions);  
  5.    }  
  6.    catch ( AssertionException& e ) {  
  7.       e.getInfo().append( anObjBuilder , "assertion" , "assertionCode" );  
  8.    }  
  9.    curop.debug().str << " assertion ";  
  10.    anObjBuilder.append("errmsg""db assertion failure");  
  11.    anObjBuilder.append("ok", 0.0);  
  12.    BSONObj x = anObjBuilder.done();  
  13.    b.appendBuf((void*) x.objdata(), x.objsize());  
  14.    return true;  
  15. }  

接著其會執行dbcommands.cpp中的_runCommands()方法

  1. //dbcommands.cpp  
  2.   bool _runCommands(const char *ns, BSONObj& _cmdobj, BufBuilder &b, BSONObjBuilder& anObjBuilder, bool fromRepl, int queryOptions) {  
  3.   cc().curop()->ensureStarted();  
  4.   string dbname = nsToDatabase( ns );  
  5.  
  6.   if( logLevel >= 1 )  
  7.      log() << "run command " << ns << ' ' << _cmdobj << endl;  
  8.  
  9.   const char *p = strchr(ns, '.');  
  10.   if ( !p ) return false;  
  11.   //再次進行cmd判斷,以確定是command  
  12.   if ( strcmp(p, ".$cmd") != 0 ) return false;  
  13.  
  14.   BSONObj jsobj;  
  15.   {  
  16.      BSONElement e = _cmdobj.firstElement();  
  17.      if ( e.type() == Object && string("query") == e.fieldName() ) {  
  18.         jsobj = e.embeddedObject();  
  19.      }  
  20.      else {  
  21.         jsobj = _cmdobj;  
  22.      }  
  23.   }  
  24.  
  25.   Client& client = cc();  
  26.   bool ok = false;  
  27.  
  28.   BSONElement e = jsobj.firstElement();  
  29.   //根據command名稱從map中找出相應的command對象  
  30.   Command * c = e.type() ? Command::findCommand( e.fieldName() ) : 0;  
  31.  
  32.   if ( c ) {  
  33.      //執行該對象  
  34.      ok = execCommand( c , client , queryOptions , ns , jsobj , anObjBuilder , fromRepl );  
  35.   }  
  36.   else {  
  37.      anObjBuilder.append("errmsg", str::stream() << "no such cmd: " << e.fieldName() );  
  38.      anObjBuilder.append("bad cmd" , _cmdobj );  
  39.   }  
  40.  
  41.   // switch to bool, but wait a bit longer before switching?  
  42.   // anObjBuilder.append("ok", ok);  
  43.   anObjBuilder.append("ok", ok?1.0:0.0);  
  44.   BSONObj x = anObjBuilder.done();  
  45.   b.appendBuf((void*) x.objdata(), x.objsize());  
  46.  
  47.   return true;  

上面代碼主要是從map中找出相應的command對象,并將該對象及操作命令參數和client(用于獲取其中的認證信息,以確定其執行權限)作為參數,來調用 execCommand方法:

  1. //dbcommands.cpp  
  2. bool execCommand( Command * c ,  
  3.   Client& client , int queryOptions ,  
  4.   const char *cmdns, BSONObj& cmdObj ,  
  5.   BSONObjBuilder& result /*返回command執行結果*/,  
  6.   bool fromRepl ) {  
  7.  
  8.      string dbname = nsToDatabase( cmdns );  
  9.  
  10.      AuthenticationInfo *ai = client.getAuthenticationInfo();  
  11.      
  12.      if( c->adminOnly() /*如果需要有管理特權開可運行*/ 
  13.      && c->localHostOnlyIfNoAuth( cmdObj ) /*要么被驗證,要么只運行在本地接口*/ 
  14.      && noauth && !ai->isLocalHost ) {//未認證 且 不是在本地運行  
  15.         result.append( "errmsg" ,  
  16.         "unauthorized: this command must run from localhost when running db without auth" );  
  17.         log() << "command denied: " << cmdObj.toString() << endl;  
  18.         return false;  
  19.      }  
  20.  
  21.     if ( c->adminOnly() && ! fromRepl && dbname != "admin" ) {  
  22.        result.append( "errmsg" ,  "access denied; use admin db" );  
  23.        log() << "command denied: " << cmdObj.toString() << endl;  
  24.        return false;  
  25.     }  
  26.  
  27.    if ( cmdObj["help"].trueValue() ) {  
  28.       stringstream ss;  
  29.       ss << "help for: " << c->name << " ";  
  30.       c->help( ss );  
  31.       result.append( "help" , ss.str() );  
  32.       result.append( "lockType" , c->locktype() );  
  33.       return true;  
  34.    }  
  35.  
  36.    bool canRunHere =  
  37.    isMaster( dbname.c_str() ) /*如為master庫*/||  
  38.    c->slaveOk() /*如果replication pair 的slaves可以運行命令*/||  
  39.    ( c->slaveOverrideOk() && ( queryOptions & QueryOption_SlaveOk ) ) ||  
  40.    fromRepl;  
  41.  
  42.    if ( ! canRunHere ) {  
  43.       result.append( "errmsg" , "not master" );  
  44.       return false;  
  45.    }  
  46.  
  47.    if ( c->adminOnly() )  
  48.       log( 2 ) << "command: " << cmdObj << endl;  
  49.  
  50.    //如當前command無須鎖時  
  51.    if ( c->locktype() == Command::NONE ) {  
  52.       // we also trust that this won't crash  
  53.       string errmsg;  
  54.       //運行當前command  
  55.       int ok = c->run( dbname , cmdObj , errmsg , result , fromRepl );  
  56.       if ( ! ok )  
  57.          result.append( "errmsg" , errmsg );  
  58.       return ok;  
  59.    }  
  60.    //判斷執行當前command是否需要'寫鎖'(每個command子類都有該屬性),枚舉定義如下(command.h):  
  61.    //enum LockType { READ = -1/*讀*/ , NONE = 0 /*無鎖*/, WRITE = 1 /*寫*/};  
  62.    bool needWriteLock = c->locktype() == Command::WRITE;  
  63.  
  64.    if ( ! needWriteLock ) {  
  65.       assert( ! c->logTheOp() );  
  66.    }  
  67.  
  68.    mongolock lk( needWriteLock );//聲明鎖對象  
  69.    Client::Context ctx( dbname , dbpath , &lk , c->requiresAuth() );  
  70.  
  71.    try {  
  72.       string errmsg;  
  73.       //運行當前command(本文中提到的count命令)  
  74.       if ( ! c->run(dbname, cmdObj, errmsg, result, fromRepl ) ) {  
  75.          result.append( "errmsg" , errmsg );  
  76.          return false;  
  77.       }  
  78.    }  
  79.    catch ( DBException& e ) {  
  80.       stringstream ss;  
  81.       ss << "exception: " << e.what();  
  82.       result.append( "errmsg" , ss.str() );  
  83.       result.append( "code" , e.getCode() );  
  84.       return false;  
  85.    }  
  86.  
  87.    if ( c->logTheOp() && ! fromRepl ) {  
  88.       logOp("c", cmdns, cmdObj);  
  89.    }  
  90.  
  91.    return true;  

到這里,流程基本就執行完畢了,之后它會將結果傳給result(其傳參為引用類型,即:"& result"方式).

#p#

***用一張時間序來大體回顧一下這***程:

好了,今天的內容到這里就告一段落了。

參考鏈接:
http://www.mongodb.org/display/DOCS/Commands
http://www.10gen.com/reference

原文鏈接:http://www.cnblogs.com/daizhj/archive/2011/04/29/mongos_command_source_code.html

【編輯推薦】

  1. Mongodb源碼分析--內存文件映射(MMAP)
  2. 走進MongoDB的世界 展開MongoDB的學習之旅
  3. 淺析Mongodb源碼之游標Cursor
  4. 野心勃勃的NoSQL新貴 MongoDB應用實戰
  5. MongoDB與CouchDB全方位對比

 

責任編輯:艾婧 來源: 博客園
相關推薦

2011-05-26 10:05:48

MongoDB

2011-05-26 16:18:51

Mongodb

2019-07-01 12:55:05

安全體系架構網絡安全企業安全

2011-04-25 17:15:39

MongodbMMAP

2016-11-25 13:14:50

Flume架構源碼

2016-11-29 09:38:06

Flume架構核心組件

2016-11-25 13:26:50

Flume架構源碼

2009-12-25 16:24:14

防火墻三大體系架構前景分析

2019-10-16 16:33:41

Docker架構語言

2022-03-18 15:55:15

鴻蒙操作系統架構

2016-09-04 14:00:31

Spark

2023-06-02 08:16:14

MySQL體系架構

2017-07-26 09:41:28

MyCATSQLMongoDB

2011-09-16 14:43:52

MongoDB

2021-02-19 06:56:33

架構協程應用

2016-11-29 16:59:46

Flume架構源碼

2009-12-23 10:13:20

WPF體系架構

2017-06-27 14:05:19

2021-01-06 10:09:38

MySQL

2014-08-26 11:11:57

AsyncHttpCl源碼分析
點贊
收藏

51CTO技術棧公眾號

av中文字幕亚洲| 97视频一区| 91亚洲精华国产精华精华液| 日本久久精品视频| 日韩av电影免费在线| 天天爽天天爽天天爽| av有码在线观看| 91视频91自| 国产日韩在线精品av| 欧美图片一区二区| 欧美日韩视频免费看| 亚洲一二三区在线观看| 神马影院我不卡午夜| 亚洲精品久久久久avwww潮水| 久久不射2019中文字幕| 久久影视免费观看 | 秋霞午夜av一区二区三区| 欧美日韩国产成人在线观看| 泷泽萝拉在线播放| 亚洲一区二区三区四区电影| 国产精品久久久久婷婷二区次| 91高清视频在线免费观看| 国产调教在线观看| 蜜桃久久久久| 日韩免费高清av| 少妇一级淫免费播放| 男人av在线播放| 一区二区三区在线观看网站| 亚洲欧美日韩不卡一区二区三区| 午夜福利视频一区二区| 国产精品1区2区| 国产精品免费久久久久久| avhd101老司机| 日韩有码av| 亚洲第一偷拍网| 黄色aaaaaa| 亚洲免费资源| 欧美日韩免费在线视频| www.四虎成人| 免费成人在线电影| 五月婷婷另类国产| 亚洲天堂第一区| 黄色免费在线观看| 国产成人综合视频| 91久久久久久国产精品| 亚洲一区 中文字幕| 天堂在线亚洲视频| 亚洲视频网站在线观看| 少妇精品一区二区三区| 蜜桃精品wwwmitaows| 亚洲精品视频免费| 自拍偷拍中文字幕| 中文有码一区| 亚洲性无码av在线| 成人黄色免费网址| 日韩av在线中文字幕| 色诱女教师一区二区三区| 国产精品二区视频| 中文一区二区三区四区| 欧美精品一区二区三区在线播放| 蜜臀av粉嫩av懂色av| 久久悠悠精品综合网| 日韩电视剧免费观看网站| 欧美丰满少妇人妻精品| 欧美日韩xxxx| 中文字幕国产日韩| 婷婷伊人五月天| 制服丝袜综合日韩欧美| 欧美 日韩 中文字幕| 成人网在线免费视频| 国产精品二区三区| 欧美男人的天堂| 91精品国产91久久久久青草| 99久久精品国产一区二区成人| 久久精品99国产国产精| 亚洲精品视频播放| 亚洲AV无码成人精品区明星换面 | 久久人人爽人人人人片| 综合综合综合综合综合网| 亚洲最新av在线| 久草视频手机在线| 一区二区三区精品视频在线观看| 日韩免费在线免费观看| 在线观看一二三区| 国产福利91精品一区| 好吊色欧美一区二区三区视频| 国产永久免费高清在线观看视频| **性色生活片久久毛片| 精品国产一区二区三区免费 | 久久大大胆人体| 国产一级一片免费播放| 国产精品腿扒开做爽爽爽挤奶网站| 国产999视频| 国产女人高潮的av毛片| 91最新地址在线播放| 亚洲一区高清| 你懂得在线网址| 自拍av一区二区三区| 成熟丰满熟妇高潮xxxxx视频| 91tv亚洲精品香蕉国产一区| 欧美r级电影在线观看| 中文字幕第100页| 538任你躁精品视频网免费| 亚洲女同性videos| 九九视频在线观看| 免费人成精品欧美精品| 精品久久蜜桃| а√中文在线8| 91极品美女在线| 日本一区二区在线观看视频| 日韩精品首页| 奇米四色中文综合久久| 国产av精国产传媒| 国产精品欧美极品| 91av资源网| 91久久偷偷做嫩草影院电| 中文字幕久精品免费视频| 伊人久久综合视频| 国产不卡一区视频| 一区二区三区四区五区精品| 最新中文字幕在线播放| 岛国av一区二区| 免费观看黄网站| 日韩欧美在线中字| 日本国产欧美一区二区三区| 日韩在线观看视频一区| 亚洲精品乱码久久久久| 九九精品久久久| 欧美综合一区| 国产成人高清激情视频在线观看| 青青草免费观看视频| 国产成人免费在线观看不卡| 中文字幕不卡每日更新1区2区| 欧美香蕉视频| 国产视频亚洲视频| 日韩在线视频免费播放| www.一区二区| 免费无码毛片一区二三区| 精品视频在线一区| 久久精品亚洲精品| a级片在线播放| 亚洲柠檬福利资源导航| 做a视频在线观看| 我不卡神马影院| 成人精品福利视频| 国产原厂视频在线观看| 日韩一区二区电影在线| 国产69视频在线观看| 亚洲欧美一区在线| 亚洲a∨日韩av高清在线观看| 蜜桃视频在线观看免费视频网站www| 欧美伊人久久大香线蕉综合69| 国产真实乱人偷精品人妻| 久久九九精品| 神马影院我不卡| 成人精品在线| 欧美激情精品久久久久久黑人| 国产成人毛毛毛片| 亚洲一级二级三级在线免费观看| 娇妻高潮浓精白浆xxⅹ| 亚洲视频1区| 日本一区二区久久精品| 国产欧美自拍| 色综合老司机第九色激情 | 国产成人综合亚洲网站| 欧美午夜小视频| 99只有精品| 日韩亚洲国产中文字幕| 精品二区在线观看| 午夜伦理一区二区| 人妻av无码一区二区三区| 另类小说综合欧美亚洲| 日本一级黄视频| 欧洲亚洲视频| 国产精品视频永久免费播放| 成年人网站在线| 日韩成人久久久| 国产情侣免费视频| 亚洲黄色av一区| 精品人妻一区二区三区香蕉 | 91精品综合久久久久久五月天| 18网站在线观看| 亚洲国内高清视频| 日韩久久久久久久久久| 一区二区三区高清不卡| 国产精品无码一区二区三| 免费久久99精品国产| 亚洲精品国产suv一区88| 国产99久久| 91久久国产自产拍夜夜嗨| 亚洲精品中文字幕| 久久天堂电影网| 欧美3p视频在线观看| 日韩一区二区精品在线观看| 波多野结衣午夜| 亚洲夂夂婷婷色拍ww47| 久久午夜精品视频| 99国产精品久久久久| 日本中文字幕观看| 国产亚洲一区在线| 小泽玛利亚av在线| 欧美精选视频在线观看| 国产亚洲一区二区三区在线播放| 国产黄色精品| 欧美在线免费看| 欧美午夜大胆人体| www.欧美免费| 国产片在线观看| 亚洲国产又黄又爽女人高潮的| 国产一区二区女内射| 欧美日在线观看| 免费在线观看av网址| 国产精品视频第一区| 日韩网站在线播放| 成人午夜av在线| 精产国品一区二区三区| 久久99热狠狠色一区二区| 国产91对白刺激露脸在线观看| 欧美另类综合| 在线视频一二三区| 成人在线免费小视频| 欧美黑人3p| 欧美a大片欧美片| 99在线首页视频| 亚洲综合伊人| 国产精品一区久久久| 蜜桃视频成人m3u8| 欧美在线免费视频| 黄在线观看免费网站ktv| 九九久久国产精品| 黄色网页在线免费观看| 视频在线一区二区| 午夜视频在线| 中文字幕精品久久| 中文字幕在线播放| 一区二区三区在线播放欧美| 免费在线高清av| 日韩麻豆第一页| 性xxxx18| 亚洲精品在线观看网站| 成人午夜免费福利| 亚洲精品动漫100p| 天天操天天干天天操| 亚洲精品美女在线观看| 午夜在线视频免费| 亚洲精品久久久久| 日本福利片高清在线观看| 精品亚洲aⅴ在线观看| 美州a亚洲一视本频v色道| 亚洲国产天堂网精品网站| 亚洲AV第二区国产精品| 日韩精品中文字幕视频在线| 国产剧情在线观看| 最近2019中文字幕mv免费看| 欧美jizz18hd性欧美| 久久天天躁日日躁| 午夜伦理在线视频| 性欧美xxxx视频在线观看| 欧美aa一级| 国产精品久久久久久久久男| 国内欧美日韩| 91免费在线观看网站| 草草视频在线一区二区| 精品免费视频123区| 国产91一区| 中文字幕免费高| 亚洲小说欧美另类社区| www一区二区www免费| 日本不卡中文字幕| 日韩精品xxx| 99久久精品免费观看| 蜜桃传媒一区二区亚洲| 中文字幕中文在线不卡住| 欧美国产在线看| 五月综合激情网| 亚洲午夜无码久久久久| 欧美一区二区观看视频| 午夜视频www| 久久久999国产| 激情黄产视频在线免费观看| 国产精品视频地址| 18国产精品| 日韩亚洲一区在线播放| 欧美网站在线| 国内自拍视频一区| 美女视频黄频大全不卡视频在线播放| 国产一二三在线视频| 一本久道久久综合狠狠爱| 亚洲成熟丰满熟妇高潮xxxxx| 免费看精品久久片| 中文字幕人妻一区| 中文字幕不卡在线播放| 妖精视频一区二区| 国产视频一区在线播放| 欧洲猛交xxxx乱大交3| 欧美性xxxx极品hd欧美风情| 91久久久久久久久久久久| 亚洲福利视频在线| 日韩av中文| 欧美在线视频在线播放完整版免费观看| 亚洲精品777| 欧美激情一区二区三区在线视频 | 在线亚洲午夜片av大片| 日本高清成人vr专区| 国产精品九九九| 久久电影在线| 青青草综合视频| 免费成人性网站| 精品人妻一区二区三区香蕉 | 男人添女人下面高潮视频| 久久se这里有精品| 在线国产视频一区| 天天色天天操综合| 不卡视频免费在线观看| 综合欧美国产视频二区| 欧美自拍电影| 久久99精品久久久久久秒播放器| 99视频精品全国免费| 别急慢慢来1978如如2| 99国产精品99久久久久久| 久草视频手机在线观看| 欧美一区二区视频在线观看2020 | 亚洲美女区一区| 在线免费看91| 亚洲精品在线不卡| 超碰资源在线| 国产丝袜不卡| 亚洲理伦在线| 国产精品久久久久久在线观看| 亚洲自拍与偷拍| www.激情五月.com| 欧美尺度大的性做爰视频| 四虎国产精品成人免费影视| 亚洲 国产 欧美一区| 日韩av网站在线观看| 真实乱视频国产免费观看| 91国偷自产一区二区开放时间 | 日本精品性网站在线观看| 久久久伦理片| 日本a在线免费观看| 成人黄色在线看| 精品人妻一区二区免费视频| 亚洲综合色自拍一区| 亚洲第一色网站| 久久久久久91| 欧美人与动xxxxz0oz| 精品一区二区中文字幕| 久久免费视频一区| a一级免费视频| 欧美日韩另类一区| 免费在线视频欧美| 亚洲一区二区三区视频| 欧美日韩a区| 亚洲熟女一区二区| 狠狠色香婷婷久久亚洲精品| 男人的天堂在线| 国产精品色婷婷视频| jiujiure精品视频播放| 又色又爽又黄视频| 一区二区三区中文免费| 欧美熟妇交换久久久久久分类| 国产91精品视频在线观看| 国产欧美高清视频在线| 奇米影视四色在线| 亚洲欧美另类图片小说| 亚洲欧美另类综合| 热re91久久精品国99热蜜臀| 成人在线亚洲| 久久久久久久久久影视| 欧美性xxxxx| 国产日产一区二区| 国新精品乱码一区二区三区18| 免费视频久久| 黄色录像免费观看| 精品国产三级a在线观看| 天堂av中文在线观看| 一本一生久久a久久精品综合蜜| 国产福利一区在线| 老熟妇仑乱一区二区av| 欧美va亚洲va| 在线观看特色大片免费视频| 亚洲精品乱码视频| 成人一区二区三区视频| 精品国产xxx| 毛片精品免费在线观看| 日韩精品欧美大片| 中文字幕第一页在线视频| 精品日韩中文字幕| 黄色av免费在线| 久热这里只精品99re8久| 久久91精品国产91久久小草| 日韩精品一区二区av| 中文字幕欧美亚洲| 久久综合社区| 日韩欧美国产片| 偷窥少妇高潮呻吟av久久免费| 欧美成年黄网站色视频| 欧美黑人xxxxx| 粉嫩av一区二区三区| 亚洲视频一区在线播放|