Многопоточный HTTP-сервер Delphi 7 Indy 9

#web-services #delphi #webserver #indy #httpserver

#веб-службы #delphi #веб-сервер #indy #httpserver

Вопрос:

Мне нужно написать многопоточный HTTP-сервер. Я знаю, что Indy обрабатывает потоки через IdThreadMgrPool.

Мои требования просты: иметь сервер, который принимает несколько одновременных клиентов (POST-запросы веб-браузера), запускает ограниченное количество потоков, вызывающих DLL Delphi (вероятно, отдельно от потоков подключения), и возвращает результаты.

Неблокирование было бы идеальным (например node.js ).

На эту тему есть несколько сообщений SO. Чего я не могу найти здесь (или через Google), так это пример использования IdThreadMgrPool. Я вижу сообщения, в которых говорится, что его необходимо использовать, но нет примера, как это сделать. На веб-сайте Indy также нет примера.

Может кто-нибудь указать мне на пример? Или есть ли фреймворк FOS, который будет работать для этого?

Одна из идей состоит в том, чтобы заставить Indy создавать несколько потоков в обычном режиме, а затем предоставить этим потокам доступ к контролируемому пулу потоков, которые запускают DLL. Если это правдоподобно, то мне просто нужно знать «обычный» пример использования case.

Комментарии:

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

2. Тогда как мне контролировать количество вызываемых экземпляров DLL? Если у меня есть, скажем, сервер с двумя процессорами, я бы хотел, чтобы 2 экземпляра DLL были инициализированы и ожидали клиентов. Я не хочу каждый раз запускать DLL и уничтожать ее (это механизм обработки и накладные расходы).

3. Это делает его очень сложным. Вы не должны делать это для каждого запроса, потому что, скажем, сервер получает 10 запросов одновременно. Может потребоваться автоматическое создание до 10 потоков для одновременной обработки их всех. Вы не хотите ограничивать Indy только порождением 2 потоков, иначе он не будет работать одновременно, как предполагалось. Вам нужно будет управлять этой DLL каким-либо другим способом, прежде чем получать какие-либо запросы.

4. Есть ли какая-то причина, по которой вы пишете свой собственный сервер, когда уже существует множество очень хороших HTTP-серверов, которые будут решать эти проблемы за вас? В частности, если вы используете Windows, IIS 7.5 предоставит вам детальный контроль над тем, о чем вы спрашиваете, если вы напишете библиотеку DLL ISAPI Delphi.

5. Воспользуйтесь хорошим советом от @Vector. Позвольте IIS обрабатывать службу и вызывать столько экземпляров вашей DLL, сколько необходимо. У меня есть тот, который может обрабатывать 40 запросов в секунду в течение всего дня. Я не беспокоюсь о потоках, Indy и т. Д..

Ответ №1:

Для использования TIdThreadMgrPool , как минимум, все, что вам нужно сделать, это создать его экземпляр, присвоить его TIdHTTPServer.ThreadMgr свойству и установить его PoolSize свойство. Все это можно сделать во время разработки.

Имейте в виду, что PoolSize не ограничивает количество подключений на сервере. Для этой цели у сервера есть собственное MaxConnections свойство. Например, у вас может быть PoolSize 10 и одновременно подключено 15 клиентов, и, следовательно, запущено 15 потоков. Когда они отключаются, 10 потоков будут помещены обратно в пул, а 5 потоков будут завершены.

Чтобы настроить потоки пула, вы можете получить новый класс TIdPeerThread , при необходимости переопределить его BeforeExecute() AfterExecute() методы virtual и для выполнения инициализации каждого потока и очистки, а затем назначить этот класс свойству сервера (не ThreadMgr) ThreadClass во время выполнения перед активацией сервера. Затем внутри обработчиков событий вашего сервера вы можете преобразовать предоставленный TIdPeerThread объект в свой пользовательский класс и использовать его по мере необходимости.

Вы можете добавлять методы в свой пользовательский класс потоков и предоставлять им внутренний доступ к DLL, регулируя их по мере необходимости. Самым простым решением было бы использовать один общий семафор для управления количеством потоков, которые могут входить в семафор одновременно. В этом отношении вы можете ограничить, скажем, 2 потоками одновременно, даже если запущено 15 потоков.

Поскольку вы говорите, что хотите «запустить DLL в потоке», семафора, скорее всего, будет недостаточно. В этом случае я бы рекомендовал вместо этого использовать порт завершения ввода-вывода. Вы можете заставить свой пользовательский класс потоков отправлять запрос в IOCP с помощью PostQueuedCompletionStatus() и ждать ответа. Регулирование осуществляется количеством потоков, которые вы создаете для обслуживания IOCP, например, один поток на ядро процессора. Каждый поток IOCP будет использоваться GetQueuedCompletionStatus() в цикле для получения отправленных запросов.

Indy не является асинхронным, поэтому вы не сможете отправить запрос в IOCP и позволить ему отправить ответ обратно клиенту напрямую, когда он будет готов. Сервер отправляет ответ обратно клиенту, используя тот же поток, который управляет клиентским подключением. Таким образом, клиентский поток должен будет отправить запрос в IOCP и дождаться его ответа, а затем отправить этот ответ клиенту. Вы можете определить запись, содержащую a TEvent , входные значения, необходимые для вызова библиотеки DLL, и выходные значения для ответа библиотеки DLL. Затем создайте экземпляр этой записи, отправьте указатель на нее в IOCP и дождитесь TEvent сигнала. Когда поток IOCP получает указатель на запись, он может вызвать DLL по мере необходимости, заполнить запись ответом, а затем передать сигнал записи TEvent . Затем ожидающий поток клиента будет разблокирован и сможет отправлять данные ответа записи клиенту по мере необходимости.

Комментарии:

1. Спасибо @Remy. У вас случайно нет ссылки на пример. Всегда проще всего увидеть рабочий код.

Ответ №2:

Сервер Indy плохо масштабируется.

Рассмотрите возможность использования http.sys сервер режима ядра, основанный на iocp.

Наш модуль SynCrtSock с открытым исходным кодом оснащен высокопроизводительным http-сервером и отлично работает с Delphi 5 до XE6.

Посмотрите, например, этот пример кода.

Ответ №3:

Если у вас есть доступный сервер Windows, я бы посоветовал написать библиотеку DLL ISAPI с Delphi 7 и развернуть ее в IIS, которая решит все ваши проблемы на стороне сервера, особенно если у вас Server 2008 R2 с IIS 7.5 (или лучше). Ваша клиентская сторона может использовать Indy для подключения к IIS, а клиент Indy в сочетании с моделью запросов / ответов Delphi ISAPI поддерживает несколько функций, которые упрощают передачу данных от вашего клиента в обработчик запросов на стороне сервера и обратно вашему клиенту. Нет ASP.NET требуется — весь родной Delphi и «классическая» модель приложения ISAPI.

Delphi 7 сгенерирует код приложения ISAPI — вы получаете редактор действий для обработки запросов, когда IIS отправляет их в вашу DLL, которая определяется URL-адресом, который отправляет ваш клиент. У вас есть возможность сохранять информацию о сеансе, создавать глобальные кэши, и у вас есть практически все невизуальные аспекты VCL, доступные в контексте вашей библиотеки DLL ISAPI.

IIS обрабатывает потоки — вероятно, будет обрабатывать гораздо больше одновременных подключений, чем вам нужно, — а также дает вам возможность определить, сколько экземпляров вашей библиотеки DLL может быть загружено одновременно, а также множество других опций, таких как утилизация, различные формы аутентификации и уровни безопасности. Вы можете использовать возможности пула приложений IIS для масштабирования и т. Д.

Я разработал несколько крупномасштабных решений корпоративного уровня с использованием этой архитектуры (клиенты Indy, встроенные в исполняемые файлы Delphi). Он стабилен и относительно «безболезнен», как только вы освоите основы модели приложений ISAPI Delphi и ошибки развертывания IIS.

Если у вас есть эта опция, зачем изобретать велосипед?

Комментарии:

1. У меня есть эта опция. Я соглашусь с этим, спасибо.

2. @IanC — самая сложная часть — это правильное развертывание в IIS. Там много настроек и опций. Большая часть этого задокументирована в MS docs online и здесь, на SO, включая несколько вопросов, которые я задал.

3. Я настроил приложение VB в IIS. Это было не весело. Я обязательно рассмотрю ваши вопросы, спасибо.

4. В @IanC -Delphi7 есть фреймворк «WebBroker» (или что-то в этом роде), которого вы, вероятно, хотите избежать — это фанковое, проприетарное решение, и оно очень устаревшее. За пределами фреймворка приложения ISAPI и редактора действий я никогда его не использовал. Вы можете делать все так же, как в настольном приложении, и использовать ISAPI и Indy для отправки текста или данных обратно вашему клиенту. Вы можете запускать запросы к базе данных на сервере с помощью ADO, загружать свои результаты в TClientDataSet, отправлять обратно в виде XML и загружать другой экземпляр на клиент с помощью XML, также захватывать его, отображать и редактировать. kbmMemTable также поддерживает потоковую передачу двоичных данных.

Ответ №4:

Смотрите пример здесь: http://sourceforge.net/p/xxm/code/HEAD/tree/trunk/Delphi/http /

Логика пула потоков здесь: http://sourceforge.net/p/xxm/code/HEAD/tree/trunk/Delphi/common/xxmThreadPool.pas

Xxm фактически предоставляет интерфейс, для которого вы можете кодировать, поэтому результат переносится через IIS, Apache или обычный HTTP-сервер с многопоточностью. Существует также http.sys версия и соединитель для локального запуска прямо в Internet Explorer (отлично подходит для отладки). У каждого из них также есть версия с автоматическим обновлением, которая будет выполнять горячую замену DLL проектов и использовать ее для любых новых запросов (отлично подходит для живых серверов).