[scheduler]일정 시간마다 특정 작업을 실행하려면?
크롤링 작업을 통해 쇼핑몰 가격 정보 수정 기록을 확인하던 중, 확인해야 할 쇼핑몰이 늘어나고, 정확한 집계를 위해 시간을 맞춰야 했다. 특정 시간에 직접 실행하기엔 변수도 많고, 실수할 수도 있어 해당 작업을 서버에서 자동으로 실행하게 하려 했다. 그렇게 찾은 방식이 scheduler의 사용이다.
scheduler는 다음과 같은 방식으로 사용하는 것 같다.
- 작업을 저장할 메모리 요소(DB, file 등)를 연결한다.
- 스케쥴링 작업과 실행 시간을 정의한다.
- 연결된 메모리에 작업을 저장한다.
- 작업 큐에 저장한 작업을 등록한다.
- 실행 시간이 되고, 작업이 실행된다.
- 작업 완료 후, 후속 처리 또는 작업을 삭제한다.
나는 fastapi로 서버 개발을 하고 있었기 때문에, apscheduler라는 라이브러리를 사용하기로 했다.
docs: https://apscheduler.readthedocs.io/en/3.x/
Advanced Python Scheduler — APScheduler 3.10.4.post2 documentation
© Copyright Alex Grönholm. Revision 2a2210fc.
apscheduler.readthedocs.io
1. 작업을 저장할 메모리 요소를 연결한다.
import os
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
BASE_DIR = os.path.dirname(os.path.abspath(**file**))
DATABASE_URL = f'sqlite:///{os.path.join(BASE_DIR, "db.sqlite")}'
jobstores = {
'default': SQLAlchemyJobStore(url=DATABASE_URL)
}
scheduler = BackgroundScheduler(jobstores=jobstores)
db 경로를 지정하고, 기본 jobstores로 정의한 db를 등록한 다음, 스케쥴러 인스턴스를 생성해 주었다.
지금 만드는 내용은 혼자 쓰려고 만들기 때문에, 스케줄이 많이 등록되지 않을 것 같아 sqlite3를 jobstore로 사용하려 했다.
2. 스케쥴링 작업과 실행 시간을 정의한다.
def my_task():
print("Scheduled task executed!")
적당한 함수 하나를 만들었다. 실행 시간은 cron 표현식을 사용해 지정할 수도 있다. 하지만 apscheduler는 조금 더 쉬운 방식을 제공하고 있어, 그 방식으로 해 보기로 했다.
3. 연결된 메모리에 작업을 저장한다.
scheduler.add_job(my_task, 'cron', hour='9,21')
9시와 21시에 my_task 함수를 실행하도록 등록했다.
apscheduler의 크론 표현식에 관한 자세한 내용은 이 링크에서 확인해 볼 수 있다.
Link: https://apscheduler.readthedocs.io/en/3.x/modules/triggers/cron.html#introduction
apscheduler.triggers.cron — APScheduler 3.10.4.post2 documentation
Daylight saving time behavior The cron trigger works with the so-called “wall clock” time. Thus, if the selected time zone observes DST (daylight saving time), you should be aware that it may cause unexpected behavior with the cron trigger when enterin
apscheduler.readthedocs.io
4. 작업 큐에 저장한 작업을 등록한다.
@app.on_event("startup")
async def startup_event():
scheduler.start()
print("Starting up FastAPI application and scheduler")
@app.on_event("shutdown")
async def shutdown_event():
scheduler.shutdown()
print("Shutting down FastAPI application and scheduler")
on_event("startup") 으로 서버가 실행될 때, scheduler를 시작하도록 해 뒀다. 이와 비슷한 맥락으로 on_event("shutdown")를 사용해 서버가 종료될 시 스케쥴러를 종료하도록 했다.
5. 실행 시간이 되고, 작업이 실행된다.
```
# second=2로 지정해 잠깐 서버를 실행한 뒤 종료한 서버 로그.
Starting up FastAPI application and scheduler
Scheduled task executed!
Scheduled task executed!
Scheduled task executed!
Scheduled task executed!
Scheduled task executed!
Shutting down FastAPI application and scheduler
```
6. 작업 완료 후, 후속 처리 또는 작업을 삭제한다.
scheduler.remove_job('my_task') # 실행
아니면, add_job 함수에서 cron 옵션 대신 date 옵션을 사용해 한 번만 작동하게 할 수 있다.