loading...

nodejs event loop

x1957 profile image x1957 ・2 min read

最近遇到个问题,在for loop里面调用http.get请求,测试发现,会慢个几ms然后请求才会发出去,代码如下:


async function run() {
    for (let i = 0; i < 100; i++) {
        for (let j = 0; j < 40; j++) {
            let local_ts = Date.now();
            let cid = 'cid_' + i * 1000 + j;
            let url = `http://127.0.0.1:8890/ts?id=${cid}&ts=${local_ts}`;
            http.get(url).then(ret => {
                let server_ts = ret.split(' ')[1];
                let finish_ts = Date.now();
                console.log(cid, ret, 'elapsed', finish_ts - local_ts, 'server_diff', server_ts - local_ts, 'finish_diff', finish_ts - server_ts);
            });
            console.log(cid, 'fired', local_ts, Date.now());
        }
        await Sleep(3000);
    }
}

run();

调用的是个http服务,传本地的timestamp给他,他返回server的timestamp,方便我们计算网络的latency。

这时候就非常疑惑了,为什么server收到我们发送的请求都要6-10ms,这中间网络肯定没这么慢。

怀疑是event loop的锅,首先看了下http的实现,在最后会调用一个process.nextTick

libuv event loop

event loop里面分了很多个loop,代码可以参考

r = uv__loop_alive(loop);
  if (!r)
    uv__update_time(loop);

while (r != 0 && loop->stop_flag == 0) {
    uv__update_time(loop);
    uv__run_timers(loop);
    ran_pending = uv__run_pending(loop);
    uv__run_idle(loop);
    uv__run_prepare(loop);

    timeout = 0;
    if ((mode == UV_RUN_ONCE && !ran_pending) || mode == UV_RUN_DEFAULT)
      timeout = uv_backend_timeout(loop);

    uv__io_poll(loop, timeout);
    uv__run_check(loop);
    uv__run_closing_handles(loop);

    if (mode == UV_RUN_ONCE) {
      uv__update_time(loop);
      uv__run_timers(loop);
    }

    r = uv__loop_alive(loop);
    if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)
      break;
}

而process.nextTick是在这里面任意一个loop结束后就执行的,所以我们的请求其实都堆积到了sleep那儿,因为http get都在for loop里面,所以这里我们如果要立马执行http get,需要切出我们的loop回到event loop. 在循环立马加入一个await Sleep(0)


async function run() {
    for (let i = 0; i < 100; i++) {
        for (let j = 0; j < 40; j++) {
            let local_ts = Date.now();
            let cid = 'cid_' + i * 1000 + j;
            let url = `http://127.0.0.1:8890/ts?id=${cid}&ts=${local_ts}`;
            http.get(url).then(ret => {
                let server_ts = ret.split(' ')[1];
                let finish_ts = Date.now();
                console.log(cid, ret, 'elapsed', finish_ts - local_ts, 'server_diff', server_ts - local_ts, 'finish_diff', finish_ts - server_ts);
            });
            console.log(cid, 'fired', local_ts, Date.now());
            await Sleep(0);
        }
        await Sleep(3000);
    }
}

run();

这时候堆积之后再请求的问题就解决了,不过问题还是有,一毫秒左右才一个请求,感觉太慢。

PS. Sleep代码

const Promise = require('bluebird');
module.exports = function(duration) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(null);
        }, duration);
    });
}

Posted on by:

Discussion

pic
Editor guide