欧美亚洲韩国_av电影院在线看_久久久久97_台湾佬中文娱乐网欧美电影

站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

一文聊聊Node.js中的cluster(集群)

一文聊聊Node.js中的cluster(集群)

日常工作中,對 Node.js 的使用都比較粗淺,趁未羊之際,來學(xué)點(diǎn)稍微高級的,那就先從 cluster 開始吧。

尼古拉斯張三說過,“帶著問題去學(xué)習(xí)是一個比較好的方法”,所以我們也來試一試。

當(dāng)初使用 cluster 時,一直好奇它是怎么做到多個子進(jìn)程監(jiān)聽同一個端口而不沖突的,比如下面這段代碼:

const cluster = require('cluster') const net = require('net') const cpus = require('os').cpus()  if (cluster.isPrimary) {   for (let i = 0; i < cpus.length; i++) {     cluster.fork()   } } else {   net     .createServer(function (socket) {       socket.on('data', function (data) {         socket.write(`Reply from ${process.pid}: ` + data.toString())       })       socket.on('end', function () {         console.log('Close')       })       socket.write('Hello!n')     })     .listen(9999) }
登錄后復(fù)制

該段代碼通過父進(jìn)程 fork 出了多個子進(jìn)程,且這些子進(jìn)程都監(jiān)聽了 9999 這個端口并能正常提供服務(wù),這是如何做到的呢?我們來研究一下。【相關(guān)教程推薦:nodejs視頻教程、編程教學(xué)】

準(zhǔn)備調(diào)試環(huán)境

學(xué)習(xí) Node.js 官方提供庫最好的方式當(dāng)然是調(diào)試一下,所以,我們先來準(zhǔn)備一下環(huán)境。注:本文的操作系統(tǒng)為 macOS Big Sur 11.6.6,其他系統(tǒng)請自行準(zhǔn)備相應(yīng)環(huán)境。

編譯 Node.js

  • 下載 Node.js 源碼

git clone https://github.com/nodejs/node.git
登錄后復(fù)制

然后在下面這兩個地方加入斷點(diǎn),方便后面調(diào)試用:

// lib/internal/cluster/primary.js function queryServer(worker, message) {   debugger;   // Stop processing if worker already disconnecting   if (worker.exitedAfterDisconnect) return;    ... }
登錄后復(fù)制

// lib/internal/cluster/child.js send(message, (reply, handle) => {   debugger   if (typeof obj._setServerData === 'function') obj._setServerData(reply.data)    if (handle) {     // Shared listen socket     shared(reply, {handle, indexesKey, index}, cb)   } else {     // Round-robin.     rr(reply, {indexesKey, index}, cb)   } })
登錄后復(fù)制

  • 進(jìn)入目錄,執(zhí)行

./configure --debug make -j4
登錄后復(fù)制

之后會生成 out/Debug/node

準(zhǔn)備 IDE 環(huán)境

使用 vscode 調(diào)試,配置好 launch.json 就可以了(其他 IDE 類似,請自行解決):

{   "version": "0.2.0",   "configurations": [     {       "name": "Debug C++",       "type": "cppdbg",       "program": "/Users/youxingzhi/ayou/node/out/Debug/node",       "request": "launch",       "args": ["/Users/youxingzhi/ayou/node/index.js"],       "stopAtEntry": false,       "cwd": "${workspaceFolder}",       "environment": [],       "externalConsole": false,       "MIMode": "lldb"     },     {       "name": "Debug Node",       "type": "node",       "runtimeExecutable": "/Users/youxingzhi/ayou/node/out/Debug/node",       "request": "launch",       "args": ["--expose-internals", "--nolazy"],       "skipFiles": [],       "program": "${workspaceFolder}/index.js"     }   ] }
登錄后復(fù)制

其中第一個是用于調(diào)式 C++ 代碼(需要安裝 C/C++ 插件),第二個用于調(diào)式 JS 代碼。接下來就可以開始調(diào)試了,我們暫時用調(diào)式 JS 代碼的那個配置就好了。

Cluster 源碼調(diào)試

準(zhǔn)備好調(diào)試代碼(為了調(diào)試而已,這里啟動一個子進(jìn)程就夠了):

debugger const cluster = require('cluster') const net = require('net')  if (cluster.isPrimary) {   debugger   cluster.fork() } else {   const server = net.createServer(function (socket) {     socket.on('data', function (data) {       socket.write(`Reply from ${process.pid}: ` + data.toString())     })     socket.on('end', function () {       console.log('Close')     })     socket.write('Hello!n')   })   debugger   server.listen(9999) }
登錄后復(fù)制

很明顯,我們的程序可以分父進(jìn)程和子進(jìn)程這兩部分來進(jìn)行分析。

首先進(jìn)入的是父進(jìn)程:

執(zhí)行 require('cluster') 時,會進(jìn)入 lib/cluster.js 這個文件:

const childOrPrimary = 'NODE_UNIQUE_ID' in process.env ? 'child' : 'primary' module.exports = require(`internal/cluster/${childOrPrimary}`)
登錄后復(fù)制

會根據(jù)當(dāng)前 process.env 上是否有 NODE_UNIQUE_ID 來引入不同的模塊,此時是沒有的,所以會引入 internal/cluster/primary.js 這個模塊:

... const cluster = new EventEmitter(); ... module.exports = cluster  const handles = new SafeMap() cluster.isWorker = false cluster.isMaster = true // Deprecated alias. Must be same as isPrimary. cluster.isPrimary = true cluster.Worker = Worker cluster.workers = {} cluster.settings = {} cluster.SCHED_NONE = SCHED_NONE // Leave it to the operating system. cluster.SCHED_RR = SCHED_RR // Primary distributes connections. ... cluster.schedulingPolicy = schedulingPolicy  cluster.setupPrimary = function (options) { ... }  // Deprecated alias must be same as setupPrimary cluster.setupMaster = cluster.setupPrimary  function setupSettingsNT(settings) { ... }  function createWorkerProcess(id, env) {   ... }  function removeWorker(worker) {  ... }  function removeHandlesForWorker(worker) {  ... }  cluster.fork = function (env) {   ... }
登錄后復(fù)制

該模塊主要是在 cluster 對象上掛載了一些屬性和方法,并導(dǎo)出,這些后面回過頭再看,我們繼續(xù)往下調(diào)試。往下調(diào)試會進(jìn)入 if (cluster.isPrimary) 分支,代碼很簡單,僅僅是 fork 出了一個新的子進(jìn)程而已:

// lib/internal/cluster/primary.js cluster.fork = function (env) {   cluster.setupPrimary()   const id = ++ids   const workerProcess = createWorkerProcess(id, env)   const worker = new Worker({     id: id,     process: workerProcess,   })    ...    worker.process.on('internalMessage', internal(worker, onmessage))   process.nextTick(emitForkNT, worker)   cluster.workers[worker.id] = worker   return worker }
登錄后復(fù)制

cluster.setupPrimary():比較簡單,初始化一些參數(shù)啥的。

createWorkerProcess(id, env)

// lib/internal/cluster/primary.js function createWorkerProcess(id, env) {   const workerEnv = {...process.env, ...env, NODE_UNIQUE_ID: `${id}`}   const execArgv = [...cluster.settings.execArgv]    ...    return fork(cluster.settings.exec, cluster.settings.args, {     cwd: cluster.settings.cwd,     env: workerEnv,     serialization: cluster.settings.serialization,     silent: cluster.settings.silent,     windowsHide: cluster.settings.windowsHide,     execArgv: execArgv,     stdio: cluster.settings.stdio,     gid: cluster.settings.gid,     uid: cluster.settings.uid,   }) }
登錄后復(fù)制

可以看到,該方法主要是通過 fork 啟動了一個子進(jìn)程來執(zhí)行我們的 index.js,且啟動子進(jìn)程的時候設(shè)置了環(huán)境變量 NODE_UNIQUE_ID,這樣 index.jsrequire('cluster') 的時候,引入的就是 internal/cluster/child.js 模塊了。

worker.process.on('internalMessage', internal(worker, onmessage)):監(jiān)聽子進(jìn)程傳遞過來的消息并處理。

接下來就進(jìn)入了子進(jìn)程的邏輯:

前面說了,此時引入的是 internal/cluster/child.js 模塊,我們先跳過,繼續(xù)往下,執(zhí)行 server.listen(9999) 時實(shí)際上是調(diào)用了 Server 上的方法:

// lib/net.js Server.prototype.listen = function (...args) {   ...       listenInCluster(         this,         null,         options.port | 0,         4,         backlog,         undefined,         options.exclusive       ); }
登錄后復(fù)制

可以看到,最終是調(diào)用了 listenInCluster

// lib/net.js function listenInCluster(   server,   address,   port,   addressType,   backlog,   fd,   exclusive,   flags,   options ) {   exclusive = !!exclusive    if (cluster === undefined) cluster = require('cluster')    if (cluster.isPrimary || exclusive) {     // Will create a new handle     // _listen2 sets up the listened handle, it is still named like this     // to avoid breaking code that wraps this method     server._listen2(address, port, addressType, backlog, fd, flags)     return   }    const serverQuery = {     address: address,     port: port,     addressType: addressType,     fd: fd,     flags,     backlog,     ...options,   }   // Get the primary's server handle, and listen on it   cluster._getServer(server, serverQuery, listenOnPrimaryHandle)    function listenOnPrimaryHandle(err, handle) {     err = checkBindError(err, port, handle)      if (err) {       const ex = exceptionWithHostPort(err, 'bind', address, port)       return server.emit('error', ex)     }      // Reuse primary's server handle     server._handle = handle     // _listen2 sets up the listened handle, it is still named like this     // to avoid breaking code that wraps this method     server._listen2(address, port, addressType, backlog, fd, flags)   } }
登錄后復(fù)制

由于是在子進(jìn)程中執(zhí)行,所以最后會調(diào)用 cluster._getServer(server, serverQuery, listenOnPrimaryHandle)

// lib/internal/cluster/child.js // 這里的 cb 就是上面的 listenOnPrimaryHandle cluster._getServer = function (obj, options, cb) {   ...   send(message, (reply, handle) => {     debugger     if (typeof obj._setServerData === 'function') obj._setServerData(reply.data)      if (handle) {       // Shared listen socket       shared(reply, {handle, indexesKey, index}, cb)     } else {       // Round-robin.       rr(reply, {indexesKey, index}, cb)     }   })    ... }
登錄后復(fù)制

該函數(shù)最終會向父進(jìn)程發(fā)送 queryServer 的消息,父進(jìn)程處理完后會調(diào)用回調(diào)函數(shù),回調(diào)函數(shù)中會調(diào)用 cblistenOnPrimaryHandle。看來,listen 的邏輯是在父進(jìn)程中進(jìn)行的了。

接下來進(jìn)入父進(jìn)程:

父進(jìn)程收到 queryServer 的消息后,最終會調(diào)用 queryServer 這個方法:

// lib/internal/cluster/primary.js function queryServer(worker, message) {   // Stop processing if worker already disconnecting   if (worker.exitedAfterDisconnect) return    const key =     `${message.address}:${message.port}:${message.addressType}:` +     `${message.fd}:${message.index}`   let handle = handles.get(key)    if (handle === undefined) {     let address = message.address      // Find shortest path for unix sockets because of the ~100 byte limit     if (       message.port < 0 &&       typeof address === 'string' &&       process.platform !== 'win32'     ) {       address = path.relative(process.cwd(), address)        if (message.address.length < address.length) address = message.address     }      // UDP is exempt from round-robin connection balancing for what should     // be obvious reasons: it's connectionless. There is nothing to send to     // the workers except raw datagrams and that's pointless.     if (       schedulingPolicy !== SCHED_RR ||       message.addressType === 'udp4' ||       message.addressType === 'udp6'     ) {       handle = new SharedHandle(key, address, message)     } else {       handle = new RoundRobinHandle(key, address, message)     }      handles.set(key, handle)   }    ... }
登錄后復(fù)制

可以看到,這里主要是對 handle 的處理,這里的 handle 指的是調(diào)度策略,分為 SharedHandleRoundRobinHandle,分別對應(yīng)搶占式和輪詢兩種策略(文章最后補(bǔ)充部分有關(guān)于兩者對比的例子)。

Node.js 中默認(rèn)是 RoundRobinHandle 策略,可通過環(huán)境變量 NODE_CLUSTER_SCHED_POLICY 來修改,取值可以為 noneSharedHandle) 或 rrRoundRobinHandle)。

SharedHandle

首先,我們來看一下 SharedHandle,由于我們這里是 TCP 協(xié)議,所以最后會通過 net._createServerHandle 創(chuàng)建一個 TCP 對象掛載在 handle 屬性上(注意這里又有一個 handle,別搞混了):

// lib/internal/cluster/shared_handle.js function SharedHandle(key, address, {port, addressType, fd, flags}) {   this.key = key   this.workers = new SafeMap()   this.handle = null   this.errno = 0    let rval   if (addressType === 'udp4' || addressType === 'udp6')     rval = dgram._createSocketHandle(address, port, addressType, fd, flags)   else rval = net._createServerHandle(address, port, addressType, fd, flags)    if (typeof rval === 'number') this.errno = rval   else this.handle = rval }
登錄后復(fù)制

createServerHandle 中除了創(chuàng)建 TCP 對象外,還綁定了端口和地址:

// lib/net.js function createServerHandle(address, port, addressType, fd, flags) {   ...   } else {     handle = new TCP(TCPConstants.SERVER);     isTCP = true;   }    if (address || port || isTCP) {       ...       err = handle.bind6(address, port, flags);     } else {       err = handle.bind(address, port);     }   }    ...   return handle; }
登錄后復(fù)制

然后,queryServer 中繼續(xù)執(zhí)行,會調(diào)用 add 方法,最終會將 handle 也就是 TCP 對象傳遞給子進(jìn)程:

// lib/internal/cluster/primary.js function queryServer(worker, message) {   ...   if (!handle.data) handle.data = message.data    // Set custom server data   handle.add(worker, (errno, reply, handle) => {     const {data} = handles.get(key)      if (errno) handles.delete(key) // Gives other workers a chance to retry.      send(       worker,       {         errno,         key,         ack: message.seq,         data,         ...reply,       },       handle // TCP 對象     )   })   ... }
登錄后復(fù)制

之后進(jìn)入子進(jìn)程:

子進(jìn)程收到父進(jìn)程對于 queryServer 的回復(fù)后,會調(diào)用 shared

// lib/internal/cluster/child.js // `obj` is a net#Server or a dgram#Socket object. cluster._getServer = function (obj, options, cb) {   ...    send(message, (reply, handle) => {     if (typeof obj._setServerData === 'function') obj._setServerData(reply.data)      if (handle) {       // Shared listen socket       shared(reply, {handle, indexesKey, index}, cb)     } else {       // Round-robin.       rr(reply, {indexesKey, index}, cb) // cb 是 listenOnPrimaryHandle     }   })   ... }
登錄后復(fù)制

登錄后復(fù)制

shared 中最后會調(diào)用 cb 也就是 listenOnPrimaryHandle

// lib/net.js function listenOnPrimaryHandle(err, handle) {   err = checkBindError(err, port, handle)    if (err) {     const ex = exceptionWithHostPort(err, 'bind', address, port)     return server.emit('error', ex)   }   // Reuse primary's server handle 這里的 server 是 index.js 中 net.createServer 返回的那個對象   server._handle = handle   // _listen2 sets up the listened handle, it is still named like this   // to avoid breaking code that wraps this method   server._listen2(address, port, addressType, backlog, fd, flags) }
登錄后復(fù)制

這里會把 handle 賦值給 server._handle,這里的 serverindex.jsnet.createServer 返回的那個對象,并調(diào)用 server._listen2,也就是 setupListenHandle

// lib/net.js function setupListenHandle(address, port, addressType, backlog, fd, flags) {   debug('setupListenHandle', address, port, addressType, backlog, fd)   // If there is not yet a handle, we need to create one and bind.   // In the case of a server sent via IPC, we don't need to do this.   if (this._handle) {     debug('setupListenHandle: have a handle already')   } else {     ...   }    this[async_id_symbol] = getNewAsyncId(this._handle)   this._handle.onconnection = onconnection   this._handle[owner_symbol] = this    // Use a backlog of 512 entries. We pass 511 to the listen() call because   // the kernel does: backlogsize = roundup_pow_of_two(backlogsize + 1);   // which will thus give us a backlog of 512 entries.   const err = this._handle.listen(backlog || 511)    if (err) {     const ex = uvExceptionWithHostPort(err, 'listen', address, port)     this._handle.close()     this._handle = null     defaultTriggerAsyncIdScope(       this[async_id_symbol],       process.nextTick,       emitErrorNT,       this,       ex     )     return   } }
登錄后復(fù)制

首先會執(zhí)行 this._handle.onconnection = onconnection,由于客戶端請求過來時會調(diào)用 this._handle(也就是 TCP 對象)上的 onconnection 方法,也就是會執(zhí)行lib/net.js 中的 onconnection 方法建立連接,之后就可以通信了。為了控制篇幅,該方法就不繼續(xù)往下了。

然后調(diào)用 listen 監(jiān)聽,注意這里參數(shù) backlog 跟之前不同,不是表示端口,而是表示在拒絕連接之前,操作系統(tǒng)可以掛起的最大連接數(shù)量,也就是連接請求的排隊(duì)數(shù)量。我們平時遇到的 listen EADDRINUSE: address already in use 錯誤就是因?yàn)檫@行代碼返回了非 0 的錯誤。

如果還有其他子進(jìn)程,也會同樣走一遍上述的步驟,不同之處是在主進(jìn)程中 queryServer 時,由于已經(jīng)有 handle 了,不需要再重新創(chuàng)建了:

function queryServer(worker, message) {   debugger;   // Stop processing if worker already disconnecting   if (worker.exitedAfterDisconnect) return;    const key =     `${message.address}:${message.port}:${message.addressType}:` +     `${message.fd}:${message.index}`;   let handle = handles.get(key);   ... }
登錄后復(fù)制

以上內(nèi)容整理成流程圖如下:

一文聊聊Node.js中的cluster(集群)

所謂的 SharedHandle,其實(shí)是在多個子進(jìn)程中共享 TCP 對象的句柄,當(dāng)客戶端請求過來時,多個進(jìn)程會去競爭該請求的處理權(quán),會導(dǎo)致任務(wù)分配不均的問題,這也是為什么需要 RoundRobinHandle 的原因。接下來繼續(xù)看看這種調(diào)度方式。

RoundRobinHandle

// lib/internal/cluster/round_robin_handle.js function RoundRobinHandle(   key,   address,   {port, fd, flags, backlog, readableAll, writableAll} ) {   ...   this.server = net.createServer(assert.fail)    ...   else if (port >= 0) {     this.server.listen({       port,       host: address,       // Currently, net module only supports `ipv6Only` option in `flags`.       ipv6Only: Boolean(flags & constants.UV_TCP_IPV6ONLY),       backlog,     })   }   ...   this.server.once('listening', () => {     this.handle = this.server._handle     this.handle.onconnection = (err, handle) => {       this.distribute(err, handle)     }     this.server._handle = null     this.server = null   }) }
登錄后復(fù)制

如上所示,RoundRobinHandle 會調(diào)用 net.createServer() 創(chuàng)建一個 server,然后調(diào)用 listen 方法,最終會來到 setupListenHandle

// lib/net.js function setupListenHandle(address, port, addressType, backlog, fd, flags) {   debug('setupListenHandle', address, port, addressType, backlog, fd)   // If there is not yet a handle, we need to create one and bind.   // In the case of a server sent via IPC, we don't need to do this.   if (this._handle) {     debug('setupListenHandle: have a handle already')   } else {     debug('setupListenHandle: create a handle')      let rval = null      // Try to bind to the unspecified IPv6 address, see if IPv6 is available     if (!address && typeof fd !== 'number') {       rval = createServerHandle(DEFAULT_IPV6_ADDR, port, 6, fd, flags)        if (typeof rval === 'number') {         rval = null         address = DEFAULT_IPV4_ADDR         addressType = 4       } else {         address = DEFAULT_IPV6_ADDR         addressType = 6       }     }      if (rval === null)       rval = createServerHandle(address, port, addressType, fd, flags)      if (typeof rval === 'number') {       const error = uvExceptionWithHostPort(rval, 'listen', address, port)       process.nextTick(emitErrorNT, this, error)       return     }     this._handle = rval   }    this[async_id_symbol] = getNewAsyncId(this._handle)   this._handle.onconnection = onconnection   this._handle[owner_symbol] = this    ... }
登錄后復(fù)制

且由于此時 this._handle 為空,會調(diào)用 createServerHandle() 生成一個 TCP 對象作為 _handle。之后就跟 SharedHandle 一樣了,最后也會回到子進(jìn)程:

// lib/internal/cluster/child.js // `obj` is a net#Server or a dgram#Socket object. cluster._getServer = function (obj, options, cb) {   ...    send(message, (reply, handle) => {     if (typeof obj._setServerData === 'function') obj._setServerData(reply.data)      if (handle) {       // Shared listen socket       shared(reply, {handle, indexesKey, index}, cb)     } else {       // Round-robin.       rr(reply, {indexesKey, index}, cb) // cb 是 listenOnPrimaryHandle     }   })   ... }
登錄后復(fù)制

登錄后復(fù)制

不過由于 RoundRobinHandle 不會傳遞 handle 給子進(jìn)程,所以此時會執(zhí)行 rr

function rr(message, {indexesKey, index}, cb) {   ...   // Faux handle. Mimics a TCPWrap with just enough fidelity to get away   // with it. Fools net.Server into thinking that it's backed by a real   // handle. Use a noop function for ref() and unref() because the control   // channel is going to keep the worker alive anyway.   const handle = {close, listen, ref: noop, unref: noop}    if (message.sockname) {     handle.getsockname = getsockname // TCP handles only.   }    assert(handles.has(key) === false)   handles.set(key, handle)   debugger   cb(0, handle) }
登錄后復(fù)制

可以看到,這里構(gòu)造了一個假的 handle,然后執(zhí)行 cb 也就是 listenOnPrimaryHandle。最終跟 SharedHandle 一樣會調(diào)用 setupListenHandle 執(zhí)行 this._handle.onconnection = onconnection

RoundRobinHandle 邏輯到此就結(jié)束了,好像缺了點(diǎn)什么的樣子。回顧下,我們給每個子進(jìn)程中的 server 上都掛載了一個假的 handle,但它跟綁定了端口的 TCP 對象沒有任何關(guān)系,如果客戶端請求過來了,是不會執(zhí)行它上面的 onconnection 方法的。之所以要這樣寫,估計(jì)是為了保持跟之前 SharedHandle 代碼邏輯的統(tǒng)一。

此時,我們需要回到 RoundRobinHandle,有這樣一段代碼:

// lib/internal/cluster/round_robin_handle.js this.server.once('listening', () => {   this.handle = this.server._handle   this.handle.onconnection = (err, handle) => {     this.distribute(err, handle)   }   this.server._handle = null   this.server = null })
登錄后復(fù)制

listen 執(zhí)行完后,會觸發(fā) listening 事件的回調(diào),這里重寫了 handle 上面的 onconnection

所以,當(dāng)客戶端請求過來時,會調(diào)用 distribute 在多個子進(jìn)程中輪詢分發(fā),這里又有一個 handle,這里的 handle 姑且理解為 clientHandle,即客戶端連接的 handle,別搞混了。總之,最后會將這個 clientHandle 發(fā)送給子進(jìn)程:

// lib/internal/cluster/round_robin_handle.js RoundRobinHandle.prototype.handoff = function (worker) {   ...    const message = { act: 'newconn', key: this.key };   // 這里的 handle 是 clientHandle   sendHelper(worker.process, message, handle, (reply) => {     if (reply.accepted) handle.close();     else this.distribute(0, handle); // Worker is shutting down. Send to another.      this.handoff(worker);   }); };
登錄后復(fù)制

而子進(jìn)程在 require('cluster') 時,已經(jīng)監(jiān)聽了該事件:

// lib/internal/cluster/child.js process.on('internalMessage', internal(worker, onmessage)) send({act: 'online'})  function onmessage(message, handle) {   if (message.act === 'newconn') onconnection(message, handle)   else if (message.act === 'disconnect')     ReflectApply(_disconnect, worker, [true]) }
登錄后復(fù)制

最終也同樣會走到 net.js 中的 function onconnection(err, clientHandle) 方法。這個方法第二個參數(shù)名就叫 clientHandle,這也是為什么前面的 handle 我想叫這個名字的原因。

還是用圖來總結(jié)下:

一文聊聊Node.js中的cluster(集群)

SharedHandle 不同的是,該調(diào)度策略中 onconnection 最開始是在主進(jìn)程中觸發(fā)的,然后通過輪詢算法挑選一個子進(jìn)程,將 clientHandle 傳遞給它。

為什么端口不沖突

cluster 模塊的調(diào)試就到此告一段落了,接下來我們來回答一下一開始的問題,為什么多個進(jìn)程監(jiān)聽同一個端口沒有報錯?

網(wǎng)上有些文章說是因?yàn)樵O(shè)置了 SO_REUSEADDR,但其實(shí)跟這個沒關(guān)系。通過上面的分析知道,不管什么調(diào)度策略,最終都只會在主進(jìn)程中對 TCP 對象 bind 一次。

我們可以修改一下源代碼來測試一下:

// deps/uv/src/unix/tcp.c 下面的 SO_REUSEADDR 改成 SO_DEBUG if (setsockopt(tcp->io_watcher.fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)))
登錄后復(fù)制

編譯后執(zhí)行發(fā)現(xiàn),我們?nèi)匀豢梢哉J褂?cluster 模塊。

那這個 SO_REUSEADDR 到底影響的是啥呢?我們繼續(xù)來研究一下。

SO_REUSEADDR

首先,我們我們知道,下面的代碼是會報錯的:

const net = require('net') const server1 = net.createServer() const server2 = net.createServer() server1.listen(9999) server2.listen(9999)
登錄后復(fù)制

但是,如果我稍微修改一下,就不會報錯了:

const net = require('net') const server1 = net.createServer() const server2 = net.createServer() server1.listen(9999, '127.0.0.1') server2.listen(9999, '10.53.48.67')
登錄后復(fù)制

原因在于 listen 時,如果不指定 address,則相當(dāng)于綁定了所有地址,當(dāng)兩個 server 都這樣做時,請求到來就不知道要給誰處理了。

我們可以類比成找對象,port 是對外貌的要求,address 是對城市的要求。現(xiàn)在甲乙都想要一個 port1米7以上 不限城市的對象,那如果有一個 1米7以上 來自 深圳 的對象,就不知道介紹給誰了。而如果兩者都指定了城市就好辦多了。

那如果一個指定了 address,一個沒有呢?就像下面這樣:

const net = require('net') const server1 = net.createServer() const server2 = net.createServer() server1.listen(9999, '127.0.0.1') server2.listen(9999)
登錄后復(fù)制

結(jié)果是:設(shè)置了 SO_REUSEADDR 可以正常運(yùn)行,而修改成 SO_DEBUG 的會報錯。

還是上面的例子,甲對城市沒有限制,乙需要是來自 深圳 的,那當(dāng)一個對象來自 深圳,我們可以選擇優(yōu)先介紹給乙,非 深圳 的就選擇介紹給甲,這個就是 SO_REUSEADDR 的作用。

補(bǔ)充

SharedHandleRoundRobinHandle 兩種模式的對比

先準(zhǔn)備下測試代碼:

// cluster.js const cluster = require('cluster') const net = require('net')  if (cluster.isMaster) {   for (let i = 0; i < 4; i++) {     cluster.fork()   } } else {   const server = net.createServer()   server.on('connection', (socket) => {     console.log(`PID: ${process.pid}!`)   })   server.listen(9997) }
登錄后復(fù)制

// client.js const net = require('net') for (let i = 0; i < 20; i++) {   net.connect({port: 9997}) }
登錄后復(fù)制

RoundRobin先執(zhí)行 node cluster.js,然后執(zhí)行 node client.js,會看到如下輸出,可以看到?jīng)]有任何一個進(jìn)程的 PID 是緊挨著的。至于為什么沒有一直按照一樣的順序,后面再研究一下。

PID: 42904! PID: 42906! PID: 42905! PID: 42904! PID: 42907! PID: 42905! PID: 42906! PID: 42907! PID: 42904! PID: 42905! PID: 42906! PID: 42907! PID: 42904! PID: 42905! PID: 42906! PID: 42907! PID: 42904! PID: 42905! PID: 42906! PID: 42904!
登錄后復(fù)制

Shared

先執(zhí)行 NODE_CLUSTER_SCHED_POLICY=none node cluster.js,則 Node.js 會使用 SharedHandle,然后執(zhí)行 node client.js,會看到如下輸出,可以看到同一個 PID 連續(xù)輸出了多次,所以這種策略會導(dǎo)致進(jìn)程任務(wù)分配不均的現(xiàn)象。就像公司里有些人忙到 996,有些人天天摸魚,這顯然不是老板愿意看到的現(xiàn)象,所以不推薦使用。

PID: 42561! PID: 42562! PID: 42561! PID: 42562! PID: 42564! PID: 42561! PID: 42562! PID: 42563! PID: 42561! PID: 42562! PID: 42563! PID: 42564! PID: 42564! PID: 42564! PID: 42564! PID: 42564! PID: 42563! PID: 42563! PID: 42564! PID: 42563!
登錄后復(fù)制

贊(0)
分享到: 更多 (0)
網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
主站蜘蛛池模板: 亚洲丁香婷婷久久一区二区 | 成人毛片av | 中文字幕综合 | 亚洲经典一区 | 婷婷6月天 | 美梦视频大全在线观看高清 | 男女性网站 | 亚洲xxxxx | 久久久久久高清 | 亚洲精品在线免费看 | 六月婷婷在线观看 | 污视频网站在线观看 | 欧美精品乱码99久久蜜桃 | 看看屋午夜伦理 | 第四色影音先锋 | 亚洲精品国产精品国自产网站 | 手机看片中文字幕 | 一级片毛片 | 久久久.www | 性国产精品 | 免费萌白酱国产一区二区三区 | 亚洲一区二区三区中文字幕 | 免看一级a毛片一片成人不卡 | 69国产在线 | 日本午夜视频在线观看 | 91网站在线免费观看 | 精品久久影院 | 亚色视频在线观看 | 亚洲麻豆国产 | 一区二区国产欧美 | 色四虎 | 国产大尺度在线 | 毛片直接看 | 午夜视频免费看 | 露脸丨91丨九色露脸 | 天堂在线免费观看视频 | 不卡的av在线 | 免看一级a毛片一片成人不卡 | 日韩超碰在线 | 手机看片一区 | 黄色大片网址 | 国产男人的天堂 | 久久免费少妇做爰 | 这里只有精品9 | 午夜免费在线观看 | 国产日韩亚洲 | 校园春色亚洲激情 | 67194午夜| 另类性姿势bbwbbw | 99国内精品 | 观看av在线 | 91精品啪| 综合黄色 | 国产黄在线观看 | 高清视频免费在线观看 | 岛国一区二区三区 | 亚洲图片一区二区 | 日本黄网站在线观看 | 99产精品成人啪免费网站 | 欧美视频一区在线 | 色资源在线观看 | 成人午夜影院在线观看 | 国产黑丝一区二区 | 宅男av | 午夜久久网 | 在线免费黄 | 一级黄色录相 | 超碰666| 红桃视频成人 | 99福利视频| 久草午夜| 日韩视频一区二区在线观看 | 蜜桃视频污在线观看 | 中文字幕欧美在线观看 | 狠狠干视频在线 | 国产奶水涨喷在线播放 | 99热国产在线观看 | 波多野结衣一区二区三区在线 | 中文字幕乱码日产无人区 | 日日射日日干 | 亚洲精选免费 | av免费大片 | www.国产麻豆 | 一本一本久久a久久精品综合麻豆 | 精品久久中文 | aaa成人| 黄色一级免费观看 | 婷婷色基地 | av东方在线 | 欧美一区二区三区视频在线观看 | 欧美激情第二页 | 一级片在线免费播放 | 在线播放亚洲视频 | 涩涩网址 | 日韩网站在线观看 | 成人h动漫精品一区二区 | 婷婷激情综合 | 九九精品网 | 国产特级乱淫免费看 | 精品婷婷 | 国产在线视频一区 | 日韩久久免费 | 亚洲乱码在线 | 少妇在线 | 国产性精品 | 伊伊成人| 91免费网址 | 热热热色 | 欧美午夜精品一区二区三区 | 超碰97免费在线 | av三级网站 | 国产成人综合在线 | 色爽影院 | 911色| 国产精品羞羞答答在线观看 | 肉色超薄丝袜脚交一区二区 | 美女视频在线观看免费 | 中文字幕色片 | 国产91在线视频 | 亚洲国产一区二区a毛片 | 在线国产一区二区三区 | 国产性生活视频 | 欧美久久综合 | 中文字幕在线播放不卡 | 一本一道久久a久久精品综合 | 日韩成人在线播放 | 亚洲品质自拍视频 | 中国一区二区视频 | 亚洲精品乱码久久久久久 | 丁香五色月 | 亚洲剧情在线 | 日日爽夜夜操 | 亚洲图片自拍偷拍区 | 天天看a | 中文字幕亚洲一区二区三区五十路 | 久久久国产精品一区二区三区 | 91手机在线播放 | 青青草日本 | 亚洲国产成人精品女人久久 | 免费网站观看www在线观看 | 欧美日韩综合在线观看 | 一级黄色大片免费 | 色女人网站 | 老司机黄色片 | 成人网在线 | 亚欧洲精品 | 欧美一区二区三区在线观看视频 | 啪啪网站免费 | 九九热这里只有精品6 | 日韩一区二区免费在线观看 | 女人18毛片一区二区三区 | 日韩欧美一区二区三区四区 | 国产精品99999 | 久久夜色网| 91视频看看 | 亚洲国产成人自拍 | 精品福利视频导航 | 午夜激情视频网站 | 性欧美69 | 超碰福利在线观看 | 日韩成人高清视频 | 毛片视频网站在线观看 | 久久久这里有精品 | 国产精品日日做人人爱 | www.五月激情 | 国产四区视频 | 亚洲在线观看免费 | 国产一级视频 | 欧美com | 亚洲第一成人av | 成人福利在线播放 | 欧美黑人疯狂性受xxxxx野外 | 精品国产免费一区二区三区 | 亚洲国产综合网 | 久久两性视频 | www.日韩视频 | 亚洲爱v| youjizzxxxxx| 国产盗摄精品一区二区酒店 | 91极品视频 | 亚洲精品国产一区黑色丝袜 | 国产成人免费看一级大黄 | 日韩福利一区 | 天堂中文字幕在线 | 久久咪咪 | 国产在线观看a | 亚洲国产天堂久久综合 | 姝姝窝人体www聚色窝 | 无毒黄色网址 | 日韩欧美高清dvd碟片 | 四虎永久免费影院 | 欧美影院一区二区三区 | 天天综合网在线 | 欧美三级中文字幕 | 日韩图片区 | 欧美日韩国产免费观看 | 男人日女人免费视频 | 国产免费片 | 久久三级黄色片 | 欧美 日韩 中文字幕 | 青青草原综合久久大伊人精品 | 国产精品视频一二三 | 久久超碰精品 | 四虎影视国产精品 | 福利一区二区 | 国产依人在线 | 色视频免费在线观看 | 欧美精品一区二区三区视频 | 夜夜春av | 亚洲福利视 | www啪啪| 福利视频三区 | 欧美精品在线观看 | 亚洲 丝袜 自拍 清纯 另类 | 亚洲一级免费看 | 色呦呦呦呦 | 欧美激情国产精品免费 | 色哟哟在线观看视频 | 亚洲精品少妇久久久久久 | 欧美精品影院 | 在线男人天堂网 | 亚洲欧美另类激情 | 日韩成人三级 | 91丨porny丨成人蝌蚪 | 国产v在线 | 婷婷激情四射 | 久久久av一区二区三区 | 国产偷怕 | 国产一级片一区二区三区 | 亚洲一区二区高清 | 在线中文字幕亚洲 | 日韩不卡毛片 | 色爽爽爽 | 国产精品二区在线 | 加勒比av在线播放 | 国产自偷自拍视频 | 欧美精品久久久久久 | 激情久久视频 | 亚洲精品国产第一综合99久久 | 久久免费片 | 日韩激情第一页 | 国产精品爽爽久久 | 国产精品亚洲欧美 | 欧美资源在线观看 | 日产精品久久久一区二区 | 在线日韩中文字幕 | 亚洲综合自拍网 | 亚洲视频一区二区在线观看 | 国产又大又黄又爽 | 国产一区欧美一区 | 给我看高清的视频在线观看 | 国产91对白在线播放 | 视频在线观看一区二区三区 | www.av网址| 99爱国产 | 成人一区三区 | 国产精品久久久久精囗交 | 性xx色xx综合久久久xx | 日韩一卡二卡在线 | 特黄av | 日本伊人网 | 欧美影院 | 日韩欧美区 | 亚洲美女网站 | 伊人成年综合网 | 欧美aⅴ在线 | 在哪里看毛片 | 天天干天天操天天爱 | 2020av在线 | 黄色片视频免费在线观看 | 日本不卡一二三区 | 免费av一区二区三区 | 91亚洲欧美激情 | 日韩视频成人 | 瑟瑟视频在线观看 | 亚洲三级免费 | 超碰在线资源 | 亚洲爽爽网 | 天天操人人干 | 亚洲成年 | 日本性视频网站 | 懂色av一区二区三区免费观看 | 中文字幕国产专区 | 久久久久久久久久91 | av中文网站| 天天色小说 | 亚洲乱码国产乱码精品精 | 日韩第三页 | 国产又粗又猛又爽又黄视频 | 精品久久久久久一区二区里番 | 亚洲第一黄色 | 亚州男人的天堂 | 精品不卡一区二区 | 仙踪林少妇高潮在线观看 | 秋霞影院午夜伦 | 亚洲啊v | 成人精品视频在线 | 一区两区小视频 | 亚洲视频导航 | 99热网址 | 在线看a网站 | 色综合社区 | 国产高潮流白浆喷水视频 | 深爱激情五月婷婷 | 午夜黄色影院 | 720url在线观看免费版 | 天天射天天射 | 国产毛片基地 | 日韩簧片 | 99热在线免费| 玩偶游戏在线观看免费 | 在线免费观看黄色 | 韩日午夜在线资源一区二区 | 精品视频一区二区在线观看 | 久久e热 | 天天色天天干天天 | 国产亚洲精品久久久久久移动网络 | 欧美日韩亚洲国产精品 | 日日操影院 | 国产精品久久久久久影院8一贰佰 | 亚洲国产成人精品久久久国产成人 | 国产suv精品一区 | 中日韩av在线 | 日日干日日摸 | 亚色图 | 日本做爰高潮又黄又爽 | 久久老司机精品视频 | 日韩激情四射 | 欧美精品播放 | 99天堂网| www.成人免费视频 | av网在线 | 日韩在线视频不卡 | 在线观看网址av | 亚洲国产片 | 亚洲综合在线观看视频 | 99久久香蕉| 激情另类综合 | 国产成年人免费视频 | 女18毛片| www.在线 | 欧美成网站| www男人天堂 | 日本免费在线观看视频 | 亚洲国产欧美国产综合一区 | 日韩av专区| 青青自拍视频 | 在线免费国产视频 | 国产精品一区不卡 | 深夜福利在线播放 | 九九av在线 | 调教丰满的已婚少妇在线观看 | 国产噜噜噜噜久久久久久久久 | 野花国产精品入口 | 日韩视频欧美视频 | 亚洲一区二区三区四区五区午夜 | 成人夜间视频 | 久久午夜网 | 五月综合久久 | 极品美女开粉嫩精品 | 天堂在线资源网 | 性做爰裸体按摩视频 | 欧美综合在线视频 | 国产精品自在在线午夜出白浆 | 日本久久久久久久久久 | 热久久久久久 | 亚洲激情成人网 | 欧美日韩系列 | 在线观看中文字幕网站 | 91综合在线| 久久久久久蜜桃 | 97看片网| 国产大片网站 | 国产精品午夜一区二区三区视频 | 96av在线 | 久久亚洲成人av | 青青草在线免费观看 | 国产午夜三级一区二区三 | 中文字幕综合网 | 国产婷婷色 | 中文字幕永久免费 | 性色tv| 亚洲午夜久久久久久久久久久 | 亚洲成熟少妇 | 91免费视频播放 | 免费在线观看av网站 | 国产欧美在线观看视频 | 97伊人| 亚洲精品一区二区三区中文字幕 | 天堂资源 | 91激情网站 | 偷偷操不一样的久久 | 91成人在线看 | 色av网 | 88av网站 | 国产精品21区 | 男人的天堂在线观看av | 7777av| 国产乱国产乱300精品 | 337p粉嫩大胆噜噜噜噜69影视 | 国产日韩网站 | 91av在线视频观看 | 成人在线毛片 | 毛片无限看 | 六月色| 成人aaaaa| 免费av观看网站 | 日韩最新网址 | 宅男666在线观看免费网站 | 超碰在线观看97 | 国产操 | 日韩精品久久久久 | 在线观看福利网站 | 夜夜操天天射 | 亚洲国产精品一区二区第一页 | 国产精品一区二区三区在线免费观看 | 亚洲综合清纯唯美 | 午夜宫| 一级国产视频 | av大全在线 | 操老女人视频 | 国产天天骚 | 91激情在线观看 | 日本女人黄色片 | 调教撅屁股啪调教打臀缝av | 97干干| 亚洲精品mv免费网站 | 91亚色| 快射视频在线观看 | 中文字幕在线视频一区 | 午夜少妇福利 | 欧美日韩一区二区三区在线 | 中国一级女人毛片 | 大奶在线播放 | 日韩精品黄 | 欧美成人三区 | 噜噜色av | 奇米网久久| 91精品免费在线观看 | 亚洲综合日韩中文字幕 | 欧美国产日韩综合 | wwwwxxxx国产| 中文字幕精品无 | 99久久婷婷国产综合精品草原 | 天天干天天色天天 | 高清av在线 | av观看网站 | 51国产偷自视频区 | 日本视频一区二区 | 日本一区二区视频在线 | 手机在线免费av | 久久国产a| 天堂二区 | 久久人人爽人人 | 久久久久久国产精品免费播放 | 国产午夜免费福利 | 岛国二区 | 国产又粗又猛又爽又黄视频 | 老色批影视 | 一级免费黄色大片 | 成人综合久久 | 91福利视频在线观看 | 中日精品一色哟哟 | 成人乱人乱一区二区三区 | 国偷自产视频一区二区久 | 成人做爰的视频 | 蜜芽久久 | 色眯眯影视 | 99久久久精品免费观看国产 | 日本欧美在线视频 | 久久不卡影院 | 国产网友自拍 | aa黄色大片 | 亚洲乱码一区二区三区在线观看 | 不卡一区二区在线观看 | 九九爱国产 | 国产综合第一页 | 午夜激情免费视频 | 天天射综合网站 | 久久精品久久久久久久 | 亚洲h视频在线观看 | 校园春色av | 叶爱在线| 日韩精品视频观看 | 一区二区不卡在线 | 国产一区精品在线 | 在线观看黄色免费视频 | 亚洲一线视频 | 亚洲国产精品自在拍在线播放 | 日韩激情在线播放 | 亚洲狼人综合网 | 国产在线国偷精品产拍 | 91精品国产综合久 | 国产精品天天干 | 黄色av免费网站 | 久久免费少妇做爰 | 在线免费一级片 | 久久成人精品 | 中国女人黄色大片 | 白丝av| 日韩欧美中文字幕在线播放 | 色网站入口| 亚洲免费播放 | 国产精品自产拍 | 久草青青草 | 久久精品视频免费观看 | 黄91在线观看| 九九在线精品视频 | 污视频导航 | 伊人99| 国产黄a三级三级看三级 | 中文字幕一区二区三区在线观看 | av在线天堂 | 亚洲欧美另类在线 | 玩偶姐姐在线观看免费 | 国产精品12区 | 亚洲国产精品午夜在线观看 | 欧美一区二区三区四区在线观看 | 久久久久久久久久久网站 | 精品久久久久久亚洲 | 欧洲做受高潮免费看 | 久久毛片网站 | 国产精品久久久久桃色tv | 亚洲成人中文字幕在线 | 欧美成人一二区 | 国产精品二区一区二区aⅴ 免费中文视频 | 国产做a视频 | 91人人视频 | 91精品国产自产91精品 | 午夜不卡在线 | 午夜影院在线视频 | 国产视频在 | 亚洲欧美日韩在线一区二区三区 | 中文字幕欧美色图 | 丁香六月综合激情 | 在线a视频 | 天天爽天天色 | 国产亚洲欧美在线 | 久久久久中文字幕 | 老司机综合网 | 2017狠狠干 | 一级性毛片| 仙踪林少妇高潮在线观看 | 色妻av | 狼人久久 | 亚洲精选一区二区三区 | 日韩白浆 | 四色永久访问 | 欧美性猛交7777777 | 一区二区三区视频网站 | 精品国产www| av在线资源观看 | 最新最近中文字幕 | 男女视频一区 | 色四月| 国产一级一片免费播放 | av观看网址 | 97人人爽人人爽人人爽 | 国产成人在线观看网站 | 亚洲免费看黄 | 亚洲一区二区三区加勒比 | 五月婷婷免费视频 | 久久国产成人 | 久久综合久久88 | 六月丁香久久 | 国产精品老牛视频 | 男人av资源 | av在线一区二区三区 | 亚州欧美在线 | 国产一区二区三区在线免费观看 | 国产亚洲美女精品久久久2020 | www.亚洲在线 | 精品欧美日韩 | 美女av黄| 天堂中文在线免费观看 | 欧美xxxxxhd | 丁香四月婷婷 | 精品二区视频 | 中国毛片a| 国产精品超碰 | 国产又粗又猛又爽又黄视频 | 欧美日韩在线网站 | 亚洲欧美韩日 | 性色一区二区三区 | 中文字幕亚洲视频 | 激情视频网站在线观看 | a国产精品 | 中文字幕专区 | 国产尤物视频在线 | 欧美日韩一区二区三区四区 | 黄色三级在线播放 | 日韩精品中文字幕一区二区 | 伊人剧场 | 成人日韩视频 | 国产免费一级 | 亚洲视频一区在线 | 亚洲一区二区三区乱码aⅴ 最新国产在线拍揄自揄视频 | 日本综合视频 | 中文字幕一区在线观看 | 久草手机在线观看 | 久久久久久久精 | 欧美顶级少妇做爰 | 在线观看一级片 | 亚洲视频在线免费 | 久操免费在线视频 | 自拍色视频 | 国产精品一区二区三区在线播放 | 黄色特级网站 | 久久久精品99 | 亚洲视频 中文字幕 | 自偷自拍亚洲 | 日本毛片在线看 | 亚洲视频一区二区 | 国产精品老女人 | 在线观看黄色免费视频 | 国产精品午夜影院 | 在线播放a | www.97ai.com| av成人在线网站 | 欧美高清成人 | 色91视频| 日本天堂在线视频 | xx视频在线观看 | 中文字幕第一页久久 | 伊人超碰在线 | 国产美女精品aⅴ在线播放 久热国产区二三四 | 亚洲女人毛茸茸 | 日干夜干天天干 | 亚洲精品9999 | 色av免费| 日本熟妇成熟毛茸茸 | 日本成人午夜 | 国产又粗又硬又长又爽的演员 | 国产高清视频在线免费观看 | 夜夜爽夜夜| av免费大全 | 亚洲精品国产精品乱码不99热 | 青青草手机视频在线观看 | 窝窝午夜看片 | 久草一区二区 | 亚洲综合日韩中文字幕 | 亚洲综合成人亚洲 | 欧美黄色性视频 | 91日日| 久久精品女人毛片国产 | 日本免费在线观看 | 久久久久亚洲 | 成人h视频在线 | 曰韩中文字幕 | 自愉自愉亚洲 | 亚洲成人123| 97精品在线| 91久久久久久久久久久久久 | 日日网 | 久久一区 | 成人3d动漫一区二区三区91 | 99久久久| 中文字幕www| 你懂的国产视频 | 日本不卡视频在线观看 | 九九热re| 久久综合精品视频 | 二区三区偷拍浴室洗澡视频 | 99视频一区二区 | 不卡一区在线 | avtt国产| 国产日韩三级 | 免费看黄在线 | 国产私拍 | 成人日韩在线观看 | jizzjizz亚洲| 日韩一区二区在线看 | 欧美挤奶吃奶水xxxxx | 中文字幕视频网站 | av永久免费网站 | 亚洲成av人影院 | 亚洲97视频 | 国产妞干网| 婷婷久久久久久 | 日韩欧美色 | 久久久福利 | 国产精品久久久久久久久久久久久久 | 成人免费看毛片 | 日本天堂网在线 | 精品一区二区三区在线观看 | av免费大片| 久久青青操 | 日韩有码第一页 | 亚洲精品在线播放视频 | 中文字幕欧美视频 | 欧美日在线 | 伊人avav | 久久亚洲99精品2021 | 色婷婷精品视频 | 亚洲xxxxxx| 美日韩一区二区 | 伊人成综合 | 国产主播一区二区 | 福利色播 | 99国产成人精品 | 国产三级第一页 | 91sex国产 | 精品国产乱码久久久久久88av | 超薄肉色丝袜一区二区 | 日韩在线播放一区二区 | 精品美女久久久 | 夜夜天天| 日本在线视频一区二区 | 国产精品欧美久久 | 成人欧美日韩 | 爱搞逼综合网 | 91亚洲精品在线观看 | 精品久久久免费视频 | 国产伊人久久 | 亚洲欧美在线观看视频 | 久久久久久久久97 | 久久一视频 | 国产精品国产三级国产aⅴ 精品欧美久久 | 天天操天天干天天 | 好吊操精品视频 | 免费一级特黄 | 超碰cc| 国产精品高潮呻吟久久久 | 国产日韩欧美精品 | 欧美在线中文 | 色老头一区二区 | 天堂av网址 | 纯爱无遮挡h肉动漫在线播放 | 亚洲美女一区二区三区 | 91视频a | 亚洲欧美日韩色 | 中文字幕 日本 | 欧美人与禽zozzozzo | 国产一级免费av | 国产亚洲欧美精品永久 | 国产小视频在线播放 | 中文字幕在线播放日韩 | 国产免费脚交足视频在线观看 | 免费观看成人毛片 | 免费av一区 | 清纯唯美激情 | 国产伦精品一区 | 久久一精品 | 亚洲天堂日本 | 国产一区二区三区四区在线观看 | 午夜高潮视频 | 一本一本久久a久久精品综合小说 | 9999国产精品 | 神马午夜av| 成人免费毛片观看 | chinese真实伦对白露脸 | 日本91网站 | 亚洲国产成人aⅴ毛片大全密桃 | 久操国产 | a级黄毛片 | 成人a在线 | av青草 | 91国产丝袜播放在线 | 91精品影视| 伊人影视大全 | 在线免费观看一级片 | 日本黄色中文字幕 | av播播 | 午夜无毒不卡 | 国产精品久久久久久模特 | 国产成人专区 | 就操在线 | 91在线无精精品一区二区 | 色一区二区三区四区 | 久久天天躁狠狠躁夜夜97 | 黄色一级视屏 | 欧美黄色短片 | 久色婷婷 | 涩涩涩涩av | 亚洲高清中文字幕 | av网站免费在线播放 | 一区三区在线观看 | 免费不卡毛片 | 成人深夜视频在线观看 | 色wwwwww| 国产精品精品国产 | 国产精品久久久久久久久久精爆 | 日韩人成| 久草免费在线观看 | 一区二区不卡视频在线观看 | 日本久久一区 | 国产69av | 天堂俺去俺来也www久久婷婷 | 国产黄色片av | 亚洲美女性视频 | 亚洲系列| av最新天堂 | 日韩免费一区二区 | 激情视频在线免费观看 | 开心色站 | 日本黄色视| 激情av中文字幕 | 可以在线观看的av网站 | 中文字幕在线不卡视频 | 在线观看的av网址 | 男女无遮挡猛进猛出 | 久久久视屏 | ktv做爰视频一区二区 | 国产美女自拍一区 | 六月婷婷中文字幕 | 亚洲图片自拍偷拍 | 亚洲成年在线 | 狠狠综合 | 欧洲成人免费视频 | 中文字幕一区二区三 | 黄色三级视频 | 九九精品免费 | 亚洲自拍偷拍精品视频 | japanese24hdxxxx日本| 国产精品99蜜臀久久不卡二区 | av免费在线网站 | 日本乱子伦 | xx69欧美| 亚洲一二三四区 | 再深点灬舒服灬太大了快点91 | 免费成人av片 | 亚洲丁香婷婷 | 日韩理论片在线观看 | 久久久精品国产sm调教 | www.久久久.com | 91福利影院| 日韩福利片在线观看 | 免费观看一区二区 | 黄色aa大片| 一级免费看 | 亚州精品视频 | 欧美成人综合 | 国产视频一级 | 欧美a级片在线观看 | 国产精品99久久久久久人 | 一本大道香蕉大a√在线 | 蜜桃av一区二区三区 | 久久无毛 | 在线视频在线 | 国产精品久久久av | 日日夜夜撸撸 | 亚洲精品美女在线观看 | 狠狠干b | 你懂得在线视频 | 爱色av.com | 久久有精品 | 色五婷婷 | 国产精品国产精品国产专区蜜臀ah | 二区三区在线观看 | 性按摩xxxⅹ视频 | 亚洲一卡二卡 | 永久免费看片女女 | 在线免费国产视频 | 欧美一区二区三区精品 | 国产精品123 | 涩涩视频免费看 | youjizz自拍| 亚洲视频男人的天堂 | 日日摸日日 | 日韩一区二区中文字幕 | 在线观看亚洲专区 | 黄色国产网站在线观看 | 99av国产精品欲麻豆 | fc2成人免费人成在线观看播放 | 黄色片在线免费看 | 久久草网站| 一起操17c | 在线免费观看h片 | 国产美女在线精品 | 一级特黄a | 国产精品高潮呻吟 | 日本在线观看视频网站 | 色5566| 欧美v亚洲 | 2018狠狠干| 精品美女久久久 | 这里只有精品在线观看 | 99久久婷婷国产精品综合 | 91本色| 红桃视频国产精品 | 99精品视频免费观看 | 91成人精品一区二区三区四区 | 善良的女邻居在线观看 | 久久99免费 | 免费国产精品视频 | 露脸丨91丨九色露脸 | 婷婷天堂网 | 精品一区二区三区四区五区 | 国产在线观看h | 久久97视频 | 免费日b视频 | 欧美激情一级 | 日韩有码一区 | 日韩射 | 黄色网址你懂的 | 色一情一伦一子一伦一区 | 香蕉手机网| 日韩精品1 | 日韩精品一区二区三区免费视频 | 久色影视 | 在线成人看片 | 久久久婷 | 特黄视频免费看 | 黄色aaa视频 | 国产精品系列视频 | 欧美久久久久久久久 | 午夜国产一区 | 亚洲天堂成人网 | 欧美草草 | www.99cao| 天天摸日日摸 | 精品国产91乱码一区二区三区 | 尤物视频免费在线观看 | 在线欧美二区 | 一级黄色大片视频 | 美女视频在线免费观看 | 国产午夜三级 | 久久99在线 | 日本不卡高清视频 | 在线观看mv的中文字幕网站 | 人人模人人干 | 久操五月天 | 中文字幕av免费 | 91精彩视频在线观看 | 伊人影院在线观看视频 | 99国产在线观看 | 免费黄色在线视频 | www.激情网.com | 爱色av·com| 国产精品久久久久久免费播放 | 国产麻豆一区二区三区在线观看 | 亚洲欧美日韩国产一区二区三区 | 影音先锋亚洲天堂 | 久久av网| 成人福利在线播放 | 亚洲天堂2014 | 热99精品| 91在线视频国产 | 国产福利91精品 | 欧美日韩一区二区三区四区 | 国产网友自拍 | 久久国产精品波多野结衣 | 韩日欧美| 日韩欧美中文字幕一区 | 日韩成人av一区二区 | 成人久久久久久久 | 激情午夜影院 | 成人靠逼视频 | av午夜影院 | 国语对白清晰刺激对白 | 国产做受网站 | 天堂中文在线8 | 精品久久在线观看 | 在线男人天堂 | 亚洲 欧美 激情 另类 | av毛片在线播放 | 精品视频九九 | 久久久96 | 国产性一乱一性一伧一色 | 久久aaa| 欧美日韩色图 | 欧美日本精品 | 久久三级网 | 成人精品自拍 | 美女av网| 亚洲区欧美区 | 久久综合五月 | 91高潮大合集爽到抽搐 | 国产一区2| 国产精品成人久久久久久久 | 国产免费福利在线观看 | 欧美色图在线视频 | 伊人三区| 亚洲日本欧美日韩高观看 | 亚洲伦理久久 | 日韩精品久久久久久久 | 国产精品欧美一区二区三区 | 亚洲欧美在线看 | 欧美在线观看一区二区三区 | 日韩视频播放 | www国产亚洲精品 | 欧美激情精品久久 | 自拍偷拍2019 | 精品视频网站 | 91精品视频免费在线观看 | 自拍偷拍视频亚洲 | 性感美女一级片 | 亚洲精品午夜国产va久久成人 | 久久99这里只有精品 | 久久欧美视频 | 日本少妇网站 | 女人十八岁毛片 | 久久久久久久久97 | 亚洲一区二区中文字幕 | 国产字幕在线观看 | 狠狠干天天干 | 色老头av | 亚洲精品1区2区3区 国产免费一级视频 | 噜噜色.com|