hisco / http2-client

Transparently make http request to both http1 / http2 server.
https://www.npmjs.com/package/http2-client
MIT License
33 stars 14 forks source link

Performance degrade from https #14

Closed arve0 closed 5 years ago

arve0 commented 5 years ago

Expected: http2-client should give a performance gain to https, as requests can multiplex. Actual: https consistently outperform http2-client, often by a factor of >2.

$ node test-http-speed.js 
..........
https: 543.7 mean, 396 min, 900 max
http2: 1172.1 mean, 944 min, 1495 max
$ node test-http-speed.js 
..........
https: 609 mean, 401 min, 2029 max
http2: 1589.4 mean, 1027 min, 4226 max

test-http-speed.js:

const { get: httpsGet } = require('https')
const { get: http2Get } = require('http2-client')

main().catch(err => console.error(err))

async function main() {
  require('events').defaultMaxListeners = 50; // avoid MaxListenersExceededWarning

  let httpsResults = []
  let http2Results = []

  for (let i = 0; i < 10; i++) {
    let start = Date.now()
    await do50RequestsInParalell(httpsGet)
    httpsResults.push(Date.now() - start)

    start = Date.now()
    await do50RequestsInParalell(http2Get)
    http2Results.push(Date.now() - start)

    process.stdout.write('.')
  }

  console.log()
  console.log(`https: ${meanMaxMin(httpsResults)}`)
  console.log(`http2: ${meanMaxMin(http2Results)}`)
}

function do50RequestsInParalell(get) {
  const urls = [...Array(50).fill(0).map((_, i) => `https://nvdbcache.geodataonline.no/arcgis/rest/services/Trafikkportalen/GeocacheTrafikkJPG/MapServer/tile/10/369/${465+i}`)]

  const promisifiedGet = promisify(get)
  const promises = urls.map(url => promisifiedGet(url))
  return Promise.all(promises)
}

function promisify(get) {
  return function (href) {
    return new Promise((resolve, reject) => {
      const req = get(href)
      req.end()

      req.on('error', (error) => {
        reject(new Error(`Unable to get ${href}: ${error}`))
      })

      req.on('response', (response) => {
        if (response.statusCode !== 200) {
          return reject(new Error(`Got status code ${response.statusCode} for request ${href}.`))
        }
        let data = ''
        response.on('data', chunk => data += chunk)
        response.on('end', () => resolve(data))
      })
    })
  }
}

function meanMaxMin(times) {
  let sum = times.reduce((s, n) => s + n, 0)
  let mean = sum / times.length
  let min = Math.min(...times);
  let max = Math.max(...times);

  return `${mean} mean, ${min} min, ${max} max`
}
hisco commented 5 years ago

Hey, Thank you for the test, I was able to confirm, that the problem is not related to http2-client. I did the same test directly with http2 core library without http2-client and the results were similar, against http2 (well, depends...).

Http2 has a lot of benefits over https, one of these is indeed multiplexing.

When I recreated your test I changed the test against small | mid | big objects to be downloaded. The results show:

Therefore, the problem relates to when to multiplex over the same connection and not directly to http2 vs https or https vs http2-client. For example, one can choose to open multiple http2 connection and multiplex with each one...

Thanks!

arve0 commented 5 years ago

Thanks 👍 Closing.