当前位置: 首页 > news >正文

项目1:异步邮件发送系统实战

异步邮件发送项目目录结构

异步发送邮件/
├── image.png
├── app.py
├── celery_config.py
├── tasks.py
└── templates/└── index.html

项目文件说明:

  1. 根目录 (异步发送邮件/)

    • image.png - 项目相关图片

    • app.py - Flask应用主文件,包含路由和视图函数

    • celery_config.py - Celery配置,设置消息代理和结果后端

    • tasks.py - 定义Celery任务,包含邮件发送功能

  2. templates目录

    • index.html - 邮件发送前端页面,包含表单和JavaScript

技术栈说明:

  • Flask - Web框架,处理HTTP请求

  • Celery - 分布式任务队列,处理异步邮件发送

  • Redis - 作为Celery的消息代理和结果后端

  • Flask-Mail - 邮件发送扩展

  • HTML/CSS/JavaScript - 前端用户界面

这个项目实现了异步邮件发送功能,用户在前端填写表单后,任务会被提交到Celery队列异步处理,前端通过轮询方式检查任务状态。

步骤

1.先写异步的配置文件

# 创建一个celery_config.py 文件
class CeleryConfig:# 使用 Redis 作为消息代理  :6379  是redis默认端口号broker_url = 'redis://localhost:6380/0'# 任务结果存储(禁用可提升性能)result_backend = 'redis://localhost:6380/1'# 定时任务配置(可选)beat_schedule = {'every-30-seconds': {'task': 'tasks.periodic_task','schedule': 30.0,  # 每30秒执行},}

2.写请求页面路由

from flask import Flask,render_template,jsonify,request
from tasks import celery,send_async_email
app = Flask(__name__)
​
# 请求页面路由
@app.route('/')
def index():return render_template('index.html')

3.写请求页面的html

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>邮件发送</title><style>* {box-sizing: border-box;margin: 0;padding: 0;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;}
​body {background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);padding: 20px;display: flex;justify-content: center;align-items: center;min-height: 100vh;}
​.container {background-color: white;border-radius: 10px;box-shadow: 0 5px 20px rgba(0, 0, 0, 0.15);padding: 30px;width: 100%;max-width: 500px;}
​h1 {text-align: center;margin-bottom: 25px;color: #2c3e50;font-size: 28px;padding-bottom: 10px;border-bottom: 2px solid #eee;}
​.form-group {margin-bottom: 20px;}
​label {display: block;margin-bottom: 8px;font-weight: 600;color: #34495e;font-size: 16px;}
​input, textarea {width: 100%;padding: 14px;border: 1px solid #ddd;border-radius: 6px;font-size: 16px;transition: all 0.3s ease;}
​input:focus, textarea:focus {outline: none;border-color: #3498db;box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2);}
​textarea {min-height: 180px;resize: vertical;}
​button {background: linear-gradient(to right, #3498db, #2980b9);color: white;border: none;border-radius: 6px;padding: 15px;font-size: 18px;cursor: pointer;width: 100%;transition: all 0.3s ease;font-weight: 600;letter-spacing: 1px;margin-top: 10px;}
​button:hover {background: linear-gradient(to right, #2980b9, #3498db);transform: translateY(-2px);box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);}
​.note {text-align: center;margin-top: 20px;color: #7f8c8d;font-size: 14px;border-top: 1px solid #eee;padding-top: 15px;}
​@media (max-width: 600px) {.container {padding: 20px;}
​h1 {font-size: 24px;}
​input, textarea {padding: 12px;}}</style>
</head>
<body><div class="container"><h1>发送邮件</h1><form id="emailForm"><div class="form-group"><label for="email">收件邮箱:</label><input type="email" id="email" name="email" required placeholder="请输入收件人邮箱地址"></div>
​<div class="form-group"><label for="subject">邮件主题:</label><input type="text" id="subject" name="subject" required placeholder="请输入邮件主题"></div>
​<div class="form-group"><label for="content">邮件内容:</label><textarea id="content" name="content" required placeholder="请输入邮件内容"></textarea></div>
​<button type="submit">发送邮件</button></form>
​<div class="note">注意:这是一个前端演示页面,实际发送功能需要后端支持。</div></div>
</body>
</html>

4.写POST请求的send路由

from flask import Flask,render_template,jsonify,request
from tasks import celery,send_async_email
app = Flask(__name__)
​
# 请求页面路由
@app.route('/')
def index():return render_template('index.html')
​
# 发送邮件路由
@app.route('/send',methods=["POST"])
def send_email():# 获取请求的数据data = request.form if request.form else request.get_json()email = data['email']subject = data['subject']content = data['content']………………………………………………………………………………………………………………………………

5.写异步任务文件tasks.py

from celery import Celery
from flask import Flask
from flask_mail import Mail, Message
​
# 初始化Celery实例
celery = Celery(__name__)
# 导入异步配置文件
celery.config_from_object('celery_config.CeleryConfig')
​
def create_app():app = Flask(__name__)
​# 邮件服务器配置(以网易邮箱为例)app.config['MAIL_SERVER'] = 'smtp.163.com'  # SMTP服务器地址app.config['MAIL_PORT'] = 465  # SSL加密端口app.config['MAIL_USE_SSL'] = True  # 启用SSL加密app.config['MAIL_USERNAME'] = '19086325659@163.com'  # 发件邮箱app.config['MAIL_PASSWORD'] = 'hsbbh'  # 邮箱授权码(非密码)app.config['MAIL_DEFAULT_SENDER'] = '19086325659@163.com'  # 默认发件人return app
​
flask_app = create_app()
mail = Mail(flask_app)
​
# 发送邮件的方法
def send_email(to, subject, content, **kwargs):msg = Message(subject, recipients=[to])msg.body = content  # 纯文本内容mail.send(msg)
​
# max_retries限制任务最低重试3次
@celery.task(bind=True,max_retries=3)
def send_async_email(self,email,subject,content):try:with flask_app.app_context():send_email(to=email,subject=subject,content=content)return f'邮件成功发送至{email}'# 如果发送错误则重试,并且60秒重试一次,最多3次except Exception as e:self.retry(exc = e,countdown=60)

6.继续补充send路由

from flask import Flask,render_template,jsonify,request
from tasks import celery,send_async_email
app = Flask(__name__)
​
# 请求页面路由
@app.route('/')
def index():return render_template('index.html')
​
# 发送邮件路由
@app.route('/send',methods=["POST"])
def send_email():# 获取请求的数据data = request.form if request.form else request.get_json()email = data['email']subject = data['subject']content = data['content']# 触发异步任务task = send_async_email.delay(email,subject,content)return jsonify({'status':'邮件任务已提交','task_id':task.id})

7.写查看任务状态路由

from flask import Flask,render_template,jsonify,request
from tasks import celery,send_async_email
app = Flask(__name__)
​
# 请求页面路由
@app.route('/')
def index():return render_template('index.html')
​
# 发送邮件路由
@app.route('/send',methods=["POST"])
def send_email():# 获取请求的数据data = request.form if request.form else request.get_json()email = data['email']subject = data['subject']content = data['content']# 触发异步任务task = send_async_email.delay(email,subject,content)return jsonify({'status':'邮件任务已提交','task_id':task.id})
​
​
# 查看状态路由
@app.route('/status/<task_id>')
def task_status(task_id):# 获取任务状态task_result = celery.AsyncResult(task_id)
​return jsonify({"status": task_result.status,"result": task_result.result if task_result.ready() else None})
​
​
if __name__ == '__main__':app.run(debug=True)

8.写index.html页面的js

<script>const emailForm = document.querySelector('#emailForm')emailForm.addEventListener('submit', (e) => {e.preventDefault()// 获取表单数据const email = document.querySelector('#email').valueconst subject = document.querySelector('#subject').value || '默认主题'const content = document.querySelector('#content').value || '这是一封测试邮件'// 给用户提示const note = document.querySelector('.note')note.innerHTML = '邮件正在发送中……'// 发送POST请求fetch('/send',{method:'POST',headers:{'Content-Type':'application/x-www-form-urlencoded'},body:`email=${email}&subject=${subject}&content=${content}`}).then(res => res.json()).then(data => {if (data.task_id){// console.log(data.task_id)// 开始检查任务checkTaskStatus(data.task_id)}}).catch(err => {note.innerHTML = `网络错误:${err.message}`})})function checkTaskStatus(taskid){const note = document.querySelector('.note')const checkTask = () =>{fetch(`/status/${taskid}`).then(res => res.json()).then(data => {if(data.status == 'SUCCESS'){note.innerHTML = '邮件发送成功'}else if(data.status == 'FAILURE'){note.innerHTML = '邮件发送失败'}else if(data.status == 'PENDING'){note.innerHTML = '邮件发送中'setTimeout(checkTask,2000)}else if(data.status == 'RETRY'){note.innerHTML = '尝试重连中'setTimeout(checkTask,3000)}else {note.innerHTML = '处理中'setTimeout(checkTask,3000)}}).catch(err =>{note.innerHTML = '出错啦'})}checkTask()}
</script>

9.启动服务并进行调试

终端1 - 启动Redis

redis-server

终端2 - 启动Celery worker

celery -A tasks.celery worker --loglevel=info

终端3 - 启动Flask应用

python app.py

http://www.dtcms.com/a/350908.html

相关文章:

  • 自由学习记录(88)
  • 设计一个完整可用的 Spring Boot Starter
  • 深入浅出 ArrayList:从基础用法到底层原理的全面解析(下)
  • 2025职场进阶:低门槛技能实用手册
  • 编写Linux下usb设备驱动方法:probe函数中要进行的工作
  • css新特性
  • openharmony之DRM开发:数字知识产权保护揭秘
  • 智能体框架CAMEL-第三章
  • 学习嵌入式的第二十五天——哈希表和内核链表
  • 基于SpringBoot的物资管理系统【2026最新】
  • Linux网络服务(六)——iptables Forward实现内网服务暴露与访问外网
  • 直播美颜SDK技术解析:人脸美型功能的算法原理与实现方案
  • linux环境下 - 如何干净地卸载掉nvidia驱动
  • 工业通信协议综合调研报告
  • 深入浅出 ArrayList:从基础用法到底层原理的全面解析(上)
  • vue-Router中通过路由地址path中的数据转换为props传参,不建议添加多个可选参数
  • More Effective C++ 条款07:不要重载、和,操作符
  • linux的conda配置与应用阶段的简单指令备注
  • Typora + PicList + Gitee 图床完整配置教程
  • 《P1656 炸铁路》
  • C++ 编译链接杂谈——前向声明
  • JavaScript 类中静态变量与私有变量的区别及用法
  • eniac:世界上第一台通用电子计算机的传奇
  • 开发避坑指南(36):Java字符串Base64编码实战指南
  • 深度学习-----《PyTorch深度学习核心应用解析:从环境搭建到模型优化的完整实践指南》
  • 初步了解多线程
  • 交换机是如何同时完成帧统计与 BER/FEC 分析的
  • 【应急响应工具教程】SPECTR3:通过便携式 iSCSI 实现远程证据的只读获取与分析
  • [pilot智驾系统] 模型守护进程(modeld)
  • rbio1:以生物学世界模型为软验证器训练科学推理大语言模型