django-celery-beat, 스케줄링 함수가 작동하지 않을 때(windows에서는 celery4.x이후 지원하지 않음, 그런 대도 진행하고 싶다면)

이도현·2023년 11월 3일
0

Error Collection

목록 보기
13/13

우선 다른 해결방법도 있다.
이 분의 해결방법도 있고
https://blossoming-man.tistory.com/entry/%EC%9C%88%EB%8F%84%EC%9A%B0-celery%EA%B0%80-%EC%9E%91%EB%8F%99%EC%9D%84-%EC%95%88-%ED%95%98%EB%8A%94-%EC%9D%B4%EC%9C%A0-%ED%95%B4%EA%B2%B0%EC%B1%85
이 분의 해결방법도 있다.
https://velog.io/@jaewan/Celerywindow%EC%97%90%EC%84%9C-celery-task%EA%B0%80-%EC%8B%A4%ED%96%89%EB%90%98%EC%A7%80-%EC%95%8A%EB%8A%94-%EB%AC%B8%EC%A0%9C
아래에 제시될 해결방법과 다른 방법이다

문제

django-celery-beat를 활용하여 함수를 스케줄링했다.

우선 redis설치하고 redis-server는 필수입니다. 다른 툴도 있지만 저는 이걸 사용했어요

  • pjt/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'skapi.apps.SkapiConfig',
    'forcast',
    'rest_framework',
    'django_celery_beat',
    'django_celery_results',
]
...
CELERY_BEAT_SCHEDULE = {
    # 8시부터 16시까지 매 2시간마다 실행 (8, 10, 12, 14 시)
    'update_every_2_hours': {
        'task': 'skapi.tasks.periodic_update_congestion_data',
        'schedule': crontab(hour='8,10,12,14'),
        'args': ()
    },
    # 16시부터 22시까지 매시간 실행 (16, 17, 18, 19, 20, 21 시)
    'update_every_hour': {
        'task': 'skapi.tasks.periodic_update_congestion_data',
        'schedule': crontab(hour='16-21'),
        'args': ()
    },
}
  • pjt/celery.py
from __future__ import absolute_import, unicode_literals
import os
from celery import Celery

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'config.settings')

app = Celery('config')

app.config_from_object('django.conf:settings', namespace='CELERY')

app.autodiscover_tasks()
  • pjt/init.py
from __future__ import absolute_import, unicode_literals
from .celery import app as celery_app

__all__ = ('celery_app',)
  • app/task.py
from celery import shared_task
from .callapi import UpdateSkAPi
from django.db import transaction
from .models import AreaInfo
import logging

logger = logging.getLogger(__name__)

@shared_task()
def periodic_update_congestion_data():
    logger.info("Starting to update congestion data.")
    try:
        with transaction.atomic():
            AreaInfo.objects.all().delete()
            logger.info("All AreaInfo records have been deleted.")
        updater = UpdateSkAPi()
        updater.update_congestion_data()
        logger.info("Finished updating congestion data.")
    except Exception as e:
        logger.error(f"Error updating congestion data: {e}", exc_info=True)
  • 이후 worker와 beat실행
celery -A proj worker -l info

celery -A proj beat -l

terminal에서 찍히는 정보를 해석했을 때 스케줄링에 맞추어 잘 호출이 진행 되지만(db의 데이터를 전부 지우는 함수) db의 데이터는 계속 생존해 있었다.

1. 해결

  • 나와 비슷한 사례를 해결해주신 답변을 Stackoverflow에서 찾았다.

celery -A proj worker -l info --without-gossip --without-mingle --without-heartbeat -Ofair --pool=solo

celery -A proj beat -l info  --scheduler django_celery_beat.schedulers:DatabaseScheduler
  • gossip: 워커들 사이에 상태 정보를 교환하는 메터니즘.(없다면 네트워크 부하와 메모리 사용 줄음)

  • mingle: 워커가 시작할 때 다른 워커가 현재 상태에 대한 정보를 교환(없다면 시작시간 줄음)

  • heartbeat: 워커가 여전히 살아있다는 신호,(없다면 메세징 부하 줄음)

  • Ofair: 여러 개의 큐에서 메시지를 고르게 가져가게하여 특정 워커가 과부화 되는 것 바지

  • pool=solo: 워커의 작업 로드 관리를 단순화, 공유 리소스에 대한 경쟁이나 복잡한/ 멀티스레딩/멀티프로세싱 이슈가 있을 때 도움 됨

    필요없는 기능을 걷어내어 통신을 안정시키고, 작업 로드를 단순화 시켜서 된 것 같다.
    아마 -Ofair --pool=solo가 큰 해결책이었던 것 같다.

  • 하지만 진짜 문제를 찾기 위해 로그를 찍어봐야한다. 가 아니라 내 운영체제는 Windows
    Celery 4.x 이후부터는 Windows를 공식적으로 지원하지 않는다.

다음부터는 자신의 환경(운영체제, 메모리, 용량, 같이사용하려는 툴과의 호환 등)과 툴이 호환되는지 확인해보자

2. 이 외의 경우 문제를 찾기 위해 시도해 볼만한 것

1) admin 계정 확인

celery -A proj beat -l info  --scheduler django_celery_beat.schedulers:DatabaseScheduler
  • 위를 실행하면 admin 계정의 스케줄링된 목록에 대해서 확인할 수 있다.

2) logging

import logging

logger = logging.getLogger(__name__)

@shared_task()
def periodic_update_congestion_data():
    logger.info("Starting to update congestion data.")
    try:
        with transaction.atomic():
            AreaInfo.objects.all().delete()
            logger.info("All AreaInfo records have been deleted.") # 로깅
        updater = UpdateSkAPi()
        updater.update_congestion_data()
        logger.info("Finished updating congestion data.")
    except Exception as e:
        logger.error(f"Error updating congestion data: {e}", exc_info=True) # 로깅
  • settings.py
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'Debug', # 레벨로는 INFO, ERROR, DEBUG, CRITICAL이 있다.
            'class': 'logging.FileHandler',
            # 파일의 경로설정이다. 만약 따로 관리하고 싶다면 log/debug.log 이렇게 해도된다.
            'filename': 'debug.log', 
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': 'debug.log',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'level': 'DEBUG',
            'propagate': True,
        },
    },
}

DEBUG를 찍으면 로그내용이 너무많다. 위를 지우고 출력할 레벨의 로그만 확인해도 됩니다.

profile
좋은 지식 나누어요

0개의 댓글