Квест → Как хакнуть форму
Прошли: 77
Подключение к Redis через Unix-сокеты, как правило, быстрее, чем подключение через TCP/IP.
Это потому, что в целом, Unix-сокеты имеют гораздо меньше накладных расходов. Они, по сути, просто файлы, которые могут читать Unix-подобные системы. Недостатком этого является то, что возможны только локальные подключения.
Имея 10М+ скачиваний, redis
является четвертым среди самых скачиваемых Docker-образов, и большинство систем настраивают подключение к Redis через Docker'овский встроеный сетевой TCP-мост.
Docker'овский собственный сетевой TCP-мост — это, конечно, хорошо и прекрасно, но не хотели бы вы выжать еще немного скорости?
Конечно, прирост небольшой, но нельзя сказать, что несущественный. Вот некоторые грубые сравнения из моих тестов:
time redis-benchmark -h 127.0.0.1 -p 6379
48.92 real 36.43 user 11.85 sys
time redis-benchmark -s /tmp/redis.sock
42.90 real 34.96 user 7.42 sys
Разница в скорости в 14% существенна, и мы должны воспользоваться этим, даже при использовании Docker.
Но подождите минуту, разве Docker-контейнеры не самодостаточны и разделены? Как мы можем использовать Unix-сокеты в такой среде?
Ответ заключается в том, что мы должны воспользоваться общим (расшариваемым) дисковым пространством Docker'а.
Допустим у нас есть PHP-контейнер, в котором работает наше приложение с Redis-клиентом, который подключается к контейнеру Redis'а.
Нам нужно создать отдельный контейнер. Он будет базой для дискового пространства, которое мы собираемся расшарить PHP и Redis'у.
Давайте подключим его в /tmp/docker/
, и скажем Redis'у, чтобы он располагал свой файл redis.sock
в этой папке.
Кроме того, эта общая папка может также быть использована для любых других файлов или данных, которые вы хотите расшарить между контейнерами.
Мы можем использовать образ busybox
для этого, потому что этот контейнер на самом деле не будет делать ничего, кроме как хранить файлы.
В конфигах контейнеров для PHP и Redis мы используем настройку volumes_from
для подключения одного и того дискового пространства из busybox в наши контейнеры.
Так что наш файл docker-compose.yml
будет выглядеть примерно так:
services: tmp: image: busybox command: chmod -R 777 /tmp/docker volumes: - /tmp/docker/ php: image: php // other app bootstrapping options volumes_from: - tmp redis: image: redis command: redis-server /etc/redis.conf volumes: - /redis/redis.conf:/etc/redis.conf volumes_from: - tmp
Здесь важными моментами являются сервис tmp
и настройки volumes_from
в других контейнерах. Это просто наглядный пример и, конечно, предполагается, что вы правильно всё пропишите, включая redis.conf
в контейнере c Redis'ом.
Вы должны убедиться, что Redis запускается с конфигом redis.conf
, т.к. нам нужно явно указать Redis создать сокет-файл, который будет доступен для подключения.
В вашем redis.conf
добавьте следующие строки:
unixsocket /tmp/docker/redis.sock unixsocketperm 777
Эти настройки укажут Redis'у создать при запуске /tmp/docker/redis.sock
и дать ему права 777
. Я не сомневаюсь, что ‘777’ будет спорным, но это на ваше усмотрение. Я считаю, что в изолированной среде Docker'а это совершенно безопасно.
С учётом того, как мы настроили расшаренные папки, то после запуска этих контейнеров, /tmp/docker/
будет доступна и на PHP-контейнере, и мы сможем достучаться до неё из кода нашего приложения.
Далее в вашем PHP-приложении подключитесь к Redis через Unix-сокет. Если вы используете популярный пакет Predis, вы можете сделать это так:
$client = new Predis\Client([ 'scheme' => 'unix', 'path' => '/tmp/docker/redis.sock', ]);
Если вы используете Laravel, вы должны будете сделать чуть больше, т.к. настройки по умолчанию предполагают, что вы хотите подключаться к Redis через TCP. В файле .env
, добавьте следующее:
REDIS_SCHEME=unix REDIS_PATH=/tmp/docker/redis.sock
Затем в config/database.php
измените секцию 'redis'
так, чтобы она выглядела следующим образом:
'redis' => [ 'client' => 'predis', 'default' => [ // 'host' => env('REDIS_HOST', '127.0.0.1'), // 'password' => env('REDIS_PASSWORD', null), // 'port' => env('REDIS_PORT', 6379), 'scheme' => env('REDIS_SCHEME'), 'path' => env('REDIS_PATH'), 'database' => 0, ], ],
Я сделал снова тест, но на этот раз настроив одно из моих приложений, которое работает на PHP и в значительной степени опирается на Redis.
Я был немного озорной ( прим. пер.: это дословно :) ) и запустил их вживую на моем боевом сервере, но я считаю, что это отличный стрес-тест на то, как мое боевое окружение держит нагрузку.
time --verbose docker-compose exec redis redis-benchmark -h 127.0.0.1 -p 6379
Command being timed: “docker-compose exec redis redis-benchmark -h 127.0.0.1 -p 6379” User time (seconds): 0.35 System time (seconds): 0.09 Percent of CPU this job got: 0% Elapsed (wall clock) time (h:mm:ss or m:ss): 1:21.64 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 25344 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 31 Minor (reclaiming a frame) page faults: 14686 Voluntary context switches: 1229 Involuntary context switches: 293 Swaps: 0 File system inputs: 22640 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0
time --verbose docker-compose exec redis redis-benchmark -s /tmp/docker/redis.sock
Command being timed: “docker-compose exec redis-server redis-benchmark -s /tmp/docker/redis.sock” User time (seconds): 0.37 System time (seconds): 0.04 Percent of CPU this job got: 0% Elapsed (wall clock) time (h:mm:ss or m:ss): 1:06.77 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 25232 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 46 Minor (reclaiming a frame) page faults: 14708 Voluntary context switches: 1167 Involuntary context switches: 294 Swaps: 0 File system inputs: 21024 File system outputs: 0 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0
TCP-соединение выполняет бенчмарк на 22% дольше, чем тест с использованием Unix-сокетов.
Я надеюсь, что этот пост поможет вам. Если у вас есть какие-либо комментарии не забудьте поделиться свими мыслями. Вопросы по материалу также приветствуются.