Django chạy các nhiệm vụ (có thể) trong tương lai xa


9

Giả sử tôi có một mô hình Event. Tôi muốn gửi thông báo (email, đẩy, bất cứ điều gì) cho tất cả người dùng được mời khi sự kiện đã kết thúc. Một cái gì đó dọc theo dòng:

class Event(models.Model):
    start = models.DateTimeField(...)
    end = models.DateTimeField(...)
    invited = models.ManyToManyField(model=User)

    def onEventElapsed(self):
        for user in self.invited:
           my_notification_backend.sendMessage(target=user, message="Event has elapsed")

Bây giờ, tất nhiên, phần quan trọng là để gọi onEventElapsedbất cứ khi nào timezone.now() >= event.end. Hãy ghi nhớ, endcó thể là vài tháng kể từ ngày hiện tại.

Tôi đã nghĩ về hai cách cơ bản để làm điều này:

  1. Sử dụng một croncông việc định kỳ (giả sử, cứ sau năm phút hoặc lâu hơn) để kiểm tra xem có bất kỳ sự kiện nào đã trôi qua trong vòng năm phút cuối và thực hiện phương pháp của tôi không.

  2. Sử dụng celeryvà lên lịch onEventElapsedsử dụng etatham số sẽ được chạy trong tương lai (trong savephương thức mô hình ).

Xem xét lựa chọn 1, một giải pháp tiềm năng có thể django-celery-beat. Tuy nhiên, có vẻ hơi lạ khi chạy một tác vụ ở một khoảng thời gian cố định để gửi thông báo. Ngoài ra, tôi đã đưa ra một vấn đề (tiềm năng) có thể (có thể) dẫn đến một giải pháp không thanh lịch:

  • Kiểm tra cứ năm phút cho các sự kiện đã trôi qua trong năm phút trước? có vẻ run rẩy, có thể một số sự kiện bị bỏ lỡ (hoặc những sự kiện khác nhận được thông báo của họ gửi hai lần?). Workaroung tiềm năng: thêm trường boolean vào mô hình được đặt thành Truemột khi thông báo đã được gửi.

Sau đó, một lần nữa, tùy chọn 2 cũng có vấn đề của nó:

  • Tự xử lý tình huống khi thời gian bắt đầu / kết thúc sự kiện được di chuyển. Khi sử dụng celery, người ta sẽ phải lưu trữ taskID(dễ dàng, ofc) và thu hồi tác vụ một khi ngày đã thay đổi và đưa ra một nhiệm vụ mới. Nhưng tôi đã đọc, rằng cần tây có vấn đề (cụ thể về thiết kế) khi xử lý các tác vụ được chạy trong tương lai: Vấn đề mở trên github . Tôi nhận ra làm thế nào điều này xảy ra và tại sao nó là tất cả nhưng tầm thường để giải quyết.

Bây giờ, tôi đã đi qua một số thư viện có khả năng giải quyết vấn đề của tôi:

  • celery_longterm_scheduler (Nhưng điều này có nghĩa là tôi không thể sử dụng cần tây như trước đây, vì lớp Trình lập kế hoạch differend? Điều này cũng liên quan đến việc sử dụng django-celery-beat... Sử dụng bất kỳ hai khung công tác nào, vẫn có thể xếp hàng công việc (mà chỉ chạy lâu hơn một chút chứ không phải vài tháng nữa?)
  • django-apscheduler , sử dụng apscheduler. Tuy nhiên, tôi không thể tìm thấy bất kỳ thông tin nào về cách nó sẽ xử lý các tác vụ được chạy trong tương lai xa.

Có một lỗ hổng cơ bản với cách tôi đang tiếp cận điều này? Tôi vui mừng cho bất kỳ đầu vào bạn có thể có.

Lưu ý: Tôi biết điều này có thể là một số ý kiến ​​dựa trên, tuy nhiên, có thể có một điều rất cơ bản mà tôi đã bỏ lỡ, bất kể những gì có thể được coi là xấu xí hay thanh lịch.


1
Tôi muốn nói rằng cách tiếp cận của bạn phụ thuộc vào việc ngay sau khi sự kiện trôi qua, người dùng cuối cần thông báo. Tôi đã có một vấn đề tương tự trong đó người dùng chỉ cần biết vào ngày hôm sau cho bất kỳ cuộc hẹn nào bị bỏ lỡ vào ngày hôm trước. Vì vậy, trong trường hợp này, tôi đã chạy một công việc định kỳ vào lúc nửa đêm và, như bạn đề xuất, một trường boolean để gắn thẻ xem các thông báo đã được gửi chưa. Đó là một cách rất đơn giản và rẻ tiền để làm điều đó.
Hayden Eastwood

1
Theo tôi, câu trả lời là về số lượng sự kiện bạn cần gửi. Nếu bạn có hàng trăm sự kiện được gửi mỗi ngày, thì không có vấn đề gì trong tương lai là một sự kiện duy nhất: sử dụng giải pháp đầu tiên (điều chỉnh thời gian lặp lại dựa trên nhu cầu của bạn), bạn có thể chạy tác vụ đọc dữ liệu cập nhật.
Dos

@HaydenEastwood Không quan trọng là người đó nhận được nó ngay lập tức, nhưng trong vòng 2 - 5 phút trong ngày kết thúc sẽ ổn. Vì vậy, bạn đã làm một cái gì đó tương tự như opion 1 của tôi?
Hafnernuss

1
@Hafnernuss Có - Tôi nghĩ rằng một cuộc gọi cron đơn giản với một trường trong cơ sở dữ liệu cho dù tin nhắn đã được gửi có phù hợp với trường hợp của bạn không.
Hayden Eastwood

1
Dramatiq sử dụng một aproach khác ngoài Celery khi thực hiện các nhiệm vụ (không phải bộ nhớ đói cho công nhân) và có thể hoạt động trong trường hợp của bạn, xem Dramatiq.io/guide.html#schedcing-messages . Nhưng như họ nói - nhà môi giới tin nhắn không phải là DB - khi bạn cần lập kế hoạch cho sự kiện dài hạn, giải pháp đầu tiên của bạn sẽ tốt hơn. Vì vậy, bạn có thể kết hợp cả hai: đặt các sự kiện trong MB, sau 1 ngày và hết hạn, chúng sẽ chuyển đến DB và sẽ được gửi qua cron.
frost-nzcr4

Câu trả lời:


2

Chúng tôi đang làm một cái gì đó như thế này trong công ty tôi làm việc và giải pháp khá đơn giản.

Có nhịp cron / cần tây chạy mỗi giờ để kiểm tra xem có cần gửi thông báo nào không. Sau đó gửi những thông báo đó và đánh dấu chúng là xong. Bằng cách này, ngay cả khi thời gian thông báo của bạn là nhiều năm trước, nó vẫn sẽ được gửi. Sử dụng ETA KHÔNG phải là cách để chờ đợi trong một thời gian chờ đợi rất lâu, bộ nhớ cache / amqp của bạn có thể bị mất dữ liệu.

Bạn có thể giảm khoảng thời gian của bạn tùy thuộc vào nhu cầu của bạn, nhưng hãy đảm bảo rằng chúng không trùng nhau.

Nếu một giờ là quá lớn so với chênh lệch múi giờ, thì những gì bạn có thể làm là, chạy một lịch trình mỗi giờ. Logic sẽ giống như

  1. chạy một tác vụ (hãy gọi tác vụ lên lịch này) hàng giờ để nhận tất cả các thông báo cần gửi trong giờ tiếp theo (thông qua nhịp cần tây) -
  2. Lên lịch các thông báo đó qua application_async (eta) - đây sẽ là gửi thực tế

Sử dụng phương pháp đó sẽ giúp bạn có cả hai thế giới tốt nhất (eta và beat)


1
Cảm ơn bạn. Đó chính xác là những gì tôi đã làm!
Hafnernuss
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.