Есть некоторые требования, которые вы, вероятно, знаете, но не были описаны в вопросе, которые затрудняют предоставление информированного ответа. Вот некоторые из этих вопросов:
- Должна ли задача успешно завершиться?
- Если задание выполнено / не выполнено успешно, «кому» нужно знать и какой тип действий необходимо выполнить?
- Каково поведение, если задача еще не завершена, когда придет время снова запустить задачу? Это должно бежать или нет?
- Насколько важно, чтобы задания выполнялись с заданным интервалом? Если интервал составляет каждые 5 минут, должен ли он быть каждые 5 минут или задача может выполняться через 5 минут и 10 секунд?
Первый шаг - ответить, как будет запланировано выполнение периодического задания. Одним из вариантов является запланированное задание Windows, но оно не является высоко доступным по своей природе, но может быть возможным обойти это. Если вы используете SQL Server, другой альтернативой может быть использование агента SQL Server в качестве планировщика, поскольку он будет переключаться при сбое как часть SQL Server.
Следующим шагом для определения является способ вызова приложения WCF. Самый простой вариант - запустить задание для вызова службы WCF через IP-адрес NLB. Это можно считать «нет-нет», если сервер базы данных (или другой сервер в этой зоне) обращается к зоне приложения (конечно, всегда есть исключения, такие как MSDTC).
Другим вариантом будет использование модели очереди. Это было бы наиболее надежным в большинстве ситуаций. например Агент SQL Server может выполнить хранимую процедуру для ввода записи в таблицу очередей. Затем на каждом сервере приложений служба может опрашивать в поисках записи в очереди для обработки. Доступ к записи в очереди будет сериализован базой данных, так что первый сервер будет выполнять задание (и это задание будет выполняться только один раз).
В зависимости от ответов на вводные вопросы в этом ответе вам, возможно, придется добавить еще несколько способов обработки ошибок. Если извлечение внешнего ресурса обычно довольно короткое, вы можете просто сохранить запись очереди заблокированной с помощью select for update
, а по завершении задачи обновить состояние (или удалить запись, если хотите). Это заблокирует другие экземпляры службы от обработки записи, пока она обрабатывается на другом сервере, и если во время обработки происходит сбой, транзакцию следует откатить, и другая служба в кластере может получить запись. (Хотя вы можете увеличить тайм-аут транзакции так долго, как считаете нужным.)
Если сохранение блокировки базы данных в течение длительного времени нецелесообразно, вы можете изменить логику и добавить некоторый мониторинг к сервисам. Теперь, когда задание начинает обрабатываться, его статус будет изменен с очереди на выполнение, и сервер, обрабатывающий запись, будет обновлен в записи. Можно создать некую таблицу статуса службы, и каждый экземпляр службы будет обновлять текущее время каждый раз, когда они опрашивают. Это позволило бы другим службам в кластере повторно обрабатывать задания, которые отображаются как работающие, но служба, на которой они должны работать, не регистрировалась в течение определенного периода.
Этот подход также имеет ограничения: что, если задача фактически выполнена, но каким-то образом потеряна связь с базой данных - задание потенциально может быть запущено снова. Конечно, я не думаю, что проблема объединения атомарных действий с базой данных в сочетании с другими нетранзакционными ресурсами (например, веб-запрос, файловая система) будет легко решена. Я предполагаю, что вы пишете файл или что-то - если внешний контент также помещается в базу данных, то одна транзакция гарантирует, что все будет согласованно.