Многопоточный серверный дизайн - PullRequest
1 голос
/ 30 января 2012

Я пытаюсь реализовать TCP-сервер, который является частью более крупного проекта. По сути, сервер должен поддерживать TCP-соединение с любым количеством клиентов (минимум 32) и обслуживать любого клиента, который запрашивает обслуживание. В нашем сценарии дело в том, что предполагается, что, как только клиент подключится к серверу, он никогда не закроет соединение, пока не произойдет какой-либо сбой (например, компьютер, на котором работает клиент, выйдет из строя), и он будет повторно запрашивать обслуживание у сервер. То же самое относится и ко всем остальным клиентам, каждый из которых будет поддерживать соединение с сервером и выполнять транзакции. Таким образом, для суммирования сервер будет одновременно поддерживать соединение с клиентами, одновременно обслуживая каждого клиента по мере необходимости, а также должен иметь возможность принимать любые другие клиентские соединения, которые хотят подключиться к серверу.

Теперь я реализовал вышеуказанную функциональность, используя системный вызов API сокета berkely select(), и он отлично работает, когда у нас небольшое количество клиентов (скажем, 10). Но сервер необходимо масштабировать до максимально возможного уровня, поскольку мы внедряем его на 16-ядерном компьютере. Для этого я просмотрел различные методы проектирования многопоточности, например, по одному потоку на клиента и т. Д., И, на мой взгляд, лучшим из них был бы дизайн пула потоков. Теперь, когда я собирался реализовать это, я столкнулся с некоторыми проблемами: Если я назначу основной поток для приема любого количества входящих соединений и сохраню каждое файловое дескриптор соединений в структуре данных, и у меня будет пул потоков, как я получу потоки для опроса того, запрашивает ли конкретный клиент сервис или не. Конструкция достаточно проста для сценариев, в которых клиент связывается с сервером, и после получения службы он закрывает соединение, чтобы мы могли выбрать поток из пула, обслуживать клиента и затем отправить его обратно в пул для дальнейшей обработки соединения. Но когда нам нужно обслуживать набор клиентов, которые поддерживают соединение и периодически запрашивают услуги, то какой подход будет лучшим для этого. Вся помощь будет высоко ценится, так как я действительно застрял в этом. Спасибо.

Ответы [ 2 ]

2 голосов
/ 30 января 2012

Используйте pthreads, с одним потоком на процессор плюс один дополнительный поток.

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

Рабочие потоки используют "select ()" и отправляют / получают данные для любых соединений, которые они приняли. Если / когда рабочий поток получит уведомление «проверьте свою очередь» от основного потока, он получит блокировку / мьютекс для своей очереди FIFO «ожидающие подключения» и добавит все вновь принятые подключения в свой список «fd_set».

Для 1024 подключений и 16 процессоров; у вас может получиться один основной поток, ожидающий новых подключений (но почти ничего не делающий, поскольку вы не ожидаете много новых подключений), и 16 рабочих потоков, обрабатывающих в среднем по 64 подключения каждый.

0 голосов
/ 30 января 2012

Один поток на клиента почти наверняка лучший дизайн. Убедитесь, что у вас всегда есть хотя бы один заблокированный поток в accept в ожидании нового соединения - это означает, что после успешного завершения приема вам может потребоваться создать новый поток, прежде чем продолжить, если он был последним. Я нашел семафоры отличным примитивом для отслеживания необходимости создавать новые потоки прослушивания.

...