В такой ситуации было бы гораздо разумнее как раз применить модель «сервер/субсервер». В соответствии с этой моделью, у нас есть сервер, который создает ряд других процессов (субсерверов). Каждый из этих субсерверов посылает сообщение серверу, но сервер не отвечает им, пока не получит запрос от клиента. Затем сервер передает запрос клиента одному из субсерверов, отвечая на его сообщение заданием, которое субсервер обязан выполнить. Этот процесс показан на приведенном ниже рисунке. Отметьте для себя направления стрелок — они соответствуют направлениям передачи сообщений!
Модель «сервер/субсервер».
Если бы вы делали что-то подобное, вы бы, скорее всего, наняли дополнительно несколько служащих. Эти служащие все пришли бы к вам (как субсерверы посылают сообщение серверу — отсюда направление стрелок на рисунке) в поисках, чего бы такого сделать. Первоначально у вас могло и не быть для них никакой работы — в таком случае их запросы остались бы без ответа. Но теперь, когда кто-нибудь принесет вам кипу бумаг, вы скажете одному из ваших подчиненных: «Это тебе!» — и подчиненный пойдет заниматься делом. Аналогично, по мере поступления других заданий вы и далее будете делегировать их остальным подчиненным.
Хитрость этой модели заключается в том, что она является управляемой по ответу (reply-driven) — выполнение задания начинается с вашего ответа (reply) субсерверу. Стандартная же модель «клиент/сервер» является управляемой по запросу (send-driven), поскольку работа начинается с передачи сообщения серверу.
Но почему клиенты приходят именно
Как вы, вероятно, и подозревали, концепцию единого многопоточного сервера и модель «сервер/субсервер» можно комбинировать. Главная хитрость при этом будет в определении того, какие части задачи лучше было бы распределить по машинам в сети (обычно это касается компонентов системы, которые не генерируют большого трафика), а какие — по процессорам SMP-архитектур (чаще всего это элементы, требующие наличия разделяемой области данных).
Зачем можно было бы комбинировать эти два метода? Используя подход «сервер/субсервер», мы сможем распределять работу между узлами сети. Это в действительности означает, что мы ограничены только числом доступных в сети машин (ну, и полосой пропускания сети, разумеется).
Объединение этот подход с принципом распределения потоков по различным процессорам в архитектурах SMP, мы получим «вычислительный кластер», где центральный «арбитр» распределяет работу (в модели «сервер/субсервер») между блоками SMP, объединенными в сеть.
Несколько примеров
Рассмотрим теперь несколько примеров применения каждого метода.
Файловая система, последовательные порты, консоли и звуковые платы — все это примеры применения модели «клиент/сервер». Прикладная программа на языке Си берет на себя роль клиента и посылает запросы этим серверам. Серверы выполняют работу и отвечают клиентам.
Некоторые из этих «обычных» серверов, однако, в действительности могут быть серверами, управляемыми по ответу (reply-driven)! Это возможно, например, в случае, когда по отношению к конечному клиенту они выглядят как стандартные серверы, а вот работу выполняют по методике «сервер/ субсервер». То есть я имею в виду, что клиент по-прежнему посылает сообщение тому, кого считает «серверным процессом», а тот просто передает работу другому процессу (субсерверу).
Один из наиболее популярных примеров программы, управляемой по ответу (reply-driven), — это программа фрактальной графики, распределенная по сети. Ведущая программа делит экран на несколько зон — например, на 64 зоны. При старте ведущей программе задается список узлов, которые могут участвовать в работе. Затем ведущая программа запускает рабочие программы (субсерверы), по одной на каждый узел, и ждет от них сообщений.
Затем ведущая программа по очереди берет «незаполненные» зоны (из имеющихся 64) и передает задачу фрактальных вычислений программе-исполнителю на другом узле, отвечая ей на ее сообщение. Когда рабочая программа завершит вычисления, она посылает результаты обратно ведущей, которая выводит их на экран.
Поскольку программа-исполнитель передала результаты ведущей программе путем отправки ей сообщения, она теперь снова готова получить от нее ответ с новым заданием. Ведущая программа так и делает до тех пор, пока все 64 зоны на экране не будут заполнены.