精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

欧美巨大另类极品videosbest | 婷婷激情四射网| 91九色综合| 亚洲欧洲成人自拍| 国产精品 日韩| 日韩电影在线观看一区二区| 久久看人人摘| 精品国产乱码久久久久久牛牛 | 国产精品乱码一区二区三区| 手机看片久久久| 国产精品99一区二区三| 亚洲电影免费观看高清完整版在线观看 | 日韩一级在线播放| 青青草成人在线观看| 欧美国产日韩一区| 中文字幕人妻一区二区三区在线视频| 在线欧美激情| 欧美日韩加勒比精品一区| 亚洲一区精彩视频| 网站黄在线观看| 麻豆免费精品视频| 97超级碰碰人国产在线观看| 一区二区三区在线播放视频| 伦理一区二区三区| 欧美日本乱大交xxxxx| 黄页免费在线观看视频| 日韩欧美小视频| 99视频在线观看一区三区| 成人免费在线视频网站| 美女又爽又黄免费视频| 欧美韩日精品| 最新国产精品亚洲| 黄色国产在线观看| av综合网站| 在线亚洲高清视频| 精品国产一区二区三区无码| 97电影在线看视频| 久久免费国产精品| 狠狠干一区二区| 国产黄色av片| 麻豆精品在线视频| 国产精品久久久久久久久久久不卡 | youjizz欧美| 欧美一区2区视频在线观看| 成人3d动漫一区二区三区| 美女的胸无遮挡在线观看| 亚洲综合一区二区| 一区二区三区四区免费观看| 91看片在线观看| 久久美女高清视频| 免费电影一区| 亚洲日本中文字幕在线| 成人黄色av网站在线| 99免费在线观看视频| 国产又大又黑又粗| 麻豆精品一区二区三区| 国产精品网址在线| 自拍偷拍第八页| 日韩国产精品久久久| 国产suv精品一区二区三区88区| 圆产精品久久久久久久久久久 | 波多野结衣家庭教师视频| free性欧美| 亚洲成人你懂的| 水蜜桃色314在线观看| 99热99re6国产在线播放| 亚洲丰满少妇videoshd| 国产毛片视频网站| 日本午夜大片a在线观看| 亚洲成精国产精品女| 丰满少妇久久久| 亚洲日本天堂| 欧美午夜在线观看| 777一区二区| 国产精品一级在线观看| 欧美一区二区播放| 蜜臀aⅴ国产精品久久久国产老师| 波多野结衣欧美| 亚洲国产精品中文| 日本成人免费视频| 99久久久国产精品美女| 久久99精品视频一区97| 国产一级特黄aaa大片| 亚洲一区自拍| 国产精品一区二区久久| 国产白浆在线观看| 成a人片国产精品| 欧洲av一区| 欧美一级二级三级区| 一区二区在线观看免费| 91九色丨porny丨国产jk| 亚洲女同av| 欧美美女视频在线观看| 日韩精品国产一区| 伊人成综合网伊人222| 中文字幕亚洲一区| 国产一级片免费| 日韩国产一区二| 91黄在线观看| 黄色av免费在线观看| 亚洲视频一二区| 久久免费视频3| 亚洲老司机网| 日韩精品视频免费专区在线播放| 免费黄色国产视频| 亚洲成人中文| 国产精品一区二区久久久久| 午夜精品久久久久久久91蜜桃| 久久久三级国产网站| 国产又大又长又粗又黄| 亚洲一级少妇| 日韩一级二级三级| 亚洲无人区码一码二码三码的含义| 欧美激情在线| 国产精品视频区| 免费a视频在线观看| 国产精品久久久久桃色tv| 91成人在线观看喷潮教学| 欧美综合影院| 亚洲天堂网站在线观看视频| 妺妺窝人体色www聚色窝仙踪| 青青草97国产精品免费观看| 精品国产乱码久久久久久108| 国产最新在线| 在线观看一区二区视频| 波多野结衣办公室双飞| 视频在线不卡免费观看| 国产ts一区二区| 蜜桃久久一区二区三区| 亚洲伦理在线精品| 浓精h攵女乱爱av| 亚洲综合小说图片| 久久久爽爽爽美女图片| 99久久国产热无码精品免费| 欧美国产综合色视频| 92看片淫黄大片一级| 风间由美性色一区二区三区四区| 久久久精品在线| 中文字幕在线观看高清| 国产午夜精品美女毛片视频| 缅甸午夜性猛交xxxx| 2020最新国产精品| 欧美xxxx做受欧美| 国产免费av电影| 国产精品久久国产精麻豆99网站 | 日本在线一级片| 日韩av高清在线观看| 免费国产一区二区| 日韩精品av| 日韩精品免费在线| 五月婷婷色丁香| 久久综合中文字幕| 日韩av资源在线| 精品国产乱码| 国产成人在线视频| 国产中文字幕在线| 欧美亚一区二区| 欧美乱大交做爰xxxⅹ小说| 男人的j进女人的j一区| 亚洲欧美日产图| 成人精品三级| 色婷婷久久一区二区| 97精品久久人人爽人人爽| 亚洲丝袜美腿综合| 日本黄色大片在线观看| 激情欧美丁香| 久热这里只精品99re8久 | 国产在线观看免费| 精品视频资源站| 在线观看亚洲网站| 国产iv一区二区三区| 欧洲精品视频在线| 国产精品网址| 日韩av黄色在线观看| 成人免费在线视频网| 在线成人免费视频| 国产在线视频99| 久久婷婷色综合| 成人性生生活性生交12| 久久久久美女| 国产欧美日韩一区| 国模一区二区| 久久这里有精品视频| 日韩一卡二卡在线| 91久久人澡人人添人人爽欧美| 男人天堂资源网| 成人毛片视频在线观看| 亚洲爆乳无码专区| 91精品国产自产在线观看永久∴| 国产精品日韩高清| 日韩电影网站| 久久99青青精品免费观看| 少妇一级淫片免费看| 欧美日韩一级黄| 国产一级一片免费播放| 国产欧美日韩视频一区二区| 丰满少妇中文字幕| 性高湖久久久久久久久| 正在播放91九色| 欧美韩一区二区| 国产精品中文字幕在线观看| 91豆花视频在线播放| 亚洲视屏在线播放| 成 人片 黄 色 大 片| 91福利视频久久久久| 免费在线一级片| 国产免费成人在线视频| 无码人妻丰满熟妇啪啪网站| 久久亚洲图片| 国产精品久久久久久久乖乖| 日韩精品欧美| 久久www免费人成精品| 国产精品亚洲四区在线观看| 国产精品69av| 成年人国产在线观看| 日韩在线中文视频| 毛片免费在线播放| 精品成人在线观看| 国产精品伊人久久| 在线一区二区三区四区五区| 日本一区二区欧美| 亚洲欧美日本在线| 精品人妻中文无码av在线| 26uuu亚洲综合色| ass极品水嫩小美女ass| 老司机精品视频导航| 日批视频在线免费看| 伊人久久综合| 在线观看污视频| 首页国产精品| 亚洲乱码国产乱码精品天美传媒| 亚洲a级精品| 国产综合色一区二区三区| 久久视频免费| 成人欧美一区二区三区在线| 国产精成人品2018| 国产成人免费av电影| 日韩激情电影免费看| 97在线视频精品| 91美女主播在线视频| 久久久亚洲成人| 精品一性一色一乱农村| 久久99亚洲热视| 中文字幕有码在线观看| 欧美成人合集magnet| 国产一二区在线观看| 久久精品电影一区二区| 日本在线观看网站| 中文字幕久久久| 超碰97在线免费观看| 一区三区二区视频| 国产区在线视频| 中文字幕9999| 日本在线观看网站| 麻豆成人在线看| 深夜国产在线播放| 欧美激情网友自拍| 成人黄色动漫| 国产91精品高潮白浆喷水| 亚洲十八**毛片| 国产精品高潮呻吟久久av野狼| 成人黄页网站视频| 91精品久久久久久久久不口人| 成人激情久久| 不卡一区二区三区视频| 国产在线播放精品| 欧美精品在线一区| 欧美综合一区| 亚洲成年人专区| 欧美色123| 精品国产免费av| 日日骚欧美日韩| 最新免费av网址| 懂色一区二区三区免费观看| 制服丝袜第一页在线观看| 久久色成人在线| 欧美午夜激情影院| 中文字幕亚洲欧美在线不卡| 成人信息集中地| 亚洲影院在线观看| 欧美国产成人精品一区二区三区| 欧美性色黄大片手机版| 99热这里只有精| 亚洲精品国产拍免费91在线| 成人网视频在线观看| 欧美乱妇高清无乱码| 在线手机中文字幕| 成人免费观看a| 红杏一区二区三区| 亚洲视频导航| 亚洲国产电影| av污在线观看| 成人精品小蝌蚪| 亚洲天堂岛国片| 亚洲图片一区二区| 中文字幕网址在线| 精品1区2区在线观看| h视频网站在线观看| 久久久久久久久91| 国产成人久久精品麻豆二区| 国产欧美一区二区视频| 成人精品久久| 免费一级特黄特色毛片久久看| 日本亚洲最大的色成网站www| 国产a√精品区二区三区四区| 国产视频一区在线观看| 久草中文在线视频| 欧美日韩免费一区二区三区| 国产99久一区二区三区a片| 精品中文视频在线| 四虎影院观看视频在线观看| 国产成人精品a视频一区www| julia中文字幕一区二区99在线| 日韩中文一区| 一本久久综合| 久久久久久无码精品人妻一区二区| 国产视频一区二区在线| 日操夜操天天操| 欧美一区二区三区在线观看视频| 精品欧美不卡一区二区在线观看 | 91精品国产综合久久香蕉的特点| 欧美日韩激情视频一区二区三区| 欧美激情一区二区久久久| 日韩午夜视频在线| 日韩精品成人一区二区在线观看| 日韩视频免费| 日本一级大毛片a一| 亚洲视频网在线直播| 一本一道精品欧美中文字幕| 亚洲乱码av中文一区二区| 麻豆av在线播放| 成人在线国产精品| 欧美国产美女| 浓精h攵女乱爱av| 日本一区二区成人| 国语对白永久免费| 亚洲国产精品va在线观看黑人| a在线免费观看| 成人久久久久久| 91综合在线| 男女视频在线看| 亚洲国产精品av| 国产成人a v| 国产一区二区精品丝袜| 韩国美女久久| 欧洲一区二区日韩在线视频观看免费 | 亚洲一区高清| 国内成人自拍视频| 日本 欧美 国产| 欧美久久一区二区| 黄色动漫在线观看| 91亚洲va在线va天堂va国 | 中文字幕成人| 一区二区三区四区五区精品| 久久国产视频网| 国产精品嫩草影院俄罗斯| 欧美一区二区三区视频免费| 91高清在线观看视频| 91在线在线观看| 国内久久视频| 国产精品一级黄片| 日韩欧美中文免费| 国产一级网站视频在线| 国产精品永久免费在线| 国产精品不卡| av在线免费观看不卡| 亚洲国产人成综合网站| 香蕉av在线播放| 国产福利视频一区二区| 久久综合国产| 亚洲成人av免费观看| 亚洲v中文字幕| 免费国产在线观看| 国产日韩在线看片| 欧美激情第10页| www.88av| 欧洲亚洲精品在线| 国产成人无吗| 精品人伦一区二区三区| 久久综合狠狠| 丰满的亚洲女人毛茸茸| 91精品午夜视频| 精精国产xxxx视频在线野外| 日韩视频在线播放| 国产精品66部| 好吊妞视频一区二区三区| 中文字幕精品视频| 97青娱国产盛宴精品视频| 日韩少妇内射免费播放| 国产欧美1区2区3区| 亚洲欧美另类日韩| 欧洲成人在线观看| 这里只有精品在线| www.久久av| 日韩亚洲欧美成人一区| 美女100%一区| 黄色污污在线观看| 久久先锋影音av鲁色资源网| www.黄色片| 国产精品av在线播放|