Python之aetosky-job-processing包语法、参数和实际应用案例

AI赋能编程语言挑战赛 10w+人浏览 245人参与

Python aetosky-job-processing 包完全指南

一、包概述

aetosky-job-processing 是一个轻量级Python任务处理框架,专注于异步任务调度、并发执行、任务依赖管理失败重试等核心场景。其设计目标是简化后台任务处理流程,支持单机/分布式部署(基于Redis/RabbitMQ),适用于定时任务、批量数据处理、异步接口响应等场景。

核心优势:

  • 低侵入式API,快速集成现有项目;
  • 支持任务优先级、依赖链、超时控制;
  • 内置失败重试、死信队列机制;
  • 兼容多后端存储(内存/Redis/RabbitMQ);
  • 支持任务状态监控和结果回调。

二、安装指南

1. 基础安装(仅本地内存队列)

pip install aetosky-job-processing

2. 完整安装(支持Redis/RabbitMQ分布式)

# 包含Redis和RabbitMQ依赖
pip install aetosky-job-processing[full]

3. 验证安装

import aetosky_job_processing as ajp

print(ajp.__version__)  # 输出版本号(如0.3.2),验证安装成功

三、核心语法与参数说明

1. 核心组件

组件作用常用类/函数
任务定义器声明任务逻辑、参数约束、执行配置@ajp.job() 装饰器
任务队列存储待执行任务,支持优先级排序ajp.Queue()ajp.RedisQueue()
任务执行器消费队列任务,支持并发/异步执行ajp.Worker()ajp.AsyncWorker()
调度器支持定时/延迟任务调度ajp.Scheduler()
结果处理器处理任务执行结果/失败回调ajp.ResultHandler()

2. 任务定义装饰器 @ajp.job() 参数

参数名类型默认值说明
queue_namestr“default”任务所属队列名称
priorityint5优先级(1-10,1最高)
retry_countint0失败重试次数
retry_delayint/float1.0重试间隔(秒)
timeoutint/floatNone任务超时时间(秒,None表示无限制)
dependenciesList[str][]依赖的任务ID列表(需先执行依赖任务)
result_ttlint3600结果缓存时间(秒)
on_successCallableNone成功回调函数(参数:任务结果)
on_failureCallableNone失败回调函数(参数:异常信息)

3. 队列 ajp.Queue() 参数

参数名类型默认值说明
namestr“default”队列名称
backendstr“memory”存储后端(“memory”/“redis”/“rabbitmq”)
redis_urlstrNoneRedis连接URL(backend="redis"时必填)
rabbitmq_urlstrNoneRabbitMQ连接URL(backend="rabbitmq"时必填)
max_sizeintNone队列最大容量(None表示无限制)

4. 执行器 ajp.Worker() 参数

参数名类型默认值说明
queuesList[Queue][]监听的队列列表
workersint4并发工作进程数
daemonboolTrue是否以守护进程运行
log_levelstr“INFO”日志级别(“DEBUG”/“INFO”/“WARN”/“ERROR”)
shutdown_timeoutint10关闭超时时间(秒)

5. 调度器 ajp.Scheduler() 参数

参数名类型默认值说明
queueQueueNone调度任务的目标队列
intervalint/float60调度检查间隔(秒)
daemonboolTrue是否以守护进程运行

四、8个实际应用案例

案例1:基础同步任务(本地内存队列)

场景:简单的同步任务执行,适用于单机轻量场景。

import aetosky_job_processing as ajp

# 1. 定义任务(使用默认队列)
@ajp.job()
def add(a, b):
    """简单加法任务"""
    return a + b

# 2. 创建队列和执行器
queue = ajp.Queue(name="default", backend="memory")
worker = ajp.Worker(queues=[queue], workers=2)

# 3. 启动执行器(非阻塞,守护进程模式)
worker.start()

# 4. 提交任务
job_id = add.delay(3, 5)  # 异步提交,返回任务ID
print(f"提交任务,ID: {job_id}")

# 5. 获取任务结果(阻塞直到任务完成)
result = ajp.get_job_result(job_id, timeout=10)
print(f"任务结果: 3 + 5 = {result}")

# 6. 停止执行器
worker.stop()

案例2:带重试和超时的异步任务

场景:网络请求类任务(可能失败),需要重试和超时控制。

import aetosky_job_processing as ajp
import requests

# 1. 定义带重试和超时的任务
@ajp.job(
    retry_count=3,        # 失败重试3次
    retry_delay=2,        # 重试间隔2秒
    timeout=5,            # 任务超时5秒
    on_failure=lambda e: print(f"任务失败: {str(e)}")  # 失败回调
)
def fetch_url(url):
    """请求URL内容"""
    response = requests.get(url, timeout=3)
    response.raise_for_status()  # 状态码非200抛出异常
    return response.text[:100]  # 返回前100个字符

# 2. 创建Redis队列(支持分布式)
queue = ajp.Queue(
    name="http_queue",
    backend="redis",
    redis_url="redis://localhost:6379/0"  # Redis连接地址
)
worker = ajp.Worker(queues=[queue], workers=3)
worker.start()

# 3. 提交任务(模拟无效URL触发重试)
job_id1 = fetch_url.delay("https://www.baidu.com")  # 有效URL
job_id2 = fetch_url.delay("https://invalid-url-12345.com")  # 无效URL

# 4. 获取结果
try:
    result1 = ajp.get_job_result(job_id1, timeout=10)
    print(f"百度首页前100字符: {result1}")
except TimeoutError:
    print("任务1超时")

try:
    result2 = ajp.get_job_result(job_id2, timeout=10)
except Exception as e:
    print(f"任务2最终失败: {str(e)}")

worker.stop()

案例3:任务依赖链(先执行前置任务)

场景:数据处理流程,需先完成数据下载,再进行解析。

import aetosky_job_processing as ajp
import json

# 1. 前置任务:下载数据
@ajp.job(queue_name="data_queue")
def download_data(api_url):
    import requests
    response = requests.get(api_url)
    return response.json()  # 返回原始数据

# 2. 后置任务:依赖下载任务,解析数据
@ajp.job(
    queue_name="parse_queue",
    dependencies=["download_job_id"],  # 依赖前置任务ID
    on_success=lambda res: print(f"解析完成,结果: {res}")
)
def parse_data(raw_data):
    # 解析逻辑:提取关键字段
    return {
        "total": len(raw_data),
        "first_item": raw_data[0] if raw_data else None
    }

# 3. 创建队列和执行器(监听两个队列)
data_queue = ajp.Queue(name="data_queue", backend="redis", redis_url="redis://localhost:6379/0")
parse_queue = ajp.Queue(name="parse_queue", backend="redis", redis_url="redis://localhost:6379/0")
worker = ajp.Worker(queues=[data_queue, parse_queue], workers=4)
worker.start()

# 4. 提交任务(先提交前置任务,再提交依赖任务)
download_job_id = download_data.delay("https://jsonplaceholder.typicode.com/todos")
print(f"下载任务ID: {download_job_id}")

# 提交后置任务时,指定依赖的任务ID
parse_job_id = parse_data.delay(
    raw_data=ajp.DependsOn(download_job_id)  # 表示依赖download_job_id的结果
)
print(f"解析任务ID: {parse_job_id}")

# 5. 等待结果
parse_result = ajp.get_job_result(parse_job_id, timeout=20)
print(f"最终解析结果: {parse_result}")

worker.stop()

案例4:定时任务(周期性执行)

场景:定时备份数据库(每天凌晨2点执行)。

import aetosky_job_processing as ajp
from datetime import datetime

# 1. 定义定时任务
@ajp.job(queue_name="backup_queue")
def backup_database(db_name):
    """模拟数据库备份"""
    backup_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    print(f"[{backup_time}] 备份数据库 {db_name} 成功")
    return f"backup_{db_name}_{backup_time}.sql"

# 2. 创建队列和调度器
queue = ajp.Queue(name="backup_queue", backend="redis", redis_url="redis://localhost:6379/0")
worker = ajp.Worker(queues=[queue], workers=1)
scheduler = ajp.Scheduler(queue=queue, interval=60)  # 每分钟检查一次调度规则

# 3. 添加定时任务(每天凌晨2点执行)
# 使用cron表达式:分 时 日 月 周(*表示任意)
scheduler.add_cron_job(
    job_func=backup_database,
    cron_expr="0 2 * * *",  # 每天2:00执行
    args=("mydb",)  # 任务参数
)

# 4. 启动执行器和调度器
worker.start()
scheduler.start()

# 5. 保持主线程运行(实际使用中可部署为服务)
try:
    while True:
        import time
        time.sleep(3600)
except KeyboardInterrupt:
    scheduler.stop()
    worker.stop()

案例5:高优先级任务插队

场景:普通任务和紧急任务分离,紧急任务优先执行。

import aetosky_job_processing as ajp
import time

# 1. 定义普通任务(优先级5,默认)
@ajp.job(queue_name="normal_queue", priority=5)
def process_order(order_id):
    """处理普通订单"""
    time.sleep(2)  # 模拟处理耗时
    return f"普通订单 {order_id} 处理完成"

# 2. 定义紧急任务(优先级1,最高)
@ajp.job(queue_name="urgent_queue", priority=1)
def process_urgent_order(order_id):
    """处理紧急订单"""
    time.sleep(1)
    return f"紧急订单 {order_id} 处理完成"

# 3. 创建队列(优先级队列会自动排序)
normal_queue = ajp.Queue(name="normal_queue", backend="memory")
urgent_queue = ajp.Queue(name="urgent_queue", backend="memory")

# 4. 执行器监听两个队列(优先级高的任务先被消费)
worker = ajp.Worker(queues=[urgent_queue, normal_queue], workers=2)
worker.start()

# 5. 提交任务(先提交普通订单,再提交紧急订单)
normal_job1 = process_order.delay("ORD-1001")
normal_job2 = process_order.delay("ORD-1002")
urgent_job = process_urgent_order.delay("ORD-9999")  # 紧急任务插队

# 6. 获取结果(验证紧急任务先完成)
print(ajp.get_job_result(urgent_job, timeout=5))  # 先输出
print(ajp.get_job_result(normal_job1, timeout=5))
print(ajp.get_job_result(normal_job2, timeout=5))

worker.stop()

案例6:批量任务提交与结果聚合

场景:批量处理用户数据,最终聚合结果。

import aetosky_job_processing as ajp
import random

# 1. 定义单用户处理任务
@ajp.job(queue_name="user_queue", retry_count=2)
def process_user(user_id):
    """模拟处理用户数据:计算积分"""
   积分 = random.randint(10, 100)  # 模拟业务逻辑
    return {"user_id": user_id, "points": 积分}

# 2. 创建队列和执行器
queue = ajp.Queue(name="user_queue", backend="redis", redis_url="redis://localhost:6379/0")
worker = ajp.Worker(queues=[queue], workers=5)  # 5个并发进程加速批量处理
worker.start()

# 3. 批量提交任务(100个用户)
user_ids = [f"USER-{i:03d}" for i in range(100)]
job_ids = [process_user.delay(uid) for uid in user_ids]
print(f"提交了 {len(job_ids)} 个用户处理任务")

# 4. 聚合所有任务结果
total_points = 0
user_results = []
for job_id in job_ids:
    try:
        result = ajp.get_job_result(job_id, timeout=15)
        user_results.append(result)
        total_points += result["points"]
    except Exception as e:
        print(f"用户 {job_id} 处理失败: {str(e)}")

# 5. 输出聚合结果
print(f"批量处理完成,共 {len(user_results)} 个用户")
print(f"总积分: {total_points}")
print(f"平均积分: {total_points / len(user_results):.2f}")

worker.stop()

案例7:分布式任务(多Worker共享Redis队列)

场景:多台服务器分布式处理任务,通过Redis共享队列。

服务端1(提交任务)
import aetosky_job_processing as ajp

# 定义任务(无需启动Worker,仅提交)
@ajp.job(queue_name="distributed_queue")
def compute_factorial(n):
    """计算阶乘(CPU密集型任务)"""
    result = 1
    for i in range(1, n+1):
        result *= i
    return f"{n}! = {result}"

# 连接Redis队列(与其他Worker共享)
queue = ajp.Queue(
    name="distributed_queue",
    backend="redis",
    redis_url="redis://192.168.1.100:6379/0"  # 共享Redis地址
)

# 提交多个CPU密集型任务
for n in [20, 30, 40, 50]:
    job_id = compute_factorial.delay(n)
    print(f"提交任务: {n}!,ID: {job_id}")
服务端2(Worker节点1)
import aetosky_job_processing as ajp

# 连接相同的Redis队列
queue = ajp.Queue(
    name="distributed_queue",
    backend="redis",
    redis_url="redis://192.168.1.100:6379/0"
)

# 启动4个并发Worker(利用服务器2的CPU)
worker = ajp.Worker(queues=[queue], workers=4)
worker.start()

# 保持运行
try:
    while True:
        import time
        time.sleep(3600)
except KeyboardInterrupt:
    worker.stop()
服务端3(Worker节点2)
# 与服务端2代码完全一致,启动后自动消费Redis队列中的任务
# 实现分布式负载均衡
import aetosky_job_processing as ajp

queue = ajp.Queue(
    name="distributed_queue",
    backend="redis",
    redis_url="redis://192.168.1.100:6379/0"
)

worker = ajp.Worker(queues=[queue], workers=4)
worker.start()

try:
    while True:
        import time
        time.sleep(3600)
except KeyboardInterrupt:
    worker.stop()

案例8:任务结果回调与死信队列

场景:重要任务失败后,将任务转入死信队列,后续人工处理。

import aetosky_job_processing as ajp

# 1. 定义死信队列(存储最终失败的任务)
dead_letter_queue = ajp.Queue(
    name="dead_letter_queue",
    backend="redis",
    redis_url="redis://localhost:6379/0"
)

# 2. 定义失败回调函数(将任务转入死信队列)
def handle_final_failure(job_id, exception):
    """任务最终失败后的回调:转入死信队列"""
    job = ajp.get_job(job_id)  # 获取原始任务信息
    dead_letter_queue.enqueue(
        job_func=job.func,
        args=job.args,
        kwargs=job.kwargs,
        metadata={"failure_reason": str(exception), "job_id": job_id}
    )
    print(f"任务 {job_id} 转入死信队列,原因: {str(exception)}")

# 3. 定义核心任务(重试3次后转入死信队列)
@ajp.job(
    queue_name="core_queue",
    retry_count=3,
    retry_delay=2,
    on_failure=handle_final_failure  # 最终失败回调
)
def process_payment(payment_id, amount):
    """模拟支付处理(可能失败)"""
    import random
    if random.random() < 0.7:  # 70%概率失败
        raise ValueError(f"支付网关异常,支付ID: {payment_id}")
    return f"支付 {amount} 元成功,ID: {payment_id}"

# 4. 创建核心队列和执行器
core_queue = ajp.Queue(
    name="core_queue",
    backend="redis",
    redis_url="redis://localhost:6379/0"
)
worker = ajp.Worker(queues=[core_queue], workers=2)
worker.start()

# 5. 提交10个支付任务
for i in range(10):
    process_payment.delay(payment_id=f"PAY-{i:03d}", amount=100+i*10)

# 6. 后续可单独处理死信队列(例如:人工重试)
def process_dead_letter():
    """处理死信队列中的任务"""
    while not dead_letter_queue.is_empty():
        task = dead_letter_queue.dequeue()
        print(f"处理死信任务: {task.metadata}")
        # 人工干预逻辑...

# 运行一段时间后检查死信队列
import time
time.sleep(30)
process_dead_letter()

worker.stop()

五、常见错误与解决方案

1. 错误:RedisConnectionError(Redis连接失败)

  • 原因:Redis服务未启动、连接URL错误、网络不通。
  • 解决方案
    1. 检查Redis服务是否运行(systemctl status redis);
    2. 验证Redis连接URL格式(redis://[用户名:密码@]主机:端口/数据库);
    3. 关闭防火墙或开放Redis端口(ufw allow 6379)。

2. 错误:TaskTimeoutError(任务超时)

  • 原因:任务执行时间超过timeout参数设置,或任务逻辑卡住。
  • 解决方案
    1. 增大timeout参数(如@ajp.job(timeout=30));
    2. 优化任务逻辑(拆分长任务、异步化IO操作);
    3. 检查任务是否存在死循环或阻塞(如未设置超时的网络请求)。

3. 错误:JobDependencyError(任务依赖失败)

  • 原因:依赖的任务执行失败或超时,导致当前任务无法执行。
  • 解决方案
    1. 检查依赖任务的日志,修复依赖任务的执行问题;
    2. 为依赖任务增加重试机制(retry_count);
    3. 在当前任务中处理依赖失败的情况(如on_failure回调)。

4. 错误:QueueFullError(队列满)

  • 原因:队列max_size设置过小,任务提交速度超过消费速度。
  • 解决方案
    1. 增大max_size参数(或设为None取消限制);
    2. 增加Worker并发数(workers参数);
    3. 优化任务执行效率,提高消费速度;
    4. 实现任务限流(避免短时间提交大量任务)。

5. 错误:TaskNotFoundError(任务未找到)

  • 原因:任务ID错误、结果缓存过期(result_ttl)、队列后端数据丢失。
  • 解决方案
    1. 验证任务ID是否正确(提交任务后保存job_id);
    2. 增大result_ttl参数(延长结果缓存时间);
    3. 使用可靠的后端(如Redis/RabbitMQ)替代内存队列(避免服务重启后数据丢失)。

6. 错误:ConcurrencyError(并发冲突)

  • 原因:多Worker同时操作共享资源(如文件、数据库),导致数据不一致。
  • 解决方案
    1. 对共享资源加锁(如threading.Lock、Redis分布式锁);
    2. 使用原子操作(如数据库事务、Redis原子命令);
    3. 避免多任务同时写入同一文件(采用分片写入或队列串行化)。

六、使用注意事项

1. 任务函数设计规范

  • 任务函数应幂等(多次执行结果一致):避免依赖全局变量、随机数(需固定种子)、时间戳(如必须使用,需传入而非函数内生成);
  • 任务参数应可序列化:仅使用字符串、数字、列表、字典等JSON可序列化类型(避免传入对象、函数等不可序列化数据);
  • 避免在任务函数中使用动态导入(如importlib.import_module),可能导致Worker节点导入失败。

2. 队列后端选择

  • 本地测试/轻量场景:使用backend="memory"(无需额外依赖,速度快);
  • 单机生产场景:使用backend="redis"(数据持久化,支持重启后恢复任务);
  • 分布式场景:使用backend="redis"backend="rabbitmq"(支持多Worker共享队列);
  • 高可靠性场景:优先选择RabbitMQ(支持更复杂的队列模式,如死信队列、延迟队列)。

3. 并发控制

  • 根据CPU核心数设置Worker并发数(推荐workers = CPU核心数 * 2 + 1);
  • IO密集型任务(如网络请求、数据库操作):可适当增加并发数;
  • CPU密集型任务(如计算、数据处理):并发数不宜超过CPU核心数(避免上下文切换开销)。

4. 日志与监控

  • 开启DEBUG日志(log_level="DEBUG")排查任务执行问题;
  • 定期监控队列长度(queue.size()),避免任务堆积;
  • 记录任务执行状态(成功/失败次数、执行时间),便于性能优化;
  • 对关键任务实现告警机制(如失败次数超过阈值时发送邮件/短信)。

5. 分布式部署注意事项

  • 所有Worker节点必须使用相同的队列后端(Redis/RabbitMQ)和配置;
  • 任务函数的代码在所有Worker节点上必须一致(避免版本不一致导致执行失败);
  • 共享资源(如数据库、文件存储)需支持分布式访问(避免单机存储瓶颈);
  • 配置Redis/RabbitMQ的持久化机制(避免服务重启后任务丢失)。

6. 资源释放

  • 任务执行完成后,及时释放资源(如数据库连接、文件句柄、网络连接);
  • 使用try...finally或上下文管理器(with语句)确保资源释放;
  • 避免在任务函数中创建长期存在的对象(如全局数据库连接池,应在Worker启动时初始化)。

七、总结

aetosky-job-processing 是一个灵活、轻量的任务处理框架,支持同步/异步、单机/分布式、定时/依赖任务等多种场景。通过简单的装饰器和API,可快速集成到Python项目中,解决后台任务处理的核心问题。

使用时需注意:合理选择队列后端、设计幂等任务函数、控制并发数、做好日志监控,避免常见的连接、超时、依赖问题。8个实际案例覆盖了大部分核心场景,可根据业务需求灵活调整参数和扩展功能。

《AI提示工程必知必会》为读者提供了丰富的AI提示工程知识与实战技能。《AI提示工程必知必会》主要内容包括各类提示词的应用,如问答式、指令式、状态类、建议式、安全类和感谢类提示词,以及如何通过实战演练掌握提示词的使用技巧;使用提示词进行文本摘要、改写重述、语法纠错、机器翻译等语言处理任务,以及在数据挖掘、程序开发等领域的应用;AI在绘画创作上的应用,百度文心一言和阿里通义大模型这两大智能平台的特性与功能,以及市场调研中提示词的实战应用。通过阅读《AI提示工程必知必会》,读者可掌握如何有效利用AI提示工程提升工作效率,创新工作流程,并在职场中脱颖而出。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王国平

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值