Celery trong Python: chạy tác vụ nền và lập lịch công việc

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Một số việc không nên làm ngay trong lúc người dùng chờ: gửi hàng trăm email, tạo báo cáo PDF, xử lý video. Celery là thư viện hàng đợi tác vụ phổ biến nhất trong Python, cho phép bạn đẩy những việc đó ra nền và còn có thể lập lịch chạy định kỳ.

Các thành phần chính

Celery gồm ba phần phối hợp với nhau:

  • Ứng dụng của bạn (producer): gửi yêu cầu chạy tác vụ.
  • Broker: hàng đợi trung gian lưu các yêu cầu, thường là Redis hoặc RabbitMQ.
  • Worker: tiến trình riêng lấy tác vụ từ broker và thực thi.

Nếu cần lưu kết quả của tác vụ, bạn cấu hình thêm một result backend (ví dụ Redis hoặc database).

Tác vụ đầu tiên

pip install celery redis
# tasks.pyfrom celery import Celeryapp = Celery("demo", broker="redis://localhost:6379/0", backend="redis://localhost:6379/1")@app.taskdef add(x, y):    return x + y@app.task(bind=True, max_retries=3)def fetch_report(self, report_id):    try:        return call_external_api(report_id)  # hàm tự viết    except ConnectionError as exc:        raise self.retry(exc=exc, countdown=30)

Khởi động worker và gọi tác vụ:

celery -A tasks worker --loglevel=info
from tasks import addresult = add.delay(2, 3)      # gửi vào hàng đợi, trả về ngayprint(result.get(timeout=10)) # 5

Hàm delay là cách ngắn gọn của apply_async. Trong web, bạn thường không gọi .get() trong request vì nó chặn chờ kết quả; thay vào đó lưu id tác vụ và cho client hỏi trạng thái sau.

Thử lại và xử lý lỗi

Tác vụ ngoài mạng có thể lỗi tạm thời. Dùng retry với countdown như trên, hoặc khai báo tự thử lại cho một số loại ngoại lệ:

@app.task(autoretry_for=(ConnectionError,), retry_backoff=True, max_retries=5)def sync_user(user_id):    ...

Hãy viết tác vụ sao cho có thể chạy lại mà không gây hậu quả (idempotent). Celery mặc định đảm bảo "ít nhất một lần", nên tác vụ có thể bị chạy hơn một lần trong một số tình huống như worker chết giữa chừng.

Lập lịch với Celery Beat

Beat là tiến trình lập lịch, đến giờ thì đẩy tác vụ vào hàng đợi:

from celery.schedules import crontabapp.conf.beat_schedule = {    "bao-cao-hang-ngay": {        "task": "tasks.fetch_report",        "schedule": crontab(hour=1, minute=0),        "args": (1,),    },}
celery -A tasks beat --loglevel=info

Chỉ chạy một tiến trình beat cho mỗi hệ thống, nếu không tác vụ sẽ bị lên lịch trùng lặp.

Thực hành tốt

  • Truyền tham số nhỏ: chỉ truyền id hoặc dữ liệu đơn giản, rồi nạp lại dữ liệu trong tác vụ, vì dữ liệu có thể đã đổi trong lúc chờ.
  • Đặt thời hạn: dùng soft_time_limit và time_limit để tác vụ treo không giữ worker mãi.
  • Chia hàng đợi: tách tác vụ nhanh và chậm sang các queue khác nhau, để việc nặng không chặn việc nhẹ.
  • Giám sát: dùng công cụ như Flower để xem worker, tác vụ đang chạy và thất bại.
  • Triển khai: chạy worker bằng systemd, Supervisor hoặc container, và khởi động lại sau mỗi lần cập nhật code.

Khi nào không cần Celery?

Với việc đơn giản, như gửi một email nhẹ, các giải pháp nhẹ hơn như thư viện hàng đợi nhỏ hoặc tác vụ nền tích hợp trong framework có thể đủ. Celery mạnh nhưng cũng đòi hỏi vận hành broker, worker và giám sát. Hãy dùng nó khi bạn thật sự có nhiều tác vụ, cần thử lại, lập lịch và mở rộng, chứ đừng thêm vào chỉ vì thấy phổ biến.

Tích hợp Celery với Django hoặc FastAPI

Trong Django, cách phổ biến là tạo file celery.py cạnh settings.py, nạp cấu hình từ settings với tiền tố CELERY_ và để Celery tự tìm file tasks.py trong từng ứng dụng. Với FastAPI, bạn khai báo ứng dụng Celery ở một module riêng, endpoint gọi delay rồi trả về id tác vụ cho client. Điểm chung là ranh giới rõ ràng: web chỉ nhận yêu cầu và xếp việc, worker mới là nơi làm việc nặng.

Khi tác vụ cần truy cập database, hãy lấy kết nối bên trong tác vụ và đóng đúng cách, đừng truyền đối tượng kết nối hay đối tượng model đang gắn với phiên làm việc qua hàng đợi, vì chúng không serialize an toàn. Truyền id, nạp lại đối tượng, xử lý, rồi lưu kết quả. Cách này vừa an toàn vừa giúp tác vụ luôn dùng dữ liệu mới nhất.

Lời khuyên vận hành

Hãy luôn kiểm tra lại bằng thực hành thay vì chỉ đọc lý thuyết: dựng một dự án nhỏ, cố tình tạo lỗi, quan sát log và công cụ gỡ lỗi, rồi sửa. Với Celery, hãy thử tắt worker giữa chừng, tắt Redis, hoặc làm tác vụ ném ngoại lệ, rồi quan sát hệ thống phản ứng thế nào. Những thử nghiệm này cho bạn sự tự tin khi gặp sự cố thật.

Kết luận

Celery giúp ứng dụng Python của bạn phản hồi nhanh và xử lý việc nặng một cách đáng tin cậy. Hãy bắt đầu bằng một tác vụ gửi email, chạy thử worker ở máy mình, rồi dần mở rộng sang lập lịch và giám sát khi nhu cầu xuất hiện.

Nếu muốn luyện tập cùng bài tập thực tế, hãy ghé các khóa học tại codebem.com khi bạn sẵn sàng.

author_photo
Nguyễn Đình Cường

0 comment