Linux, закольцовывание сокета после падения процесса, почему?
Всем привет, столкнулся со следующей проблемой:
- debian 8
- REST сервис на nodejs(слушает порт, 50100)
- PHP(curl) клиент, довольно часто подключается, делает запрос, отключается
Все отлично работает до тех пор пока не выключить сервис(или он сам не упадет по какой либо причине), далее происходит следующее:
$ netstat -apne|grep 50100
tcp 0 0 127.0.0.1:50100 127.0.0.1:50100 ESTABLISHED 10000 2375204860 8054/php
php процесс который был подключен(или пытался подключится?) в момент отключения сервиса становится подключенным сам к себе, при этом он уходит в вечный poll:
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7fc404f7d8d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7fc404f7d8d0}, NULL, 8) = 0
poll([{fd=9, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7fc404f7d8d0}, NULL, 8) = 0
poll([{fd=9, events=POLLIN}], 1, 1000) = 0 (Timeout)
rt_sigaction(SIGPIPE, NULL, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7fc404f7d8d0}, 8) = 0
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7fc404f7d8d0}, NULL, 8) = 0
poll([{fd=9, events=POLLIN|POLLPRI|POLLRDNORM|POLLRDBAND}], 1, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {SIG_IGN, [PIPE], SA_RESTORER|SA_RESTART, 0x7fc404f7d8d0}, NULL, 8) = 0
poll([{fd=9, events=POLLIN}], 1, 1000) = 0 (Timeout)
Основная проблема - поднять обратно сервис, порт занят - пока не прибить php процесс порт не освободится
Более того, периодически разные php процессы могут занять этот порт на всех интерфейсах, включая ipv6
Заметил возникновение такого же один-в-один вечного POLL-а с одним различием, что сокет в состоянии ESTABLISHED подключен к удаленному IP (Amazon SQS). в итоге PHP-процесс висит бесконечно.
не являюсь экспертом в сокетах, но хотел спросить:
1. почему случается такая ситуация? допускаю удаленный сервис отпал?
2. почему сокет не отпал?
3. почему локальный код никак не отлавливает проблему с сокетом?
4. как вы отлавливаете подобные случаи?
Порты для исходящих подключений по умолчанию выбираются из диапазона 32768-60999 (задаётся sysctl net.ipv4.ip_local_port_range).
Используемый вами порт 50100 попадает в этот диапазон и когда PHP пытается подключиться к упавшему сервису, он рано или поздно натыкается на этот порт и подключается сам к себе.
Решить проблему можно задав диапазон портов в curl:
curl_setopt($ch, CURLOPT_LOCALPORT, 12345); // Начальный порт
curl_setopt($ch, CURLOPT_LOCALPORTRANGE, 10); // Количество портов в диапазоне
В данном примере будут использованы порты с 12345 до 12355.