Многопоточный дизайн для API
Я реализую API. Фронт-эндом, скорее всего, будет REST/HTTP, бэкэнд-MSSQL, с легким промежуточным звеном между ними. Вероятно, IIS размещен.
К каждому входящему запросу будет прикреплен неуникальный идентификатор. Любые запросы, которые имеют один и тот же Id, должны обрабатываться сериализованно (и FIFO); тогда как запросы с разными идентификаторами могут (и должны) обрабатываться одновременно для повышения производительности и эффективности.
- Предположим, что когда клиенты вызывают мой API, создается новый поток для обработки запроса (поток на модель вызова).
- Предположим, что каждый запрос имеет одинаковый размер и одинаковый объем вычислительной работы.
Текущий дизайн
Мой текущий дизайн и реализация очень просты и понятны. Монитор создается для каждого идентификатора, и все запросы обрабатываются в критической части кода, обеспечивающей сериализацию. Другими словами, Monitor for Id X будет блокировать все потоки, несущие запрос с Id X, пока текущий поток, несущий Id X, не завершит свою работу.
Преимущество этого текущего дизайна - Простота (это было легко осуществить). Второе преимущество заключается в том, что при переключении данных между потоками нет никаких затрат - каждый поток несет запрос на всем пути от его первоначального прибытия в API до отправки ответа клиенту.
Один из возможных недостатков заключается в том, что, когда многие запросы поступают с одним и тем же идентификатором, будет много блокировок потоков (и, в конечном счете, снова разблокирование). Другой возможный недостаток заключается в том, что этот дизайн не поддается асинхронному коду для потенциально увеличивающейся масштабируемости (масштабируемости). скорее всего придется реализовывать другими способами).
Альтернативный дизайн
Другой дизайн может состоять из более сложной схемы:
- Создайте выделенную коллекцию BlockingCollection для каждого обнаруженного идентификатора. Создайте отдельный выделенный длительный поток потребителя для каждой коллекции BlockingCollection.
- Каждый поток, который обрабатывает запрос, действует как производитель, помещая запрос в соответствующую коллекцию BlockingCollection.
- Затем производящий поток ожидает (в асинхронном стиле), пока ответ не будет готов для сбора.
- Каждый потребительский поток обрабатывает элементы из своей очереди BlockingCollection в последовательном порядке и сообщает потоку, который ожидает ответа, как только ответ будет готов
Недостатком этой конструкции является сложность
Вторым недостатком является то, что из-за переключения данных между потоками возникают издержки (не менее двух раз на запрос).
Тем не менее, я думаю, что в достаточно загруженной системе, куда поступает много запросов, это может быть лучше при уменьшении количества блокирующих потоков; И, возможно, общее количество требуемых потоков будет меньше.
Он также лучше подходит для асинхронного кода, чем оригинальный дизайн, что может сделать его лучше масштабируемым.
- Вопросы
Учитывая усилия и сложность повторной реализации с использованием альтернативного дизайна, вероятно, будет ли это целесообразным? (На данный момент я склоняюсь к НЕТ и придерживаюсь своего нынешнего дизайна: но любые взгляды или общие мысли будут высоко оценены.)
Если нет прямого ответа на поставленный выше вопрос, то каковы ключевые соображения, которые мне необходимо учитывать при принятии решения?
1 ответ
Ваше текущее решение будет ужасно масштабироваться, если количество запросов станет слишком большим (запросы начнут стоять в очереди). Каждый запрос порождает новый поток и также выделяет необходимые ресурсы.
Посмотрите на модель актера.
Вы бы порождали поток для каждого идентификатора запроса и просто отправляли запросы через "сообщение" субъекту.
Подумайте о том, чтобы использовать ленивую инициализацию для актеров. Это означает, что вы порождаете поток, только если на самом деле происходит запрос идентификатора. Если очередь сообщений актера пуста, вы можете прекратить их и вызывать только снова, если поступает новый запрос с его идентификатором.
Реализация, сделанная с Threadpool, также должна помочь с производительностью в будущем.