Сокет-сервер SO_REUSEPORT - PullRequest
       2

Сокет-сервер SO_REUSEPORT

0 голосов
/ 11 сентября 2018

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

У меня уже есть кодэто создает общий epoll / socket - с включенным EPOLLONESHOT - и каждый поток извлекает события из этого и затем перезагружает EPOLLONESHOT на fd после обработки.

(«Обработка» в данном случае означает чтение доEAGAIN / EWOULDBLOCK, а затем отправляю простой ответ. По сути, я использую «ab» для проверки этого, поэтому он отправляет HTTP-запрос GET и отправляет обратно HTTP «200 OK».)

НоЯ хотел попробовать SO_REUSEPORT.Таким образом, каждый поток имеет свой собственный epoll / socket, связанный с одним и тем же портом.По сути, каждый поток - это свой собственный «мини-сервер», и мы позволяем ядру распределять нагрузку между ними.

Я делаю accept (), получаю fd для входящего соединения, поэтому я добавляю это кEpoll.Как только обработка завершена на этом fd, я, естественно, вызываю close (), чтобы завершить диалог.

Но это, кажется, периодически отбрасывает входящие подтверждения (и "периодически" я имею в виду, что это ведет себя как состояние гонки - иногдаработает, иногда нет, случайным образом).

Читая об этом, очевидно, есть известная ошибка, что может быть условие гонки между accept () и close (), так как close () вызываетперебалансировка вещей и очередь приема просто сбрасываются, поэтому они сбрасываются.

Я пытаюсь найти способ обойти эту проблему.

Одна идея, которая у меня была, была разделитьпринимает из очереди обработки epoll, так что закрытие fd в epoll не может стереть принятие в этой очереди.

Но это не работает логически, так как у меня не может быть потока, оба блокаon accept () и блокировать epoll_wait () одновременно.Чтобы правильно мультиплексировать, мы должны блокировать все события.

Я понял, что столько «мини-серверов», сколько и ядер, и каждый из них привязан к ядру.Таким образом, они действительно работают бок о бок, без переключателей контекста.

Это означает, что хотя я мог бы порождать новый поток для обработки нового входящего fd - и оставить основной поток, чтобы просто принять ()в цикле - тогда это несколько противоречит цели закрепления процессоров, и вся идея мультиплексирования состоит в том, чтобы избавиться от вещи «один поток на соединение».

Я искал исходный код для сервера SO_REUSEPORTЧтобы узнать, как другие могут справиться с этим, но все, что я смог найти, это простая демонстрация, которая не была многопоточной / многоядерной.

Кто-нибудь знает, как я могу решить эту проблему, чтобы многопоточный сервер сокетов SO_REUSEPORT действительно работал?

1 Ответ

0 голосов
/ 12 сентября 2018

Поскольку я тестирую свой собственный серверный код, я сосредоточился на сервере. Достаточно естественно.

Но отмечая пример, который я нашел в Интернете, который устанавливает опцию сокета SO_RCVTIMEO - время ожидания получения - перед добавлением сокета fd из accept к epoll, я тоже попробовал это, и теперь все работает без проблем ( миллион запросов с параллелизмом 1000, при этом каждое ядро ​​работает на уровне 30-40%).

Я бы хотел точно знать, почему это исправило ситуацию, но я полагаю, просто мой сервер не был достаточно терпимым к задержкам, проблемам и тому подобному на стороне клиента при общении, поэтому он застрял на любой сбой в общении - и тогда он будет ждать вечно, поскольку не было тайм-аутов.

Имеет смысл добавить тайм-ауты для всех операций - на всякий случай - поскольку сетевое взаимодействие никогда не бывает на 100% идеальным. И, как всегда в кодировании, все входные данные следует рассматривать как ненадежные и потенциально вредоносные.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...