Архитектура серверной части системы
От: Xenon_IPC  
Дата: 30.07.12 00:04
Оценка:
Здравствуйте уважаемые гуру проектирования и разработки программного обеспечения.
Требуется разработать одну систему с небольшой нагрузкой (до нескольких десятков клиентов). Клиент постараюсь сделать максимально возможно тонким и бизнес-логику по максимуму вынести на сервер. Хотел бы посоветоваться с архитектурой серверной части. Общую идею попробовал изобразить на следующей картинке (клик чтобы увеличить):

Интересуют следующие вопросы:
    1. Насколько из данного изображения понятна архитектура и основные внутренние процессы в системе
    2. Насколько академически корректны данные условные обозначения (по возможности конкретизировать какие элементы изображены неверно и какое условное обозначение является для них верным)
    3. Какие замечания собственно по самой архитектуре (что неверно, что можно улучшить)

Чтобы внести ясность того насколько Ваше понимание системы после ознакомления с изображением соответствует тому что я хотел передать, постараюсь описать это словами:
на неком сервере (в intranet сети либо internet сети это собственно не важно) существует сервис который принимает соединения от удаленных клиентов. Для каждого подключенного клиента создается свой экземпляр сервиса в котором он работает (в терминологии WCF InstanceContextMode = PerSession). Вызовы с клиента являются асинхронными (для этого на изображении я попытался это обозначить с помощью дуги на стрелке), т.е. клиент при вызове метода не блокируется. Ответ от сервера является также асинхронным, по сути это callback на клиент с результатом либо некоторым извещением (например об ошибке или прогрессе операции). Другими словами обычный дуплексный режим. Далее, в сервисе существует некий синглтон-пул для работы с БД. Каждый экземпляр сервиса заносит свой запрос в этот пул, пул его обрабатывает и результат отдает обратно сервису по callback'у. У некоторых возможно возникнет вопрос, для чего понадобился пул запросов и почему к БД не обращаться непосредственно из сервера. Это вызвано одним из требованием к системе: клиент может сделать одновременно несколько запросов к серверу, которые должны выполняться параллельно. Т.е. например клиент хочет получить список заказов и пока они грузятся открывает новое окошко с каталогом продукции. В этом случае на сервер сначала идет запрос получить список заказов, этот запрос ложится в пул и сервис готов для приема следующего запроса: каталога продукции, который тоже ложится в пул и т.д. В пуле созданы 4 потока (думаю этот параметр сделать настраиваемым), которые ожидают появления новых запросов. Расположение БД не должно играть роль (будет находиться на том же компьютере либо на удаленном).
Вот, в общих чертах на словах схема работы серверной части. Прошу высказывайте свои комментарии и конструктивную критику.

С уважением, Александр
Re: Архитектура серверной части системы
От: Poul_Ko Казахстан  
Дата: 30.07.12 03:52
Оценка: 1 (1)
Здравствуйте, Xenon_IPC, Вы писали:

X_I>У некоторых возможно возникнет вопрос, для чего понадобился пул запросов и почему к БД не обращаться непосредственно из сервера. Это вызвано одним из требованием к системе: клиент может сделать одновременно несколько запросов к серверу, которые должны выполняться параллельно. Т.е. например клиент хочет получить список заказов и пока они грузятся открывает новое окошко с каталогом продукции. В этом случае на сервер сначала идет запрос получить список заказов, этот запрос ложится в пул и сервис готов для приема следующего запроса: каталога продукции, который тоже ложится в пул и т.д. В пуле созданы 4 потока (думаю этот параметр сделать настраиваемым), которые ожидают появления новых запросов. Расположение БД не должно играть роль (будет находиться на том же компьютере либо на удаленном).


Стандартный Connection Pooling не решает ту же задачу? Т.е., код сервиса работает непосредственно с БД, соединение для каждого запроса берётся из пула.
Brainbench transcript #6370594
Re: Архитектура серверной части системы
От: Sshur Россия http://shurygin-sergey.livejournal.com
Дата: 30.07.12 06:28
Оценка:
Здравствуйте, Xenon_IPC, Вы писали:

X_I>Здравствуйте уважаемые гуру проектирования и разработки программного обеспечения.

X_I>Требуется разработать одну систему с небольшой нагрузкой (до нескольких десятков клиентов). Клиент постараюсь сделать максимально возможно тонким и бизнес-логику по максимуму вынести на сервер. Хотел бы посоветоваться с архитектурой серверной части. Общую идею попробовал изобразить на следующей картинке (клик чтобы увеличить):

X_I>на неком сервере (в intranet сети либо internet сети это собственно не важно) существует сервис который принимает соединения от удаленных клиентов. Для каждого подключенного клиента создается свой экземпляр сервиса в котором он работает (в терминологии WCF InstanceContextMode = PerSession).

X_I>С уважением, Александр


Свой экземпляр сервиса == новый поток?
Шурыгин Сергей

"Не следует преумножать сущности сверх необходимости" (с) Оккам
Re[2]: Архитектура серверной части системы
От: Xenon_IPC  
Дата: 30.07.12 14:46
Оценка:
Здравствуйте, Sshur, Вы писали:

S>Свой экземпляр сервиса == новый поток?


Если я правильно понял Ваш вопрос, то да. Это стандартное поведение WCF при InstanceContextMode.PerSession и ConcurrencyMode.Single
Re[2]: Архитектура серверной части системы
От: Xenon_IPC  
Дата: 30.07.12 14:58
Оценка:
Здравствуйте, Poul_Ko, Вы писали:

P_K>Стандартный Connection Pooling не решает ту же задачу? Т.е., код сервиса работает непосредственно с БД, соединение для каждого запроса берётся из пула.


Дело в том, что мне необходимо выполнять также некоторую специфичную работу. Например, открытие некоторой записи пользователем на редактирование автоматически блокирует эту запись от редактирования для всех остальных пользователей (т.е. другие пользователи смогут открыть ее только на чтение). Также необходим механизм запроса пользователя "захватившего" запись "отпустить" ее, а также механизм принудительного "освобождения" записи, например администратором системы. Все это конечно можно сделать через БД (добавить флажок EntityState), но мне кажется, что более продуктивно это сделать на сервере приложения. Единственное место где я вижу это можно сделать — это между БД и самим сервисом, т.е. пул. Может есть какие-то еще пути которые мне не пришли в голову? Сейчас этой проблемой я особо не заморачиваюсь, да по сути не особо и важно использовать стандартный connection pooling или свой. Сейчас идет проектирование на уровне абстракций. Как их реализовывать и какие средства использовать это уже следующий этап.
Re[3]: Архитектура серверной части системы
От: Sshur Россия http://shurygin-sergey.livejournal.com
Дата: 30.07.12 14:58
Оценка:
Здравствуйте, Xenon_IPC, Вы писали:

X_I>Здравствуйте, Sshur, Вы писали:


S>>Свой экземпляр сервиса == новый поток?


X_I>Если я правильно понял Ваш вопрос, то да. Это стандартное поведение WCF при InstanceContextMode.PerSession и ConcurrencyMode.Single



Ну тогда на десятках клиентов это конечно будет работать, но правильной практикой будет выделение потока из пула под конкретный запрос. Тогда в каждый момент будет занято столько потоков, сколько имеется одновременно выполняемых запросов. Более того, при асинхронной обработке запросов в БД на время ожидания ответа из БД (а это обычно занимает больше всего времен) поток будет возвращаться в пул.
Шурыгин Сергей

"Не следует преумножать сущности сверх необходимости" (с) Оккам
Re[3]: Архитектура серверной части системы
От: Sshur Россия http://shurygin-sergey.livejournal.com
Дата: 30.07.12 15:02
Оценка:
Здравствуйте, Xenon_IPC, Вы писали:

X_I>Здравствуйте, Poul_Ko, Вы писали:


P_K>>Стандартный Connection Pooling не решает ту же задачу? Т.е., код сервиса работает непосредственно с БД, соединение для каждого запроса берётся из пула.


X_I>Дело в том, что мне необходимо выполнять также некоторую специфичную работу. Например, открытие некоторой записи пользователем на редактирование автоматически блокирует эту запись от редактирования для всех остальных пользователей (т.е. другие пользователи смогут открыть ее только на чтение). Также необходим механизм запроса пользователя "захватившего" запись "отпустить" ее, а также механизм принудительного "освобождения" записи, например администратором системы. Все это конечно можно сделать через БД (добавить флажок EntityState), но мне кажется, что более продуктивно это сделать на сервере приложения. Единственное место где я вижу это можно сделать — это между БД и самим сервисом, т.е. пул. Может есть какие-то еще пути которые мне не пришли в голову? Сейчас этой проблемой я особо не заморачиваюсь, да по сути не особо и важно использовать стандартный connection pooling или свой. Сейчас идет проектирование на уровне абстракций. Как их реализовывать и какие средства использовать это уже следующий этап.


А эта специфичная работа требует знания и одних и тех же connection id? С ходу не видно, чем самопальный пулинг лучше стандартного
Шурыгин Сергей

"Не следует преумножать сущности сверх необходимости" (с) Оккам
Re[4]: Архитектура серверной части системы
От: Xenon_IPC  
Дата: 30.07.12 15:04
Оценка:
Здравствуйте, Sshur, Вы писали:

... Тогда в каждый момент будет занято столько потоков, сколько имеется одновременно выполняемых запросов...

Не совсем согласен с этим утверждением. У меня конечно нет опыта в разработке приложений где одновременное количество запросов исчисляется тысячами, но теоретически рассуждая, создавать такое количество потоков это неверно. По моему мнению намного эффективнее создать определенное кол-во потоков (8, 16, 32... это уже другой вопрос сколько) которые будут последовательно вытягивать запросы из некой очереди.
Re[4]: Архитектура серверной части системы
От: Xenon_IPC  
Дата: 30.07.12 15:10
Оценка:
Здравствуйте, Sshur, Вы писали:

S>А эта специфичная работа требует знания и одних и тех же connection id? С ходу не видно, чем самопальный пулинг лучше стандартного


Фактически да, в частности OperationContext. Нам необходимо взаимодействовать как минимум с 2-мя клиентами: 1-й который "захватил" объект и 2-й который хочет этот объект "захватить". Есть еще некоторые примочки которые планируются реализовать. Но на данном этапе это все мелочи. Фактически можно отобразить на изображении данный блок как black box ConnectionPooling (стандартный он или кастомный это не суть важно). Это уже будет решаться на след. этапе проектирования. Сейчас необходимо добиться след. поведения: сервис делает запрос к БД и готов к приему следующих запросов от клиента.
Re[5]: Архитектура серверной части системы
От: boot  
Дата: 30.07.12 15:51
Оценка:
Здравствуйте, Xenon_IPC, Вы писали:

X_I>Фактически да, в частности OperationContext. Нам необходимо взаимодействовать как минимум с 2-мя клиентами: 1-й который "захватил" объект и 2-й который хочет этот объект "захватить". Есть еще некоторые примочки которые планируются реализовать. Но на данном этапе это все мелочи. Фактически можно отобразить на изображении данный блок как black box ConnectionPooling (стандартный он или кастомный это не суть важно). Это уже будет решаться на след. этапе проектирования. Сейчас необходимо добиться след. поведения: сервис делает запрос к БД и готов к приему следующих запросов от клиента.


Речь идет о всем объекте или его части? Типа один клиент редактирует одну страницу книги, а второй читает другую? У клиентов есть свойство которое позволяет забрать право на редактирование или они равноправны?
Жизнеспособность прямо пропорциональна простоте!
Re[6]: Архитектура серверной части системы
От: Xenon_IPC  
Дата: 30.07.12 16:13
Оценка:
Здравствуйте, boot, Вы писали:

B>Речь идет о всем объекте или его части? Типа один клиент редактирует одну страницу книги, а второй читает другую? У клиентов есть свойство которое позволяет забрать право на редактирование или они равноправны?


Блокировка накладывается на весь объект (фактически запись в БД). В большинстве своем клиенты равноправны, но систему "старшинства" планируется ввести. Идеальный алгоритм примерно таков: клиент имеющий более высокий приоритет говорит, что он заберет блокировку объекта на себя, на клиенте с текущей блокировкой отображается немодальное диалоговое окно с атрибутом TopMost, что мол такой-то клиент по истечению например 1 минуты принудительно заберет блокировку на себя. Клиент должен до истечения этого времени освободить объект либо объект будет освобожден принудительно и все изменения в нем будут потеряны. Естественно точного выдерживания таймингов добиться не получится, но это и не суть важно.
Re[7]: Архитектура серверной части системы
От: boot  
Дата: 30.07.12 17:03
Оценка:
Здравствуйте, Xenon_IPC, Вы писали:

X_I>Здравствуйте, boot, Вы писали:


B>>Речь идет о всем объекте или его части? Типа один клиент редактирует одну страницу книги, а второй читает другую? У клиентов есть свойство которое позволяет забрать право на редактирование или они равноправны?


X_I>Блокировка накладывается на весь объект (фактически запись в БД). В большинстве своем клиенты равноправны, но систему "старшинства" планируется ввести. Идеальный алгоритм примерно таков: клиент имеющий более высокий приоритет говорит, что он заберет блокировку объекта на себя, на клиенте с текущей блокировкой отображается немодальное диалоговое окно с атрибутом TopMost, что мол такой-то клиент по истечению например 1 минуты принудительно заберет блокировку на себя. Клиент должен до истечения этого времени освободить объект либо объект будет освобожден принудительно и все изменения в нем будут потеряны. Естественно точного выдерживания таймингов добиться не получится, но это и не суть важно.


Такие "мелочи" многое определяют в архитектуре. Поди еще серверная часть распределенная, сервер сессий на одной машине, а СУБД не одна, да еще и на другой машине? Поди еще и тяжелые процедуры на сервере или в СУБД имеют место быть? Я не стал бы забивать на эти "мелочи" в самом начале, в конечном итоге все определяет скорость, а скорость это цифра, значит необходимы расчеты, иначе, методом тыка, дольше будет разработка. Ваша схема недоступна для критики, мало сведений. Тут хорошо бы еще знать как много запросов к СУБД, и сколько из них повторяется. Возможно будет полезна кеширующая прослойка в памяти. Чесслово не знаю как Вам лучше сделать, но детали интересны. Конкурентов много опережает?
Жизнеспособность прямо пропорциональна простоте!
Re[8]: Архитектура серверной части системы
От: Xenon_IPC  
Дата: 30.07.12 17:30
Оценка:
Здравствуйте, boot, Вы писали:

B>Такие "мелочи" многое определяют в архитектуре. Поди еще серверная часть распределенная, сервер сессий на одной машине, а СУБД не одна, да еще и на другой машине? Поди еще и тяжелые процедуры на сервере или в СУБД имеют место быть? Я не стал бы забивать на эти "мелочи" в самом начале, в конечном итоге все определяет скорость, а скорость это цифра, значит необходимы расчеты, иначе, методом тыка, дольше будет разработка. Ваша схема недоступна для критики, мало сведений. Тут хорошо бы еще знать как много запросов к СУБД, и сколько из них повторяется. Возможно будет полезна кеширующая прослойка в памяти. Чесслово не знаю как Вам лучше сделать, но детали интересны. Конкурентов много опережает?


Более того, хотелось бы изначально заложить в архитектуру и распределенность самого сервиса, т.е. сервис может находиться на нескольких серверах. но боюсь, что "малой кровью" для обеспечения такой архитектуры не отделаться, а необходимости в этом пока не вижу. Тяжелые места на сервере имеют место быть, на СУБД вряд ли (по-крайней мере я буду максимально стараться всю тяжесть бизнес-логики переносить на сам сервис и ХП использовать по минимуму).
Нагрузка средняя — обычная ERP система, до нескольких десятков пользователей. Ожидаются относительно тяжелые вычисления в некоторых модулях, но как я писал, вся нагрузка будет приходиться на сервисы с бизнес-логикой. Предполагаю, что в большинстве своем получится построить запросы так чтобы они откешировались СУБД (на данный момент планирую SQL Server). Разнообразность предполагаю построить через параметры запросов. Насчет кеширующей прослойки я и сам думал , но все взвесил и необходимости в ней не вижу.
Вроде у себя сформировал полностью целостностное представление всей архитектуры, но боюсь что-то упустить или выбрать неверное архитектурное решение, т.к. от этого зависит мое будущее в данной компании (а возможно и в глобальном смысле тоже )
Что Вы имели в виду под вопросом: "Конкурентов много опережает?"?
Re[9]: Архитектура серверной части системы
От: boot  
Дата: 31.07.12 03:46
Оценка:
Здравствуйте, Xenon_IPC, Вы писали:

X_I>Что Вы имели в виду под вопросом: "Конкурентов много опережает?"?


Имел в виду, какое место занимает компания в которой Вы работаете, среди конкурентов двигающихся в том же направлении? Оценивать Вас будут именно по способности превзойти хотя бы одного конкурента, а не по красивости стрелочек между квадратиками. Даже если это стартап, конкуренты все равно есть, и большая удача если они не будут думать о скорости.
Вообще, смотрите, у Вас получается три очереди: 1) от клиентов к серверу соединений 2) от экземпляра сервиса к пулу исполнителей запросов 3) очередь к СУБД встроена в ms sql server, который Вы желаете использовать. Вполне логично при выбранном подходе, а теперь представьте, что со всем, что Вы не считаете необходимым, требуется доказать необходимость 2-го пункта. Ведь можно обойтись и без него. В случае провала этот вопрос обязательно задаст тот, от кого Вы этого не ждете.
Жизнеспособность прямо пропорциональна простоте!
Re: Архитектура серверной части системы
От: knst Россия  
Дата: 01.09.12 08:28
Оценка:
Сервис это фасад к системе , не стоит в нем реализовывать логику. Лучше сделать так:при обращении к сервису создается задача (таск, заявка , заказ) ей присваивается id она кладется в очередь.Клиент пользуясь тем же сервисом видит свои задачи и их статусы. Обработка задач выполняется отдельным компонентом (пулом воркеров, каждый тащит задачи из очереди и меняет их статус после обработки, сохраняет результат). Если проект большой то стоит посмотреть в сторону CQRS (http://martinfowler.com/bliki/CQRS.html). В простом достаточно хранить задачи в базе и отдельно поднять компонент для их обработки (при этом обрабатывать может несколько инстансов и не обязательно на той же машине где крутится wcf, это увеличит масштабируемость и безопасность). Если сервисы под iis то тем более нужно разделять бизнес логику и фасадный интерфейс — iis может в любой момент выгрузить приложение из памяти и при следующем обращении оно начнет стартовать заново, что не всегда быстро. В идеале, сервис вообще не ходит в БД а общается с backend тогда можно закрыть доступ к БД из дмз , что правильнее сточки зрения безопасности.

Здравствуйте, Xenon_IPC, Вы писали:

X_I>Здравствуйте уважаемые гуру проектирования и разработки программного обеспечения.

X_I>Требуется разработать одну систему с небольшой нагрузкой (до нескольких десятков клиентов). Клиент постараюсь сделать максимально возможно тонким и бизнес-логику по максимуму вынести на сервер. Хотел бы посоветоваться с архитектурой серверной части. Общую идею попробовал изобразить на следующей картинке (клик чтобы увеличить):
X_I>
X_I>Интересуют следующие вопросы:
X_I>

    X_I>1. Насколько из данного изображения понятна архитектура и основные внутренние процессы в системе
    X_I>2. Насколько академически корректны данные условные обозначения (по возможности конкретизировать какие элементы изображены неверно и какое условное обозначение является для них верным)
    X_I>3. Какие замечания собственно по самой архитектуре (что неверно, что можно улучшить)
    X_I>

X_I>Чтобы внести ясность того насколько Ваше понимание системы после ознакомления с изображением соответствует тому что я хотел передать, постараюсь описать это словами:

X_I>на неком сервере (в intranet сети либо internet сети это собственно не важно) существует сервис который принимает соединения от удаленных клиентов. Для каждого подключенного клиента создается свой экземпляр сервиса в котором он работает (в терминологии WCF InstanceContextMode = PerSession). Вызовы с клиента являются асинхронными (для этого на изображении я попытался это обозначить с помощью дуги на стрелке), т.е. клиент при вызове метода не блокируется. Ответ от сервера является также асинхронным, по сути это callback на клиент с результатом либо некоторым извещением (например об ошибке или прогрессе операции). Другими словами обычный дуплексный режим. Далее, в сервисе существует некий синглтон-пул для работы с БД. Каждый экземпляр сервиса заносит свой запрос в этот пул, пул его обрабатывает и результат отдает обратно сервису по callback'у. У некоторых возможно возникнет вопрос, для чего понадобился пул запросов и почему к БД не обращаться непосредственно из сервера. Это вызвано одним из требованием к системе: клиент может сделать одновременно несколько запросов к серверу, которые должны выполняться параллельно. Т.е. например клиент хочет получить список заказов и пока они грузятся открывает новое окошко с каталогом продукции. В этом случае на сервер сначала идет запрос получить список заказов, этот запрос ложится в пул и сервис готов для приема следующего запроса: каталога продукции, который тоже ложится в пул и т.д. В пуле созданы 4 потока (думаю этот параметр сделать настраиваемым), которые ожидают появления новых запросов. Расположение БД не должно играть роль (будет находиться на том же компьютере либо на удаленном).
X_I>Вот, в общих чертах на словах схема работы серверной части. Прошу высказывайте свои комментарии и конструктивную критику.

X_I>С уважением, Александр
Re[3]: Архитектура серверной части системы
От: Константин Черногория  
Дата: 06.09.12 19:20
Оценка:
S>>Свой экземпляр сервиса == новый поток?
X_I>Если я правильно понял Ваш вопрос, то да. Это стандартное поведение WCF при InstanceContextMode.PerSession и ConcurrencyMode.Single
Нет.
В WCF всё хорошо с потоками, он их никогда не создаёт по одному на клиент.

InstanceContextMode.PerSession означает что 1 сессия = 1 экземпляр вашего объекта который отвечает на сетевые запросы.

ConcurrencyMode.Single означает что ваш объект не умеет одновременно обрабатывать несколько запросов, т.е. если один клиент одновременно пришлёт 2 запроса, они будут обработаны по очереди (или никогда, если возникнет deadlock, типичный для duplex контрактов с ConcurrencyMode.Single, но это к теме не относится). Но конечно разные запросы он будет обрабатывать в разных потоках.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.