python - Flask-Mail 队列消息被发送到不同的电子邮件

标签 python connection worker task-queue flask-mail

我正在为我的 Flask 应用程序使用 Flask-Mail 库,以便在用户注册以添加到时事通讯时向他们发送默认的欢迎电子邮件。调试该库后,我发现它一次只能处理一个连接发送消息,然后会自动关闭连接。如果后端在连接仍处于打开状态时向另一个用户发送电子邮件,则会引发此异常:raise SMTPServerDisconnected("Connection unexpectedly closed: " smtplib.SMTPServerDisconnected: Connection unexpectedly closed: [WinError 10054] An existing connection was forcibly closed by the remote host .我希望能够在连接关闭后将邮件库排队以向另一个收件人发送新消息,但目前当我尝试将函数排队以发送消息时,它一直抛出我上面提到的错误。
worker .py:

import os
import redis
from rq import Worker, Queue, Connection

listen = ['high', 'default', 'low']

redis_url = os.environ.get('REDISTOGO_URL')

conn = redis.from_url(redis_url)

if __name__ == '__main__':
    with Connection(conn):
        worker = Worker(map(Queue, listen))
        worker.work()
用户.routes.py
from flask import request, Blueprint, redirect, render_template
from flask_app import mail, db
from flask_app.users.forms import NewsLetterRegistrationForm
from flask_app.models import User
from flask_mail import Message
from rq import Queue
from worker import conn
import os, time

users = Blueprint("users", __name__)
queue = Queue(connection=conn)

@users.route("/newsletter-subscribe", methods=["GET", "POST"])
def newsletter_subscribe():
    form = NewsLetterRegistrationForm()
    if form.validate_on_submit():
        user = User(name=form.name.data, email=form.email.data)
        db.session.add(user)
        db.session.commit()
        queue.enqueue(send_welcome_email(user))

        return "Success"
    return "Failure"

def send_welcome_email(user):
    with mail.connect() as con:
        html = render_template("welcome-email.html", name=user.name)
        subject = "Welcome!"
        msg = Message(
            subject=subject,
            recipients=[user.email],
            html=html
        )
        con.send(msg)
主路由.py
from flask import render_template, session, request, current_app, Blueprint, redirect, url_for, json, make_response
from flask_app.users.forms import NewsLetterRegistrationForm
import os

main = Blueprint("main", __name__)

@main.route("/", methods=["GET"])
def index():
    return render_template("index.html", title="Home")

@main.route("/example", methods=["GET"])
def example():
    return render_template("example.html", title="example")

@main.context_processor
def inject_template_scope():
    injections = dict()
    form = NewsLetterRegistrationForm()
    injections.update(form=form)
    return injections
_init_.py
from logging.config import dictConfig
from flask import Flask, url_for, current_app
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_talisman import Talisman
from flask_compress import Compress
from flask_mail import Mail
import os

config = {
    "SECRET_KEY": os.environ.get("SECRET_KEY"),
    "DEBUG": True,
    "SQLALCHEMY_DATABASE_URI": os.environ.get("DATABASE_URL"),
    "SQLALCHEMY_TRACK_MODIFICATIONS": False,
    "SQLALCHEMY_ECHO": False,
    "MAIL_SERVER": "mail.privateemail.com",
    "MAIL_PORT": 587,
    "MAIL_USE_SSL": False,
    "MAIL_USE_TLS": True,
    "MAIL_USERNAME": "test@example.com",
    "MAIL_PASSWORD": os.environ.get("NEWS_MAIL_PASSWORD"),
    "MAIL_DEFAULT_SENDER": "test@example.com"
}

talisman = Talisman()
db = SQLAlchemy()
bcrypt = Bcrypt()
compress = Compress()
mail = Mail()
app = Flask(__name__)

def create_app():
    app.config.from_mapping(config)
    talisman.init_app(app)
    db.init_app(app)
    bcrypt.init_app(app)
    compress.init_app(app)
    mail.init_app(app)

    from flask_app.users.routes import users
    app.register_blueprint(users)

    with app.app_context():
        db.create_all()
    return app
运行.py
from flask_app import create_app
错误日志:
Traceback (most recent call last):
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\smtplib.py", line 391, in getreply
    line = self.file.readline(_MAXLINE + 1)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\socket.py", line 669, in readinto
    return self._sock.recv_into(b)
ConnectionResetError: [WinError 10054] An existing connection was forcibly closed by the remote host

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 2464, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 2450, in wsgi_app
    response = self.handle_exception(e)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 1867, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\_compat.py", line 39, in reraise
    raise value
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\_compat.py", line 39, in reraise
    raise value
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\User\Work Stuff\example.com\flask_app\users\routes.py", line 18, in newsletter_subscribe
    send_welcome_email(user, request.host_url)
  File "C:\User\Work Stuff\example.com\flask_app\users\routes.py", line 42, in send_welcome_email 
    with mail.connect() as con:
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask_mail.py", 
line 144, in __enter__
    self.host = self.configure_host()
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask_mail.py", 
line 158, in configure_host
    host = smtplib.SMTP(self.mail.server, self.mail.port)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\smtplib.py", line 253, in __init__
    (code, msg) = self.connect(host, port)
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\smtplib.py", line 341, in connect
    (code, msg) = self.getreply()
  File "C:\Users\user\AppData\Local\Programs\Python\Python38-32\Lib\smtplib.py", line 394, in getreply
    raise SMTPServerDisconnected("Connection unexpectedly closed: "
smtplib.SMTPServerDisconnected: Connection unexpectedly closed: [WinError 10054] An existing connection was forcibly closed by the remote host

最佳答案

在我看来,您的邮件服务器正在关闭连接,因为您发出的请求超出了其配置允许的数量。如果您使用的是第三方邮件提供商,您可能需要检查您使用的服务是否提供任何方式来发送批量电子邮件,例如通过 API 或文件上传。或者,如果他们有办法为您更改该配置。
如果不可能:
一种解决方案是进行阻塞调用(time.sleep()),以降低发送邮件的频率:

import time

def send_welcome_email(user):
    with mail.connect() as con:
        html = render_template("welcome-email.html", name=user.name)
        subject = "Welcome!"
        msg = Message(
            subject=subject,
            recipients=[user.email],
            html=html
        )
        time.sleep(2) # 2 seconds, modify as you see fit
        con.send(msg)
另一种解决方案是将您的代码包装在 try catch block 中,并在出现异常时等待一段时间,然后再尝试再次发送电子邮件。
def send_welcome_email(user, is_retry=False):
    try:
        with mail.connect() as con:
            html = render_template("welcome-email.html", name=user.name)
            subject = "Welcome!"
            msg = Message(
                subject=subject,
                recipients=[user.email],
                html=html
            )
            con.send(msg)
    except SMTPServerDisconnected:
        time.sleep(60) # Wait for a while, 1 minute tends to be a good measure as most configurations specify how many requests can be made a minute. 
        if not is_retry:  # Only retry once -> you could modify this to make the use of a counter.
            send_welcome_email(user, is_retry=True) # Try again
   

关于python - Flask-Mail 队列消息被发送到不同的电子邮件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63235625/

相关文章:

python - 博托 + Python + AWS S3 : How to get last_modified attribute of specific file?

python - Python 脚本中的 EOFError

python - python中的图形连接

SharePoint 列表 Web 部件连接选项不可见

c - 简单 MPI 发送/接收程序中的行为不明确

heroku - 如何在应用程序内仅让工作人员在 Heroku 免费层上存活?

multithreading - 可能/推荐在 Go 中编写事件驱动的应用程序?

python - 如何在 Windows 上的 Python 3.9 上安装 psycopg2?

javascript - Flask webservice 的 HighCharts 查询参数问题

php - PHP 中的连接错误