激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務(wù)器之家:專注于服務(wù)器技術(shù)及軟件下載分享
分類導(dǎo)航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務(wù)器之家 - 編程語言 - JavaScript - node.js - Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理

Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理

2022-03-10 16:13weixin_39214481 node.js

這篇文章主要介紹了Nodejs單線程實(shí)現(xiàn)高并發(fā)原理,對(duì)Node.js感興趣的同學(xué),可以參考下

前言

從Node.js進(jìn)入我們的視野時(shí),我們所知道的它就由這些關(guān)鍵字組成 事件驅(qū)動(dòng)、非阻塞I/O、高效、輕量,它在官網(wǎng)中也是這么描述自己的。
Node.js® is a JavaScript runtime built on Chrome"s V8 JavaScript engine. Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient.

于是在我們剛接觸Nodejs時(shí),會(huì)有所疑問:

1、為什么在瀏覽器中運(yùn)行的Javascript能與操作系統(tǒng)進(jìn)行如此底層的交互? 
2、nodejs 真的是單線程嗎?
3、如果是單線程,他是如何處理高并發(fā)請(qǐng)求的?
4、nodejs 事件驅(qū)動(dòng)是如何實(shí)現(xiàn)的?

看到這些問題,是否有點(diǎn)頭大,別急,帶著這些問題我們來慢慢看這篇文章。

架構(gòu)一覽

上面的問題,都挺底層的,所以我們從 Node.js 本身入手,先來看看 Node.js 的結(jié)構(gòu)。 

Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理

Node.js 標(biāo)準(zhǔn)庫,這部分是由 Javascript編寫的,即我們使用過程中直接能調(diào)用的 API。在源碼中的 lib 目錄下可以看到。

Node bindings,這一層是 Javascript與底層 C/C++ 能夠溝通的關(guān)鍵,前者通過 bindings 調(diào)用后者,相互交換數(shù)據(jù)。實(shí)現(xiàn)在 node.cc

這一層是支撐 Node.js 運(yùn)行的關(guān)鍵,由 C/C++ 實(shí)現(xiàn)。
V8:Google 推出的 Javascript VM,也是 Node.js 為什么使用的是 Javascript的關(guān)鍵,它為 Javascript提供了在非瀏覽器端運(yùn)行的環(huán)境,它的高效是 Node.js 之所以高效的原因之一。
Libuv:它為 Node.js 提供了跨平臺(tái),線程池,事件池,異步 I/O 等能力,是 Node.js 如此強(qiáng)大的關(guān)鍵。
C-ares:提供了異步處理 DNS 相關(guān)的能力。
http_parser、OpenSSL、zlib 等:提供包括 http 解析、SSL、數(shù)據(jù)壓縮等其他的能力。

與操作系統(tǒng)交互

舉個(gè)簡單的例子,我們想要打開一個(gè)文件,并進(jìn)行一些操作,可以寫下面這樣一段代碼:

var fs = require("fs");fs.open("./test.txt", "w", function(err, fd) {    //..do something});

這段代碼的調(diào)用過程大致可描述為:lib/fs.js → src/node_file.cc → uv_fs

lib/fs.js

async function open(path, flags, mode) {  mode = modeNum(mode, 0o666);  path = getPathFromURL(path);
  validatePath(path);
  validateUint32(mode, "mode");
  return new FileHandle(
    await binding.openFileHandle(pathModule.toNamespacedPath(path),
             stringToFlags(flags),             mode, kUsePromises));
}

src/node_file.cc

static void Open(const FunctionCallbackInfo& args) {  Environment* env = Environment::GetCurrent(args);  const int argc = args.Length();  if (req_wrap_async != nullptr) {  // open(path, flags, mode, req)    AsyncCall(env, req_wrap_async, args, "open", UTF8, AfterInteger,
              uv_fs_open, *path, flags, mode);
  } else {  // open(path, flags, mode, undefined, ctx)    CHECK_EQ(argc, 5);    FSReqWrapSync req_wrap_sync;    FS_SYNC_TRACE_BEGIN(open);    int result = SyncCall(env, args[4], &req_wrap_sync, "open",
                          uv_fs_open, *path, flags, mode);    FS_SYNC_TRACE_END(open);
    args.GetReturnValue().Set(result);
  }
}

uv_fs

/* Open the destination file. */
  dstfd = uv_fs_open(NULL,                     &fs_req,
                     req->new_path,
                     dst_flags,
                     statsbuf.st_mode,                     NULL);
  uv_fs_req_cleanup(&fs_req);

Node.js 深入淺出上的一幅圖:

Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理

具體來說,當(dāng)我們調(diào)用 fs.open 時(shí),Node.js 通過 process.binding 調(diào)用 C/C++ 層面的 Open 函數(shù),然后通過它調(diào)用 Libuv 中的具體方法 uv_fs_open,最后執(zhí)行的結(jié)果通過回調(diào)的方式傳回,完成流程。

我們?cè)?Javascript中調(diào)用的方法,最終都會(huì)通過 process.binding 傳遞到 C/C++ 層面,最終由他們來執(zhí)行真正的操作。Node.js 即這樣與操作系統(tǒng)進(jìn)行互動(dòng)。

單線程

在傳統(tǒng)web 服務(wù)模型中,大多都使用多線程來解決并發(fā)的問題,因?yàn)镮/O 是阻塞的,單線程就意味著用戶要等待,顯然這是不合理的,所以創(chuàng)建多個(gè)線程來響應(yīng)用戶的請(qǐng)求。
Node.js 對(duì)http 服務(wù)的模型:

Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理

Node.js的單線程指的是主線程是“單線程”,由主要線程去按照編碼順序一步步執(zhí)行程序代碼,假如遇到同步代碼阻塞,主線程被占用,后續(xù)的程序代碼執(zhí)行就會(huì)被卡住。實(shí)踐一個(gè)測(cè)試代碼:

var http = require("http");function sleep(time) {    var _exit = Date.now() + time * 1000;    while( Date.now() < _exit ) {}    return ;
}var server = http.createServer(function(req, res){
    sleep(10);
    res.end("server sleep 10s");
});

server.listen(8080);

下面為代碼塊的堆棧圖: 

Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理

先將index.js的代碼改成這樣,然后打開瀏覽器,你會(huì)發(fā)現(xiàn)瀏覽器在10秒之后才做出反應(yīng),打出Hello Node.js。

JavaScript是解析性語言,代碼按照編碼順序一行一行被壓進(jìn)stack里面執(zhí)行,執(zhí)行完成后移除然后繼續(xù)壓下一行代碼塊進(jìn)去執(zhí)行。上面代碼塊的堆棧圖,當(dāng)主線程接受了request后,程序被壓進(jìn)同步執(zhí)行的sleep執(zhí)行塊(我們假設(shè)這里就是程序的業(yè)務(wù)處理),如果在這10s內(nèi)有第二個(gè)request進(jìn)來就會(huì)被壓進(jìn)stack里面等待10s執(zhí)行完成后再進(jìn)一步處理下一個(gè)請(qǐng)求,后面的請(qǐng)求都會(huì)被掛起等待前面的同步執(zhí)行完成后再執(zhí)行。

那么我們會(huì)疑問:為什么一個(gè)單線程的效率可以這么高,同時(shí)處理數(shù)萬級(jí)的并發(fā)而不會(huì)造成阻塞呢?就是我們下面所說的--------事件驅(qū)動(dòng)。

事件驅(qū)動(dòng)/事件循環(huán)

Event Loop is a programming construct that waits for and dispatches events or messages in a program. 

Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理

1、每個(gè)Node.js進(jìn)程只有一個(gè)主線程在執(zhí)行程序代碼,形成一個(gè)執(zhí)行棧(execution context stack)。
2、主線程之外,還維護(hù)了一個(gè)"事件隊(duì)列"(Event queue)。當(dāng)用戶的網(wǎng)絡(luò)請(qǐng)求或者其它的異步操作到來時(shí),node都會(huì)把它放到Event Queue之中,此時(shí)并不會(huì)立即執(zhí)行它,代碼也不會(huì)被阻塞,繼續(xù)往下走,直到主線程代碼執(zhí)行完畢。
3、主線程代碼執(zhí)行完畢完成后,然后通過Event Loop,也就是事件循環(huán)機(jī)制,開始到Event Queue的開頭取出第一個(gè)事件,從線程池中分配一個(gè)線程去執(zhí)行這個(gè)事件,接下來繼續(xù)取出第二個(gè)事件,再從線程池中分配一個(gè)線程去執(zhí)行,然后第三個(gè),第四個(gè)。主線程不斷的檢查事件隊(duì)列中是否有未執(zhí)行的事件,直到事件隊(duì)列中所有事件都執(zhí)行完了,此后每當(dāng)有新的事件加入到事件隊(duì)列中,都會(huì)通知主線程按順序取出交EventLoop處理。當(dāng)有事件執(zhí)行完畢后,會(huì)通知主線程,主線程執(zhí)行回調(diào),線程歸還給線程池。
4、主線程不斷重復(fù)上面的第三步。

我們所看到的node.js單線程只是一個(gè)js主線程,本質(zhì)上的異步操作還是由線程池完成的,node將所有的阻塞操作都交給了內(nèi)部的線程池去實(shí)現(xiàn),本身只負(fù)責(zé)不斷的往返調(diào)度,并沒有進(jìn)行真正的I/O操作,從而實(shí)現(xiàn)異步非阻塞I/O,這便是node單線程和事件驅(qū)動(dòng)的精髓之處了。

Node.js 中的事件循環(huán)**的實(shí)現(xiàn):**

Node.js采用V8作為js的解析引擎,而I/O處理方面使用了自己設(shè)計(jì)的libuv,libuv是一個(gè)基于事件驅(qū)動(dòng)的跨平臺(tái)抽象層,封裝了不同操作系統(tǒng)一些底層特性,對(duì)外提供統(tǒng)一的API,事件循環(huán)機(jī)制也是它里面的實(shí)現(xiàn)。 在src/node.cc中:

Environment* CreateEnvironment(IsolateData* isolate_data,
                               Local context,                               int argc,                               const char* const* argv,                               int exec_argc,                               const char* const* exec_argv) {
  Isolate* isolate = context->GetIsolate();  HandleScope handle_scope(isolate);
  Context::Scope context_scope(context);  auto env = new Environment(isolate_data, context,
                             v8_platform.GetTracingAgent());
  env->Start(argc, argv, exec_argc, exec_argv, v8_is_profiling);  return env;
}

這段代碼建立了一個(gè)node執(zhí)行環(huán)境,可以看到第三行的uv_default_loop(),這是libuv庫中的一個(gè)函數(shù),它會(huì)初始化uv庫本身以及其中的default_loop_struct,并返回一個(gè)指向它的指針default_loop_ptr。 之后,Node會(huì)載入執(zhí)行環(huán)境并完成一些設(shè)置操作,然后啟動(dòng)event loop

{
    SealHandleScope seal(isolate);
    bool more;
    env.performance_state()->Mark(
        node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_START);
    do {
      uv_run(env.event_loop(), UV_RUN_DEFAULT);

      v8_platform.DrainVMTasks(isolate);

      more = uv_loop_alive(env.event_loop());      if (more)
        continue;

      RunBeforeExit(&env);      // Emit `beforeExit` if the loop became alive either after emitting
      // event, or after running some callbacks.
      more = uv_loop_alive(env.event_loop());
    } while (more == true);
    env.performance_state()->Mark(
        node::performance::NODE_PERFORMANCE_MILESTONE_LOOP_EXIT);
  }

  env.set_trace_sync_io(false);

  const int exit_code = EmitExit(&env);
  RunAtExit(&env);

more用來標(biāo)識(shí)是否進(jìn)行下一輪循環(huán)。 env->event_loop()會(huì)返回之前保存在env中的default_loop_ptr,uv_run函數(shù)將以指定的UV_RUN_DEFAULT模式啟動(dòng)libuv的event loop。如果當(dāng)前沒有I/O事件也沒有定時(shí)器事件,則uv_loop_alive返回false。

Event Loop的執(zhí)行順序

根據(jù)Node.js官方介紹,每次事件循環(huán)都包含了6個(gè)階段,對(duì)應(yīng)到 libuv 源碼中的實(shí)現(xiàn),如下圖所示: 

Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理

  • timers 階段:這個(gè)階段執(zhí)行timer(setTimeout、setInterval)的回調(diào)
  • I/O callbacks 階段:執(zhí)行一些系統(tǒng)調(diào)用錯(cuò)誤,比如網(wǎng)絡(luò)通信的錯(cuò)誤回調(diào)
  • idle, prepare 階段:僅node內(nèi)部使用
  • poll 階段:獲取新的I/O事件, 適當(dāng)?shù)臈l件下node將阻塞在這里
  • check 階段:執(zhí)行setImmediate()的回調(diào)
  • close callbacks 階段:執(zhí)行socket的close事件回調(diào)。

核心函數(shù)uv_run:源碼 核心源碼

int uv_run(uv_loop_t* loop, uv_run_mode mode) {  int timeout;  int r;  int ran_pending;//首先檢查我們的loop還是否活著//活著的意思代表loop中是否有異步任務(wù)//如果沒有直接就結(jié)束
  r = uv__loop_alive(loop);  if (!r)
    uv__update_time(loop);//傳說中的事件循環(huán),你沒看錯(cuò)了?。【褪且粋€(gè)大while
  while (r != 0 && loop->stop_flag == 0) { //更新事件階段
    uv__update_time(loop); //處理timer回調(diào)
    uv__run_timers(loop); //處理異步任務(wù)回調(diào) 
    ran_pending = uv__run_pending(loop);//沒什么用的階段
    uv__run_idle(loop);
    uv__run_prepare(loop);    //這里值得注意了
    //從這里到后面的uv__io_poll都是非常的不好懂的
    //先記住timeout是一個(gè)時(shí)間
    //uv_backend_timeout計(jì)算完畢后,傳遞給uv__io_poll
    //如果timeout = 0,則uv__io_poll會(huì)直接跳過
    timeout = 0;    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);

    uv__io_poll(loop, timeout);    //就是跑setImmediate
    uv__run_check(loop);    //關(guān)閉文件描述符等操作
    uv__run_closing_handles(loop);    if (mode == UV_RUN_ONCE) {      /* UV_RUN_ONCE implies forward progress: at least one callback must have
       * been invoked when it returns. uv__io_poll() can return without doing
       * I/O (meaning: no callbacks) when its timeout expires - which means we
       * have pending timers that satisfy the forward progress constraint.
       *
       * UV_RUN_NOWAIT makes no guarantees about progress so it"s omitted from
       * the check.
       */
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)      break;
  }  /* The if statement lets gcc compile it to a conditional store. Avoids
   * dirtying a cache line.
   */
  if (loop->stop_flag != 0)    loop->stop_flag = 0;  return r;
}

代碼中我已經(jīng)寫得很詳細(xì)了,相信不熟悉c代碼的各位也能輕易搞懂,沒錯(cuò),事件循環(huán)就是一個(gè)大while而已!神秘的面紗就此揭開。

uv__io_poll階段

這個(gè)階段設(shè)計(jì)得非常巧妙,這個(gè)函數(shù)第二個(gè)參數(shù)是一個(gè)timeout參數(shù),而這個(gè)timeOut由來自u(píng)v_backend_timeout函數(shù),我們進(jìn)去一探究竟!

源碼

int uv_backend_timeout(const uv_loop_t* loop) {  if (loop->stop_flag != 0)    return 0;  if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop))    return 0;  if (!QUEUE_EMPTY(&loop->idle_handles))    return 0;  if (!QUEUE_EMPTY(&loop->pending_queue))    return 0;  if (loop->closing_handles)    return 0;  return uv__next_timeout(loop);
}

原來是一個(gè)多步if函數(shù),我們一個(gè)一個(gè)分析

1. stop_flag:這個(gè)標(biāo)記是 0的時(shí)候,意味著事件循環(huán)跑完這一輪就退出了,返回的時(shí)間是0

2. !uv__has_active_handles和!uv__has_active_reqs:看名字都知道,如果沒有任何的異步任務(wù)(包括timer和異步I/O),那timeOut時(shí)間一定就是0了

3. QUEUE_EMPTY(idle_handles)和QUEUE_EMPTY(pending_queue):異步任務(wù)是通過注冊(cè)的方式放進(jìn)了pending_queue中,無論是否成功,都已經(jīng)被注冊(cè),如果什么都沒有,這兩個(gè)隊(duì)列就是空,所以沒必要等了。

4. closing_handles:我們的循環(huán)進(jìn)入了關(guān)閉階段,沒必要等待了

以上所有條件判斷來判斷去,為的就是等這句話return uv__next_timeout(loop);這句話,告訴了uv__io_poll說:你到底停多久,接下來,我們繼續(xù)看這個(gè)神奇的uv__next_timeout是怎么獲取時(shí)間的。

int uv__next_timeout(const uv_loop_t* loop) {  const struct heap_node* heap_node;  const uv_timer_t* handle;
  uint64_t diff;

  heap_node = heap_min((const struct heap*) &loop->timer_heap);  if (heap_node == NULL)    return -1; /* block indefinitely */

  handle = container_of(heap_node, uv_timer_t, heap_node);  if (handle->timeout time)    return 0;//這句代碼給出了關(guān)鍵性的指導(dǎo)
  diff = handle->timeout - loop->time;//不能大于最大的INT_MAX
  if (diff > INT_MAX)
    diff = INT_MAX;  return diff;
}

等待結(jié)束以后,就會(huì)進(jìn)入check 階段.然后進(jìn)入closing_handles階段,至此一個(gè)事件循環(huán)結(jié)束。 因?yàn)槭窃创a解析,所以具體的我就不多說,大家只可以看官方文檔

總結(jié)

1、Nodejs與操作系統(tǒng)交互,我們?cè)?Javascript中調(diào)用的方法,最終都會(huì)通過 process.binding 傳遞到 C/C++ 層面,最終由他們來執(zhí)行真正的操作。Node.js 即這樣與操作系統(tǒng)進(jìn)行互動(dòng)。

2、nodejs所謂的單線程,只是主線程是單線程,所有的網(wǎng)絡(luò)請(qǐng)求或者異步任務(wù)都交給了內(nèi)部的線程池去實(shí)現(xiàn),本身只負(fù)責(zé)不斷的往返調(diào)度,由事件循環(huán)不斷驅(qū)動(dòng)事件執(zhí)行。

3、Nodejs之所以單線程可以處理高并發(fā)的原因,得益于libuv層的事件循環(huán)機(jī)制,和底層線程池實(shí)現(xiàn)。

4、Event loop就是主線程從主線程的事件隊(duì)列里面不停循環(huán)的讀取事件,驅(qū)動(dòng)了所有的異步回調(diào)函數(shù)的執(zhí)行,Event loop總共7個(gè)階段,每個(gè)階段都有一個(gè)任務(wù)隊(duì)列,當(dāng)所有階段被順序執(zhí)行一次后,event loop 完成了一個(gè) tick。

以上就是Nodejs探秘之深入理解單線程實(shí)現(xiàn)高并發(fā)原理的詳細(xì)內(nèi)容,更多關(guān)于Nodejs的資料請(qǐng)關(guān)注服務(wù)器之家其它相關(guān)文章!

原文鏈接:https://blog.csdn.net/weixin_39214481/article/details/86170751

延伸 · 閱讀

精彩推薦
  • node.jsNode.js ObjectWrap 的弱引用問題

    Node.js ObjectWrap 的弱引用問題

    最近在寫 Node.js Addon 的過程中,遇到了一個(gè)問題,然后發(fā)現(xiàn)是 ObjectWrap 弱引用導(dǎo)致的,本文介紹一下具體的問題和排查過程,以及 ObjectWrap 的使用問題。...

    編程雜技9852022-01-04
  • node.js詳解node.js創(chuàng)建一個(gè)web服務(wù)器(Server)的詳細(xì)步驟

    詳解node.js創(chuàng)建一個(gè)web服務(wù)器(Server)的詳細(xì)步驟

    這篇文章主要介紹了詳解node.js創(chuàng)建一個(gè)web服務(wù)器(Server)的詳細(xì)步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,...

    王佳斌8952021-12-31
  • node.jslinux服務(wù)器快速卸載安裝node環(huán)境(簡單上手)

    linux服務(wù)器快速卸載安裝node環(huán)境(簡單上手)

    這篇文章主要介紹了linux服務(wù)器快速卸載安裝node環(huán)境(簡單上手),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需...

    mose-x8462022-01-22
  • node.jsk8s node節(jié)點(diǎn)重新加入master集群的實(shí)現(xiàn)

    k8s node節(jié)點(diǎn)重新加入master集群的實(shí)現(xiàn)

    這篇文章主要介紹了k8s node節(jié)點(diǎn)重新加入master集群的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋...

    Scarborought13922022-01-22
  • node.js在瀏覽器中,把 Vite 跑起來了!

    在瀏覽器中,把 Vite 跑起來了!

    大家好,我是 ssh,前幾天在推上沖浪的時(shí)候,看到 Francois Valdy 宣布他制作了 browser-vite[1],成功把 Vite 成功在瀏覽器中運(yùn)行起來了。這引起了我的興趣,如...

    前端從進(jìn)階到入院9282022-01-11
  • node.jsNode.js 中如何收集和解析命令行參數(shù)

    Node.js 中如何收集和解析命令行參數(shù)

    這篇文章主要介紹了Node.js 中如何收集和解析命令行參數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋...

    descire8802021-12-28
  • node.jsnodejs中使用worker_threads來創(chuàng)建新的線程的方法

    nodejs中使用worker_threads來創(chuàng)建新的線程的方法

    這篇文章主要介紹了nodejs中使用worker_threads來創(chuàng)建新的線程的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友...

    flydean程序那些事8982022-01-06
  • node.jsrequire加載器實(shí)現(xiàn)原理的深入理解

    require加載器實(shí)現(xiàn)原理的深入理解

    這篇文章主要給大家介紹了關(guān)于require加載器實(shí)現(xiàn)原理的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需...

    隱冬8462022-03-03
主站蜘蛛池模板: 奇米影视888狠狠狠777不卡 | 国产亚洲美女精品久久久2020 | 91短视频网页版 | 国产亚洲精品成人 | 成年人在线免费 | 91成人精品 | 国产一级片91 | 无遮挡一级毛片视频 | 久久精品中文字幕一区二区 | 中文字幕在线观看网址 | 成人免费自拍视频 | 99欧美视频 | 欧美a∨一区二区三区久久黄 | 久久精品国产99国产精品亚洲 | 国产一级αv片免费观看 | 国产精品中文在线 | 毛片在哪看 | 国产精品免费大片 | 一区二区三区小视频 | 欧美成人h版在线观看 | 久久精品艹 | 国产一区二区三区视频观看 | 亚洲成人入口 | 久久影院国产精品 | 国产91丝袜在线播放 | 亚洲导航深夜福利涩涩屋 | hdhdhd79xxxxх| 亚洲精品在线观看免费 | 欧美日韩精品不卡一区二区三区 | 正在播放91精 | 北原夏美av | av不卡毛片 | 亚洲va久久久噜噜噜久久男同 | 在线亚洲观看 | 欧美成在线视频 | 国产毛片网站 | 久久在现视频 | 久久国产一级 | av在线免费播放 | 久久中文免费 | 亚洲成人欧美在线 |