Запуск асинхронного цикла событий с n задачами из функции синхронизации

В моем веб-приложении Django есть метод, который отправляет всем подписчикам уведомление по электронной почте всякий раз, когда создается новая запись в блоге. Выполнение этого метода отнимает много времени, и я пытаюсь сделать его более эффективным с помощью библиотеки asyncio в python. Моим мотивом является не только производительность, но и то, что мой веб-сервер приостанавливает отправку любых форм, которые занимают более 300 секунд (включая форму, которая создает новые записи в блоге и, следовательно, отправляет письма всем подписчикам). Я полагаю, что неразумно делать окно таймаута намного длиннее этого, и я знаю, что выполнение этого метода будет занимать тем больше времени, чем больше у меня объектов подписчиков. Поэтому я думаю, что нужно использовать какое-то асинхронное решение. В общем, хватит бредить. Вот что я пробовал:

# This method gets called every time a new blog post (agpost) is saved for the first time.
def send_subs_new_post_email(agpost):
    logger.info("Setting up async emailing loop...")
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_until_complete(setup_async_sub_email_loop(agpost, loop))
    loop.close()


# Add a send_email task in the event loop for every subscriber.
async def setup_async_sub_email_loop(agpost, loop):

    for i, sub in enumerate(Subscriber.objects.all()):
        domain = Site.objects.get_current().domain
        absolute_path = 'https://' + str(format(domain))

        # Build unsubscribe link
        token = encrypt(sub.email + "-" + timezone.now().today().strftime("%Y%m%d"))
        unsub_confirmation = urljoin(absolute_path, reverse('homepage:unsub_confirmation') + "?token=" + token)

        # Build post link
        post_url = urljoin(absolute_path, reverse('posts:show', kwargs={'slug': agpost.slug}))

        data = dict()
        data["desc"] = agpost.desc
        data["body"] = agpost.body
        data["post_url"] = post_url
        data["unsubscribe_url"] = unsub_confirmation
        template = get_template("email_templates/post_email.html")

        # Render email text (html and plain) and set subject
        html_content = template.render(data)
        text_content = strip_tags(html_content)
        subject = agpost.title
        email = sub.email

        # Attempt to send notification email
        logging.info("Trying to send notification email for post " + subject + " to sub " + email + "...")
        loop.create_task(try_to_send_post_notification(email, subject, html_content, text_content), name=("task" + str(i)))
        await asyncio.sleep(0)


# Send notification email, log the results.
def try_to_send_post_notification(email, subject, html_content, text_content):
    if send_email(email, subject, html_content, text_content):
        logger.info("SUCCESS: Notification sent to sub " + email + " for post " + subject)
    else:
        logger.error(
            "ERROR: Notification FAILED to send to sub " + email + " for post " + subject)


# Send email.
def send_email(email, subject, html_content, text_content):

    # Send email with subject as both html and plain text
    try:
        send_mail(subject, text_content, None, [email], html_message=html_content, fail_silently=True)
        return True
    except SMTPException as e:
        logging.error("There was an SMTPException sending a user email: ", e)
        return False

Когда я создаю и сохраняю блогпост и запускаю этот метод, я получаю ошибку "You cannot call this from an async context - use a thread or sync_to_async." Я попробовал украсить верхний метод "@sync_to_async", но это, похоже, не помогло. Я также заметил, что практически во всех примерах, которые я видел, цикл async объявляется и выполняется вне любого метода. Это меня смущает, и я искал, почему так происходит. Я хочу, чтобы этот цикл создавался и запускался только тогда, когда я начинаю рассылать электронные письма. Зачем мне запускать цикл вне метода, который его использует? Я уверен, что здесь я совершаю много ошибок новичка в async. Я просто пока не смог найти ответы. Любое руководство будет оценено по достоинству, спасибо огромное за ваше время

Вернуться на верх