Python 异步运行Flask邮件

Python 异步运行Flask邮件,python,email,flask,Python,Email,Flask,我正在使用Flask邮件扩展名从我的Flask应用程序发送电子邮件。它同步运行send()方法,我必须等待它发送消息。如何使其在后台运行?它没有那么复杂-您需要在另一个线程中发送邮件,这样您就不会阻塞主线程。但有一个诀窍 下面是我的代码,用于呈现模板,创建邮件正文,并允许同步和异步发送模板: mail\u sender.py import threading from flask import render_template, copy_current_request_context, curr

我正在使用Flask邮件扩展名从我的Flask应用程序发送电子邮件。它同步运行send()方法,我必须等待它发送消息。如何使其在后台运行?

它没有那么复杂-您需要在另一个线程中发送邮件,这样您就不会阻塞主线程。但有一个诀窍

下面是我的代码,用于呈现模板,创建邮件正文,并允许同步和异步发送模板:

mail\u sender.py

import threading
from flask import render_template, copy_current_request_context, current_app
from flask_mail import Mail, Message

mail = Mail()

def create_massege(to_email, subject, template, from_email=None, **kwargs):
    if not from_email:
        from_email = current_app.config['ROBOT_EMAIL']
    if not to_email:
        raise ValueError('Target email not defined.')
    body = render_template(template, site_name=current_app.config['SITE_NAME'], **kwargs)
    subject = subject.encode('utf-8')
    body = body.encode('utf-8')
    return Message(subject, [to_email], body, sender=from_email)

def send(to_email, subject, template, from_email=None, **kwargs):
    message = create_massege(to_email, subject, template, from_email, **kwargs)
    mail.send(message)

def send_async(to_email, subject, template, from_email=None, **kwargs):
    message = create_massege(to_email, subject, template, from_email, **kwargs)

    @copy_current_request_context
    def send_message(message):
        mail.send(message)

    sender = threading.Thread(name='mail_sender', target=send_message, args=(message,))
    sender.start()
app = Flask('app')
mail_sender.mail.init_app(app)
请注意
@copy\u current\u request\u context
decorator。这是必需的,因为Flask Mail内部使用请求上下文。如果我们将在新线程中运行它,则将丢失上下文。我们可以使用
@copy\u current\u request\u context
-当调用函数时,Flask将推送context来阻止此装饰函数

要使用此代码,还需要使用Flask应用程序初始化
mail
对象:

run.py

import threading
from flask import render_template, copy_current_request_context, current_app
from flask_mail import Mail, Message

mail = Mail()

def create_massege(to_email, subject, template, from_email=None, **kwargs):
    if not from_email:
        from_email = current_app.config['ROBOT_EMAIL']
    if not to_email:
        raise ValueError('Target email not defined.')
    body = render_template(template, site_name=current_app.config['SITE_NAME'], **kwargs)
    subject = subject.encode('utf-8')
    body = body.encode('utf-8')
    return Message(subject, [to_email], body, sender=from_email)

def send(to_email, subject, template, from_email=None, **kwargs):
    message = create_massege(to_email, subject, template, from_email, **kwargs)
    mail.send(message)

def send_async(to_email, subject, template, from_email=None, **kwargs):
    message = create_massege(to_email, subject, template, from_email, **kwargs)

    @copy_current_request_context
    def send_message(message):
        mail.send(message)

    sender = threading.Thread(name='mail_sender', target=send_message, args=(message,))
    sender.start()
app = Flask('app')
mail_sender.mail.init_app(app)

我想简化Marboni的代码,请看这里

import threading

from flask import copy_current_request_context
from flask_mail import Message
from app import app, mail


def create_message(recipient, subject, body):

    if not recipient:
        raise ValueError('Target email not defined.')

    subject = subject.encode('utf-8')
    body = body.encode('utf-8')

    return Message(subject, [recipient], body, sender=app.config['MAIL_USERNAME'] or "groundifly@gmail.com")


def send_async(recipient, subject, body):

    message = create_message(recipient, subject, body)

    @copy_current_request_context
    def send_message(message):
        mail.send(message)

    sender = threading.Thread(name='mail_sender', target=send_message, args=(message,))
    sender.start()
使用。完全公开,我自己写了它来解决这个确切的问题

为什么?

  • 使用concurrent.futures设置线程池而不是手动管理的线程,可以防止创建任意数量的线程。相反,预定义的线程池用于运行从队列馈送的作业
  • Flask Executor为提交的任务提供了当前请求上下文和当前应用上下文,因此您无需编写任何处理代码来管理此任务
  • 下面是它的样子:

    from flask import Flask, current_app
    from flask_executor import Executor
    from flask_mail import Mail, Message
    
    app = Flask(__name__)
    # Set email server/auth configuration in app.config[]
    
    executor = Executor(app)
    mail = Mail(app)
    
    
    def send_email(to, subject, message_text, message_html):
        msg = Message(subject, sender=current_app.config['MAIL_USERNAME'], recipients=[to])
        msg.body = message_text
        msg.html = message_html
        mail.send(msg)
    
    
    @app.route('/signup')
    def signup():
        # My signup form logic
        future = executor.submit(send_email, 'recipient@example.com', 'My subject', 'My text message', '<b>My HTML message</b>')
        print(future.result())
        return 'ok'
    
    if __name__ == '__main__':
        app.run()
    
    从烧瓶导入烧瓶,当前应用程序
    从执行器导入执行器
    从电子邮件导入邮件、消息
    app=烧瓶(名称)
    #在app.config[]中设置电子邮件服务器/auth配置
    执行者=执行者(应用程序)
    邮件=邮件(应用程序)
    def发送电子邮件(收件人、主题、消息文本、消息html):
    msg=Message(主题,发送者=current_app.config['MAIL_USERNAME'],接收者=[to])
    msg.body=消息\文本
    msg.html=message\u html
    邮件发送(msg)
    @应用程序路径(“/signup”)
    def signup():
    #我的注册表格逻辑
    未来=执行人。提交(发送电子邮件,'recipient@example.com“,”我的主题“,”我的短信“,”我的HTML短信“)
    打印(future.result())
    返回“ok”
    如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu':
    app.run()
    
    基本上,您可以编写
    send_email
    函数,就像运行常规内联逻辑一样,并将其提交给执行器。无论您发送多少封电子邮件,只有executor中定义的最大线程数(默认情况下为5*个CPU内核)将运行,发送电子邮件的请求中的任何溢出都将排队


    总的来说,您的代码保持干净,您不需要为要运行的每个异步函数编写一堆包装器代码。

    对于我来说,这段代码可能会发生重复的情况是,有时我会收到电子邮件,有时则不会