Фоновые задания
Плагины InfraVision могут откладывать определённые операции, помещая в очередь фоновые задания, которые выполняются асинхронно фоновыми рабочими процессами. Это полезно для отделения длительных процессов от цикла запрос-ответ, обращённого к пользователю.
Например, вашему плагину может потребоваться получить данные из удалённой системы. В зависимости от объёма данных и отзывчивости удалённого сервера это может занять несколько минут. Отложение этой задачи в задание очереди гарантирует, что она может быть завершена в фоновом режиме, не прерывая пользователя. Данные, которые она получает, могут быть доступны после завершения задания.
Исполнители заданий
Фоновое задание реализует базовый исполнитель Job для всех видов задач. В нём реализована логика для управления связанным объектом задания, перепланирования периодических заданий с заданным интервалом и обработки ошибок. Добавление пользовательских заданий выполняется путём создания подкласса класса JobRunner InfraVision.
::: netbox.jobs.JobRunner
Пример
from netbox.jobs import JobRunner
class MyTestJob(JobRunner):
class Meta:
name = "My Test Job"
def run(self, *args, **kwargs):
obj = self.job.object
# ваша логика здесь
Завершённые задания по умолчанию получают статус "completed" (завершено), или "errored" (ошибка), если метод run() вызвал необработанное исключение. Чтобы намеренно отметить задание как неудачное, вызовите исключение core.exceptions.JobFailed. (Обратите внимание, что "failed" (неудача) отличается от "errored" (ошибка) тем, что неудача может быть ожидаема при определённых условиях, тогда как ошибка — нет.)
Вы можете запланировать фоновое задание из вашего кода (например, из метода save() модели или представления), вызвав MyTestJob.enqueue(). Этот метод передаёт все аргументы в Job.enqueue(). Однако аргумент name передавать не нужно, так как вместо него будет использовано имя фонового задания.
Совет
Набор предопределённых интервалов доступен в core.choices.JobIntervalChoices для удобства.
Атрибуты
Атрибуты JobRunner определяются во вложенном классе Meta внутри задания. Они необязательны, но рекомендуются.
name
Это понятное человеку имя вашего фонового задания. Если не указано, будет использовано имя класса.
Логирование
Информация
Эта функция была введена в InfraVision v4.4.
Логгер Python создаётся исполнителем для каждого задания. Его можно использовать в методе run() задания по мере необходимости:
def run(self, *args, **kwargs):
obj = MyModel.objects.get(pk=kwargs.get('pk'))
self.logger.info("Retrieved object {obj}")
Поддерживаются четыре стандартных уровня логирования Python:
debug()info()warning()error()
Записи лога, созданные с помощью логгера исполнителя, будут сохранены в логе задания в базе данных в дополнение к обработке другими обработчиками системного логирования.
Запланированные задания
Как описано выше, задания могут быть запланированы для немедленного выполнения или на любое более позднее время с помощью метода enqueue(). Однако для целей управления метод enqueue_once() позволяет запланировать задание ровно один раз, избегая дубликатов. Если задание уже запланировано для конкретного экземпляра, второе не будет запланировано, с соблюдением потокобезопасности. Примером использования может быть планирование периодической задачи, привязанной к экземпляру в целом, но не к какому-либо событию этого экземпляра (например, обновлениям). Параметры метода enqueue_once() идентичны параметрам enqueue().
Совет
Не запрещено использовать enqueue() для дополнительных заданий, пока активно интервальное расписание. Примером этого может быть планирование периодической ежедневной синхронизации, но также запуск дополнительных синхронизаций по требованию, когда пользователь нажимает кнопку.
Пример
from django.db import models
from core.choices import JobIntervalChoices
from netbox.models import NetBoxModel
from .jobs import MyTestJob
class MyModel(NetBoxModel):
foo = models.CharField()
def save(self, *args, **kwargs):
MyTestJob.enqueue_once(instance=self, interval=JobIntervalChoices.INTERVAL_HOURLY)
return super().save(*args, **kwargs)
def sync(self):
MyTestJob.enqueue(instance=self)
Системные задания
Некоторые плагины могут реализовывать фоновые задания, отделённые от цикла запрос/ответ. Типичными случаями использования являются задачи обслуживания или задания синхронизации. Они могут быть зарегистрированы как системные задания с помощью декоратора system_job(). Интервал задания должен быть передан как целое число (в минутах) при регистрации системного задания. Системные задания планируются автоматически при запуске RQ worker (manage.py rqworker).
Пример
from core.choices import JobIntervalChoices
from netbox.jobs import JobRunner, system_job
from .models import MyModel
# Укажите предопределённый выбор или целое число,
# указывающее количество минут между выполнениями задания
@system_job(interval=JobIntervalChoices.INTERVAL_HOURLY)
class MyHousekeepingJob(JobRunner):
class Meta:
name = "My Housekeeping Job"
def run(self, *args, **kwargs):
MyModel.objects.filter(foo='bar').delete()
Примечание
Убедитесь, что все системные задания импортируются при инициализации. В противном случае они не будут зарегистрированы. Этого можно достичь, расширив метод ready() PluginConfig. Например:
def ready(self):
super().ready()
from .jobs import MyHousekeepingJob
Очереди задач
По умолчанию определены три очереди задач с разными приоритетами:
- High (Высокий)
- Default (По умолчанию)
- Low (Низкий)
Все задачи в очереди "high" завершаются до проверки очереди default, а все задачи в очереди default завершаются до задач в очереди "low".
Плагины также могут добавлять собственные очереди для своих нужд, установив атрибут queues в классе PluginConfig. Пример приведён ниже:
class MyPluginConfig(PluginConfig):
name = 'myplugin'
...
queues = [
'foo',
'bar',
]
PluginConfig выше создаёт две пользовательские очереди с именами my_plugin.foo и my_plugin.bar. (Имя плагина добавляется в начало каждой очереди, чтобы избежать конфликтов между плагинами.)
Внимание
По умолчанию процесс RQ worker InfraVision обслуживает только очереди high, default и low. Плагины, вводящие пользовательские очереди, должны рекомендовать пользователям либо перенастроить worker по умолчанию, либо запустить выделенный worker, указав необходимые очереди. Например:
python manage.py rqworker my_plugin.foo my_plugin.bar