Що таке Unix Domain Socket: секретний тунель у Linux
У нашій подорожі світом сокетів ми почали з “верхнього поверху” — WebSocket у браузері, потім спустилися до фундаменту інтернету — TCP/IP сокетів. Але є ще один, особливий вид сокетів, який живе глибоко в надрах вашого сервера і про який часто забувають.
Ви, напевно, бачили у конфігах Nginx або MySQL дивні шляхи, що закінчуються на .sock, наприклад, /var/run/php/php8.2-fpm.sock. Це не картинка, не текстовий файл і не папка. Це — Unix Domain Socket (UDS). У цій статті я розповім, чому ці “файли” є секретною зброєю для прискорення локальної комунікації.
Аналогія: Пошта проти Записки
Щоб зрозуміти різницю між TCP-сокетом і Unix-сокетом, уявімо офіс.
- TCP/IP Сокет (127.0.0.1:80): Ви хочете передати документ колезі, який сидить за сусіднім столом. Ви кладете документ у конверт, пишете адресу офісу, клеїте марку, кидаєте в поштову скриньку. Пошта сортує його і приносить назад у ваш офіс вашому колезі. Це надійно, але безглуздо довго, якщо ви в одній кімнаті.
- Unix Сокет (/tmp/mysql.sock): Ви просто передаєте документ колезі прямо в руки. Жодних конвертів, адрес, марок і поштарів.
Як це працює технічно?
Unix-сокет — це стандартний механізм міжпроцесної взаємодії (IPC) в POSIX-сумісних системах (Linux, macOS). На відміну від мережевих сокетів, які використовують IP-адреси та порти, Unix-сокети використовують файлову систему як адресний простір.
Ось чому вони швидші:
- Немає мережевого оверхеду: Ядру ОС не потрібно створювати TCP-пакети, рахувати контрольні суми, керувати порядком пакетів та підтвердженням доставки (ACK).
- Пряме копіювання пам’яті: Дані просто копіюються з буфера однієї програми в буфер іншої через ядро.
- Менше перемикань контексту: Процесор виконує менше зайвих операцій.
Безпека: права доступу як у файлів
Це одна з головних переваг. Оскільки Unix-сокет відображається як файл у файловій системі, до нього застосовуються стандартні права доступу Linux (chmod, chown).
Наприклад, якщо у вас є файл сокета бази даних /tmp/mysql.sock, ви можете налаштувати права так, що тільки користувач www-data (ваш веб-сервер) зможе в нього писати. Будь-який інший користувач на сервері навіть не зможе спробувати підключитися до бази. З TCP-портом (наприклад, 3306) це зробити складніше — він відкритий для всіх на localhost.
Практичне застосування: де ми це використовуємо?
1. Nginx + PHP-FPM
Це класика. У моїй статті про FastCGI ми налаштовували зв’язок між веб-сервером і PHP. Ви можете зробити це двома способами:
| Метод | Конфігурація Nginx | Коли використовувати |
|---|---|---|
| TCP Сокет | fastcgi_pass 127.0.0.1:9000; |
Якщо Nginx і PHP-FPM на різних серверах (розподілена архітектура). |
| Unix Сокет | fastcgi_pass unix:/var/run/php/php-fpm.sock; |
Якщо вони на одному сервері. Дає приріст продуктивності. |
2. Docker Daemon
Ви коли-небудь замислювалися, як Docker CLI спілкується з самим Docker двигуном? За замовчуванням це відбувається через Unix-сокет /var/run/docker.sock. Саме тому, щоб запустити Docker без sudo, вам потрібно додати користувача в групу, яка має права на цей файл-сокет.
3. Бази даних (MySQL, PostgreSQL, Redis)
Якщо ваш сайт підключається до бази даних на тому ж сервері (“localhost“), використання сокета (localhost:/tmp/mysql.sock) буде швидшим, ніж підключення через TCP (127.0.0.1:3306).
Практика: спілкуємося через файл-сокет на Python
Давайте створимо сервер, який слухає не порт, а файл. Зверніть увагу на використання socket.AF_UNIX замість AF_INET.
Код сервера (unix_server.py)
import socket
import os
socket_path = "/tmp/demo_socket.sock"
# Переконаємося, що файл сокета не існує
if os.path.exists(socket_path):
os.remove(socket_path)
# Створюємо Unix-сокет (AF_UNIX)
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
# Прив'язуємо до файлу
server.bind(socket_path)
# Слухаємо
server.listen(1)
print(f"Слухаю на {socket_path}")
while True:
connection, client_address = server.accept()
try:
print("З'єднання встановлено!")
data = connection.recv(1024)
print(f"Отримано: {data.decode()}")
connection.sendall(b"Hello from Unix Socket!")
finally:
connection.close()
Клієнт для такого сервера виглядає майже так само, тільки замість IP та порту він підключається до шляху файлу:
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.connect("/tmp/demo_socket.sock")
client.sendall(b"Hello Server!")
Висновок: локальний герой
Unix Domain Sockets — це незамінний інструмент для оптимізації високопродуктивних систем. Вони прибирають зайвих посередників у спілкуванні програм на одному комп’ютері.
Якщо ваші сервіси (веб-сервер, база даних, кеш) живуть на одній машині, завжди надавайте перевагу Unix-сокетам перед TCP. Це безкоштовний і простий спосіб знизити затримки (latency) та навантаження на процесор.