Простой http сервер на nodejs: response time 1 милисекунда это много?
господа. Написал простейший http сервер на ноде (v15.11) на fastify (v3.20.2) (как одной из самых производительных библиотек на ноде для этих целей). И результаты меня несколько удручают:
- при отправке 1 запроса время ответа +/- 1 милисекунда
- при отправке 10 одновременных запросов среднее время ответа = 5,6 милисекунд
Min time: 4 msec 5 µsec 800 nsec Max time: 5 msec 913 µsec 200 nsec Avg time: 5 msec 560 µsec 150 nsec
- при отправке 100 одновременных запросов среднее время ответа = 41 милисекунд
Min time: 21 msec 212 µsec 300 nsec Max time: 41 msec 570 µsec 300 nsec Avg time: 40 msec 918 µsec 113 nsec
Как мне кажется для сервера который ничего не делает это ОЧЕНЬ большие тайминги. Может я что-то не правильно делаю? Какие будут мысли на этот счет?
Исходный код:
// server.js
const server = require('fastify')({ keepAliveTimeout: 20000 })
server.get('/ping', async (req, res) => {
res.code(200).send('')
})
server.listen(3000)
// benchmark.js
const http = require('http')
const ITER = 100
const agent = new http.Agent({ keepAlive: true, maxFreeSockets: 500 })
const { hrtime } = process
console.log('Starting benchmark...')
const hrStart = hrtime.bigint()
makeRequest(0) // make 1 request for a handshake
.then(() => start(ITER)) // start benchmark
.then(results => { // display benchmark results
console.log(`Benchmark finished in ${stringify(hrtime.bigint() - hrStart)}`)
const min = results.reduce((res, v) => Math.min(res, v), Number.MAX_SAFE_INTEGER)
const max = results.reduce((res, v) => Math.max(res, v), 0)
const total = results.reduce((res, v) => res + v, 0)
console.log(`Min time: ${stringify(min)}`)
console.log(`Max time: ${stringify(max)}`)
console.log(`Avg time: ${stringify(Math.round(total / ITER))}`)
})
async function start(iterCount) {
const results = new Array(ITER) // an array of elapsed time for each request
for(let nIter = 0; nIter < iterCount; ++nIter) {
results[nIter] = makeRequest(nIter).then(({ iter, elapsed }) => results[iter] = elapsed)
}
return Promise.all(results).then(() => results)
}
function makeRequest(iter) {
return new Promise(resolve => {
const start = hrtime.bigint()
http.get('http://localhost:3000/ping', { agent } , res => {
res.resume().on('end', () => resolve({ iter, elapsed: Number(hrtime.bigint() - start) }))
})
})
}
function stringify(time) {
time = Number(time)
if(Number.isNaN(time)) {
throw new Error(`time is not a number`)
}
const nsec = time % 1000
time = (time - nsec) / 1000
const mcsec = time % 1000
time = (time - mcsec) / 1000
const msec = time
return `${msec} msec ${mcsec} µsec ${nsec} nsec`
}
Ответы (1 шт):
Александр,
позвольте для начала Вас похвалить за отлично проделанную работу!
Вы не только написали сервер, померили, задумались о скорости, задали вопрос - вы еще, фактически, дали отвечающим свою методику измерений.
Теперь - по сути: ну вроде 1 мс (то есть одна тысячная доля секунды) на запрос - это неплохо. Ведь там огромные накладные расходы: открывается tcp коннекшн, происходит начальный хэндшейкинг, потом начинается запихивание в этот коннекшн http заголовков с одной стороны и их обработка на другой стороне. Целое дело!
При этом нода сторит "машину состояний" для того, чтобы по завершеню получения запроса запустить тот код, который будет отвечать. а нода - это интерпретатор, хоть и очень быстрый.
А вообще вопрос интересный. Если он останется открытым и актуальным - я могу попробовать через день-два померить время чисто открытия tcp коннекшна и время такого же сервера, который хостит статику, но написанного на C++.
Еще, говорят, в Golang довольно популярны пузомерки на самый быстрый http ответ. Хотя, если нужна именно скорость - то, может, просто не использовать http? Например, многие биржевые протоколы основаны чисто на udp пакетах.