Что может создать сокеты unix в PHP 8.2 в качестве побочного эффекта?

В двух из наших многочисленных модулей PHP (мы работаем под управлением Kubernetes) возникла проблема с утечкой сокетов Unix. Кажется, что наш процесс PHP CLI открывает, а не закрывает множество unix-сокетов, пока в несвязанном месте PHP не выйдет из строя с PHP Warning: stream_select(): You MUST recompile PHP with a larger value of FD_SETSIZE.

В других модулях, которые у нас есть, также есть открытые UNIX-сокеты, но в небольшом количестве, так что это не проблема.

Самое загадочное, что мы ни для чего не используем сокеты UNIX. Вся связь с другими процессами осуществляется исключительно через TCP/IP. Все розетки:

  • В ESTABLISHED штате
  • ss -x говорит, что на эти сокеты не было ни отправлено, ни получено данных
  • У них нет файла в файловой системе
  • netstat говорит, что у них всех есть RefCount из 3, если это что-нибудь значит
  • ss -xp также показывает, что оба конца каждого сокета удерживаются одним и тем же (одним и единственным) процессом PHP.

Итак... по какой-то причине программа PHP создает сокеты UNIX, удерживает их открытыми, удерживая оба конца этого сокета (два разных файловых дескриптора) и не отпуская их.

Кроме того, в коде нет вызовов stream_socket_pair или socket_create_pair.

Из этого я понял, что эти сокеты создаются как побочный эффект какой-то другой встроенной функции PHP, которую я использую. CURL здесь является главным подозреваемым, но он также без проблем широко используется в других модулях. На самом деле я не могу вспомнить ничего, что эти два модуля делают такого, чего не делают другие.

Какие встроенные функции PHP (включая функции в расширениях, которые по умолчанию включены в PHP) могут привести к непреднамеренному созданию пары сокетов UNIX?

Добавлен:

ss -xp выглядит так:

# ss -xp
Netid               State               Recv-Q               Send-Q                             Local Address:Port                                  Peer Address:Port                   Process
u_str               ESTAB               0                    0                                              * 481304571                                        * 481304572               users:(("php",pid=7,fd=494))
u_str               ESTAB               0                    0                                              * 480934363                                        * 480934362               users:(("php",pid=7,fd=289))
u_str               ESTAB               0                    0                                              * 480899260                                        * 480899259               users:(("php",pid=7,fd=255))
u_str               ESTAB               0                    0                                              * 480853228                                        * 480853229               users:(("php",pid=7,fd=207))
u_str               ESTAB               0                    0                                              * 478659551                                        * 478659550               users:(("php",pid=7,fd=82))
u_str               ESTAB               0                    0                                              * 481263776                                        * 481263775               users:(("php",pid=7,fd=484))
u_str               ESTAB               0                    0                                              * 481014545                                        * 481014546               users:(("php",pid=7,fd=339))
u_str               ESTAB               0                    0                                              * 480847844                                        * 480847845               users:(("php",pid=7,fd=221))
u_str               ESTAB               0                    0                                              * 479446282                                        * 479446283               users:(("php",pid=7,fd=185))
u_str               ESTAB               0                    0                                              * 479430966                                        * 479430965               users:(("php",pid=7,fd=162))
u_str               ESTAB               0                    0                                              * 481391710                                        * 481391711               users:(("php",pid=7,fd=515))
u_str               ESTAB               0                    0                                              * 481013994                                        * 481013995               users:(("php",pid=7,fd=352))
u_str               ESTAB               0                    0                                              * 480884372                                        * 480884373               users:(("php",pid=7,fd=233))
.... many, may more lines ...

Я предполагаю, что TCP-соединения, которые никогда не закрывались (запрос клиента, завиток и т. д.)

Vivick 14.03.2024 11:02

@Vivick - Но я думаю, что они будут отображаться как соединения TCP/IP, а не сокеты UNIX.

Vilx- 14.03.2024 11:04

Также обратите внимание, что stream_select — это всего лишь системный вызов select для массива ресурсов PHP stream.

Vivick 14.03.2024 11:05

@Vivick - На самом деле TCP-соединения ДЕЙСТВИТЕЛЬНО отображаются как таковые. Если я наберу ss -p, то получу, например: tcp ESTAB 0 0 1.2.3.4:43652 5.6.7.8:17733 users:(("php",pid=7,fd=12))

Vilx- 14.03.2024 11:06

Есть stream_socket_client звонки?

Vivick 14.03.2024 11:29

неблокирующие элементы в расширениях БД, похоже, используют сокеты и, следовательно, могут использоваться с такими функциями, как stream_select

Vivick 14.03.2024 11:36

@Вивик - Нет, нет, нет, stream_select не имеет к этому никакого отношения. Я сам использую его в несвязанном месте программы. Это то, что дает сбой, но только потому, что одновременно открыто очень много сокетов. Вопрос в том, что открывает все эти сокеты, а не закрывает? Потому что я не могу найти в своем коде ничего, что открывало бы сокеты UNIX. TCP-сокеты — да, конечно, много. Но не UNIX-сокеты. В моем коде нет ни одного места, которое могло бы открыть сокет UNIX, но по какой-то причине процесс со временем открывает сотни таких сокетов.

Vilx- 14.03.2024 11:46

Нет, я говорю это потому, что это приводит к достижению предела файлового дескриптора. Расширения БД открывают сокеты при использовании в неблокирующем режиме.

Vivick 14.03.2024 11:47

Вы используете что-то похожее на Swoole?

Vivick 14.03.2024 11:49

@Vivick Нет, никакого Swoole или другого неблокирующего доступа к базе данных. Я использую Curl в неблокирующем режиме.

Vilx- 14.03.2024 11:52

@Vivick Curl тоже был моим первым подозреваемым, но другие модули тоже без проблем используют его в неблокирующем режиме.

Vilx- 14.03.2024 11:56

Все эти носки созданы pid=7? Pid=7 — это главный процесс или процесс, созданный вами?

shingo 14.03.2024 12:05

@shingo Да, все по pid=7. В модуле есть только один процесс, и он не создает дочерние процессы. Оба конца всех сокетов Unix удерживаются этим процессом.

Vilx- 14.03.2024 12:08

Вы пробовали использовать strace? Так как есть только один процесс.

shingo 14.03.2024 12:21

просто WAG... Curl не выпускает в некоторых версиях, если только вы не unset канал.

YvesLeBorg 14.03.2024 12:43

@shingo - Нет. Я буду иметь в виду этот вариант, но он кажется довольно сложным, и я также не могу себе представить, какую полезную информацию я могу там найти. Возможно, если все остальное не поможет, я попробую это.

Vilx- 14.03.2024 13:17

Отследите socket, чтобы знать, какая функция C ее вызывает.

shingo 14.03.2024 13:28

@YvesLeBorg - Хорошо, но открывает ли он сокет домена unix для чего-либо без запроса? Например, если я попытаюсь открыть URL-адрес https://, может ли он также открыть сокет unix в дополнение к сокету tcp?

Vilx- 14.03.2024 13:28

@shingo Мой код PHP... Я имею в виду, да, это, вероятно, выполнимо, но это будет чертовски сложно. Мне, вероятно, придется проследить и через собственные источники PHP. Как я уже сказал, я оставлю этот вариант открытым в крайнем случае.

Vilx- 14.03.2024 13:30

Нет, я думаю, это, вероятно, не будет чертовски сложно. Создание носков Unix будет отображаться как socket(AT_UNIX... в strace, поскольку вы их не используете. Если вы видите какой-либо вызов, это виновник. С помощью stacktrace вы можете узнать соответствующую функцию C.

shingo 14.03.2024 15:18

@shingo - Хм... Да, я увижу соответствующую функцию C, но она будет внутри самого PHP (или одного из его расширений). Или, зная, насколько продвинут PHP в наши дни, это также может быть какой-то динамически генерируемый ассемблерный код. В любом случае, как мне потом отследить его до моего PHP-кода?

Vilx- 14.03.2024 16:24

@shingo - Хорошо, похоже, в этом нет необходимости. Путем экспериментов я обнаружил, что пары сокетов создаются Curl всякий раз, когда я делаю веб-запросы. Пока только одна пара, но я уверен, что добавление реальной нагрузки и множества параллельных запросов (как в затронутых модулях) может привести к созданию нескольких пар. Реальный вопрос: почему он вообще это делает, когда я делаю HTTP-запросы, а не запросы сокетов Unix? Кроме того, это происходит только в Kubernetes, а не на моей локальной машине.

Vilx- 14.03.2024 18:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Оживление вашего приложения Laravel: Понимание режима обслуживания
Оживление вашего приложения Laravel: Понимание режима обслуживания
Здравствуйте, разработчики! В сегодняшней статье мы рассмотрим важный аспект управления приложениями, который часто упускается из виду в суете...
Установка и настройка Nginx и PHP на Ubuntu-сервере
Установка и настройка Nginx и PHP на Ubuntu-сервере
В этот раз я сделаю руководство по установке и настройке nginx и php на Ubuntu OS.
Коллекции в Laravel более простым способом
Коллекции в Laravel более простым способом
Привет, читатели, сегодня мы узнаем о коллекциях. В Laravel коллекции - это способ манипулировать массивами и играть с массивами данных. Благодаря...
Как установить PHP на Mac
Как установить PHP на Mac
PHP - это популярный язык программирования, который используется для разработки веб-приложений. Если вы используете Mac и хотите разрабатывать...
1
22
99
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я нашел ответ сам. Публикую здесь, чтобы помочь тому, кто придет в следующий раз. Виновник... *барабанная дробь*... КУРЛ. В частности, curl_multi_init() в PHP 8.2 открывает пару UNIX-сокетов, предположительно для какой-то межпотоковой синхронизации.

Вот фрагмент кода, который это воспроизводит:

$handles = [];

for ( $i = 0; $i < 10; $i++ )
    $handles[] = curl_multi_init();

echo "Sleeping...";
sleep(10); // Here we have 20 unix sockets open

foreach ($handles as $h)
    curl_multi_close($h); // According to PHP docs, this doesn't do anything in PHP 8, but it's good style anyway.

$handles = [];
echo "Sleeping again...";
sleep(10); // Here we have 2 unix sockets open... because the $h variable still holds the last handle

echo "Goodbye!";

Я не знаю, почему такого поведения нет на моем локальном компьютере. Возможно, это как-то связано с конкретной версией CURL или PHP. В частности:

У меня PHP 8.2.13 с CURL 8.5.0 (с этим нет проблем), а у модуля Kubernetes — PHP 8.2.16 с CURL 7.88.1 (с этим есть проблема).

Если кто-то знает, почему этого не происходит на моей машине, пожалуйста, дайте мне знать!

Хорошо, теперь просто найти свою собственную CurlMultiHandle утечку — это гораздо более выполнимая задача и выходит за рамки этого вопроса.

Другие вопросы по теме