CBLT — безопасный, быстрый и минималистичный веб-сервер на языке программирования Rust

fc177f81cc5d531f866248b02e04f217.png

Для изучения нового языка программирования я использую следующий подход. Сначала я читаю учебник по этому языку программирования, в котором объясняются синтаксис, идиомы, философия и принципы работы языка. После этого я пишу небольшой пет-проект на этом языке программирования. На пет-проекте я немного практикуюсь с новым языком, с его стандартными библиотеками и популярными фреймворками.

Чтобы погрузиться сильнее в язык, вместо пет-проекта я начинаю писать свои библиотеки для работы с базами данных (ORM), JSON, акторами, MVC веб-фреймворком, логированием и т.д. Библиотеки, которые вряд ли будут кому-то нужны, но они помогут мне лучше понять язык программирования. На удивление, с языком Rust я добрался до написания своего веб-сервера. Раньше такого не было. Думаю, это из-за того, что Rust — это язык системного программирования и грех на нём не попробовать заняться оптимизацией перформанса.

В итоге я столкнулся с тем, что Rust не имеет аналогов Nginx, Lighttpd, Caddy, HAProxy, Apache, Tomcat, Jetty и т.д. Все эти веб-сервера написаны на C, Go, Java и т.д. Имеются только веб-фреймворки: Actix, Axum, Rocket, Hyper и т.д.

В целом я прикинул, что обычно я использую Nginx для следующих целей:

1. TLS для доменов

2. Проксирование запросов на бэкэнд

3. Раздача статических файлов

В итоге решил написать свою реализацию веб-сервера на Rust.

Сервер поддерживает конфигурационный файл в формате KDL Document Language. Вот примеры «Cbltfile» конфигурационного файла для веб-сервера Cblt:

Файл сервер

"*:80" {
    root "*" "/path/to/folder"
    file_server
}

Файл сервер & Проксирование

"127.0.0.1:8080" {
    reverse_proxy "/test-api/*" "http://10.8.0.3:80"
    root "*" "/path/to/folder"
    file_server
}

TLS поддержка

"example.com" {
    root "*" "/path/to/folder"
    file_server
    tls "/path/to/your/domain.crt" "/path/to/your/domain.key"
}

Сейчас Cblt веб-сервер можно запустить двумя способами: через Cargo или Docker.

Cargo

cargo run --release

Docker

docker build -t cblt:0.0.3 .
docker run -d -p 80:80 --restart unless-stopped --name cblt cblt:0.0.3

На текущий момент я добился приемлемой скорости работы для проксирования статических файлов.

Провёл тест с Apache Benchmark (ab) для 300 запросов с 100 одновременными соединениями. Загрузка изображения размером 5 МБ с example.com/logo_huge.png.

ab -c 100 -n 300 http://example.com/logo_huge.png

Percent

Cblt

Nginx

Caddy

50%

1956

1941

1768

75%

2101

2065

1849

100%

2711

2360

2270

Cblt

igumn@lenovo MINGW64 ~/cblt (main)
$ docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS                 PORTS                                                       NAMES
0589d8f26d91   cblt:0.0.1            "./cblt"                 2 minutes ago   Up 2 minutes           0.0.0.0:80->80/tcp                                          cblt

igumn@lenovo MINGW64 ~/cblt (main)
$ ab -c 100 -n 300 http://example.com/logo_huge.png
This is ApacheBench, Version 2.3 <$Revision: 1913912 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking example.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests


Server Software:
Server Hostname:        example.com
Server Port:            80

Document Path:          /logo_huge.png
Document Length:        5122441 bytes

Concurrency Level:      100
Time taken for tests:   6.020 seconds
Complete requests:      300
Failed requests:        0
Total transferred:      1536745500 bytes
HTML transferred:       1536732300 bytes
Requests per second:    49.83 [#/sec] (mean)
Time per request:       2006.721 [ms] (mean)
Time per request:       20.067 [ms] (mean, across all concurrent requests)
Transfer rate:          249283.62 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       2
Processing:  1293 1926 262.3   1956    2711
Waiting:        1  118 139.1     63     645
Total:       1293 1926 262.3   1956    2711

Percentage of the requests served within a certain time (ms)
  50%   1956
  66%   2027
  75%   2101
  80%   2127
  90%   2213
  95%   2394
  98%   2544
  99%   2597
 100%   2711 (longest request)

Nginx

igumn@lenovo MINGW64 ~/cblt/benchmark/nginx (main)
$ docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED         STATUS                  PORTS                                                       NAMES
37fbf1dac42b   nginx_srv             "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes            0.0.0.0:80->80/tcp                                          nginx_srv

igumn@lenovo MINGW64 ~/cblt/benchmark/nginx (main)
$ ab -c 100 -n 300 http://example.com/logo_huge.png
This is ApacheBench, Version 2.3 <$Revision: 1913912 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking example.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests


Server Software:        nginx/1.27.2
Server Hostname:        example.com
Server Port:            80

Document Path:          /logo_huge.png
Document Length:        5122441 bytes

Concurrency Level:      100
Time taken for tests:   6.043 seconds
Complete requests:      300
Failed requests:        0
Total transferred:      1536804300 bytes
HTML transferred:       1536732300 bytes
Requests per second:    49.65 [#/sec] (mean)
Time per request:       2014.267 [ms] (mean)
Time per request:       20.143 [ms] (mean, across all concurrent requests)
Transfer rate:          248359.28 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       2
Processing:  1387 1940 168.4   1941    2360
Waiting:        1  115  84.5     98     301
Total:       1387 1940 168.4   1941    2360

Percentage of the requests served within a certain time (ms)
  50%   1941
  66%   2024
  75%   2065
  80%   2088
  90%   2152
  95%   2201
  98%   2263
  99%   2317
 100%   2360 (longest request)

Caddy

igumn@lenovo MINGW64 ~/cblt (main)
$ ab -c 100 -n 300 http://example.com/logo_huge.png
This is ApacheBench, Version 2.3 <$Revision: 1913912 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking example.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Finished 300 requests


Server Software:        Caddy
Server Hostname:        example.com
Server Port:            80

Document Path:          /logo_huge.png
Document Length:        5122441 bytes

Concurrency Level:      100
Time taken for tests:   5.440 seconds
Complete requests:      300
Failed requests:        0
Total transferred:      1536804000 bytes
HTML transferred:       1536732300 bytes
Requests per second:    55.14 [#/sec] (mean)
Time per request:       1813.469 [ms] (mean)
Time per request:       18.135 [ms] (mean, across all concurrent requests)
Transfer rate:          275858.99 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.3      0       2
Processing:  1264 1749 191.1   1767    2270
Waiting:        1   96 104.7     67     467
Total:       1265 1749 191.1   1768    2270

Percentage of the requests served within a certain time (ms)
  50%   1768
  66%   1821
  75%   1849
  80%   1877
  90%   1955
  95%   2152
  98%   2226
  99%   2241
 100%   2270 (longest request)

В планах также провести тесты для проксирования бэкенда, в общем reverse_proxy на производительность не тестировал еще.

Может в этот раз мой мини-проект кого-то заинтересует? И это увлечение вырастет в что-то большее?

Если интересно глянуть код или поконтрибутить, вот ссылка на репозиторий: https://github.com/evgenyigumnov/cblt

© Habrahabr.ru