DEV Community

x1957
x1957

Posted on

1

nodejs event loop

最近遇到个问题,在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);
    });
}

Image of Datadog

How to Diagram Your Cloud Architecture

Cloud architecture diagrams provide critical visibility into the resources in your environment and how they’re connected. In our latest eBook, AWS Solution Architects Jason Mimick and James Wenzel walk through best practices on how to build effective and professional diagrams.

Download the Free eBook

Top comments (0)

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more