Кто доставляет большее количество одновременных запросов?

Мы разрабатываем что-то вроде рекламного прокси или Google Ad Buffer. Сервис просто перенаправляет рекламные HTTP-запросы на сервер SSP. Для этого необходимо создавать множество HTTP-запросов с минимальными аппаратными ресурсами. Поэтому мы решили провести исследование и сравнить языки программирования с виртуальными машинами и скомпилировали их.

Мы хорошо знакомы с технологиями NodeJS и JavaScript. Поэтому мы начали тестировать HTTP-соединение с движком V8. Конечно, мы не начинали с нуля, мы использовали пакет fastify. На самом деле он основан на HTTP-пакете NodeJS. Таким образом, в нижней части стека программного обеспечения находится скомпилированный HTTP-сервер низкого уровня. Но в любом случае есть крошечный слой, работающий под V8. Вопрос в том, как этот слой замедляет выполнение.

NodeJS

Сценарий довольно прост.

const fastify = require(“fastify”)({
 logger: false,
});
fastify.get(“/fillbuffer”, async (request, reply) => {
 reply.type(“application/json”).code(200);
 return {
 result: `{result: “Hello world”}`,
 };
});
fastify.listen(3008, (err, address) => {
 if (err) throw err;
});

Для тестирования я использовал инструмент ApacheBench (ab). Давайте пропустим полную спецификацию оборудования. Могу лишь сказать, что использовал процессор I7–8550U.

ab -n 1000000 -c 100 localhost:3008/fillbuffer
Requests per second: 12925.33 [#/sec] (mean)
Time per request: 7.737 [ms] (mean)
Time per request: 0.077 [ms] (mean, across all concurrent requests)
Percentage of the requests served within a certain time (ms)
 50% 8
 66% 8
 75% 8
 80% 8
 90% 9
 95% 10
 98% 12
 99% 13
 100% 106 (longest request)

Давайте попробуем больше одновременных подключений.

ab -n 1000000 -c 500 localhost:3008/fillbuffer
Results:
Requests per second: 9673.37 [#/sec] (mean)
Time per request: 51.688 [ms] (mean)
Time per request: 0.103 [ms] (mean, across all concurrent request
Percentage of the requests served within a certain time (ms)
 50% 48
 66% 49
 75% 50
 80% 51
 90% 58
 95% 79
 98% 137
 99% 156
 100% 286 (longest request)

Все идет нормально. 500 одновременных подключений превышают лимит ЦП, и решение Node начинает испытывать трудности, но давайте получим цифры Go.

Go

Сценарий немного длиннее, но все же короткий.

package main
import (
 “encoding/json”
 “fmt”
 “log”
 “github.com/valyala/fasthttp”
)
var (
 addr = “:3008”
 strContentType = []byte(“Content-Type”)
 strApplicationJSON = []byte(“application/json”)
 httpClient *fasthttp.Client
)
func main() {
 fmt.Println(“Starting server…”)
 h := requestHandler
 h = fasthttp.CompressHandler(h)
httpClient = &fasthttp.Client{
 MaxConnsPerHost: 2048,
 }
if err := fasthttp.ListenAndServe(addr, h); err != nil {
 log.Fatalf(“Error in ListenAndServe: %s”, err)
 }
}
func requestHandler(ctx *fasthttp.RequestCtx) {
 if string(ctx.Method()) == “GET” {
 switch string(ctx.Path()) {
 case “/fillbuffer”:
 ctx.Response.Header.SetCanonical(strContentType, strApplicationJSON)
 ctx.Response.SetStatusCode(200)
 response := map[string]string{“result”: fmt.Sprintf(“hello world”)}
 if err := json.NewEncoder(ctx).Encode(response); err != nil {
 log.Fatal(err)
 }
 }
 }
}

Как видите, я решил использовать fasthttp в качестве HTTP-сервера. Сервер не основан на какой-либо HTTP-библиотеке. Так что это действительно чистая реализация протокола HTTP. Давайте посмотрим результат для 100 одновременных запросов.

ab -n 1000000 -c 100 localhost:3008/fillbuffer
Requests per second: 15847.80 [#/sec] (mean)
Time per request: 6.310 [ms] (mean)
Time per request: 0.063 [ms] (mean, across all concurrent requests)
Percentage of the requests served within a certain time (ms)
 50% 6
 66% 7
 75% 7
 80% 7
 90% 7
 95% 7
 98% 8
 99% 8
 100% 18 (longest request)

Что ж, цифры действительно велики по сравнению с решением NodeJS. Особенно запросы, обслуживаемые в течение определенного времени. Он почти плоский. Начнем последний тест.

ab -n 1000000 -c 500 localhost:3008/fillbuffer
Requests per second: 14682.27 [#/sec] (mean)
Time per request: 34.055 [ms] (mean)
Time per request: 0.068 [ms] (mean, across all concurrent requests)
Percentage of the requests served within a certain time (ms)
 50% 34
 66% 36
 75% 37
 80% 37
 90% 39
 95% 40
 98% 41
 99% 41
 100% 62 (longest request)

Заключение

Как видите, время обслуживания решения Go по-прежнему не меняется. Похоже, что еще есть место для получения большего количества одновременных запросов, но давайте просто сравним основные цифры.

Go — единственный победитель, особенно с большим количеством одновременных запросов.

Так что крошечный слой под двигателем V8 не такой уж и крошечный. Со 100 одновременными запросами доставляйте на 18 % больше запросов. 500 одновременных запросов увеличили прибыль до более чем 34%.