๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ

Python

[Python] APScheduler ๊ฐ€์ด๋“œ

728x90

๐Ÿ“Œ1. APScheduler ์„ค์น˜ํ•˜๊ธฐ

pip install apscheduler

 

์ถ”๊ฐ€์ ์ธ ์˜์กด์„ฑ ์„ค์น˜

  • APScheduler๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ `threading`์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ, ์‹คํ–‰ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋ฐฉ์‹๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.
  • ํŠน์ • ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ ค๋ฉด ์•„๋ž˜ ํŒจํ‚ค์ง€๋„ ํ•จ๊ป˜ ์„ค์น˜ํ•ด์•ผ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
๋”๋ณด๊ธฐ

pip install apscheduler[asyncio] # asyncio ํ™˜๊ฒฝ ์ง€์› pip install apscheduler[gevent] # gevent ํ™˜๊ฒฝ ์ง€์› pip install apscheduler[tornado] # tornado ํ™˜๊ฒฝ ์ง€์› pip install apscheduler[redis] # Redis ๊ธฐ๋ฐ˜ Job Store ์‚ฌ์šฉ

๐Ÿ“Œ 2. APScheduler์˜ ์ฃผ์š” ๊ฐœ๋…

APScheduler๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์ž‘์—…(Job)์„ ์˜ˆ์•ฝํ•˜์—ฌ ์‹คํ–‰ํ•˜๋Š” ๊ธฐ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฐœ๋…์„ ์ดํ•ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ฐœ๋… ์„ค๋ช…
Job Store (์ž‘์—… ์ €์žฅ์†Œ) ์˜ˆ์•ฝ๋œ ์ž‘์—…์„ ์ €์žฅํ•˜๋Š” ๊ณต๊ฐ„ (๊ธฐ๋ณธ์ ์œผ๋กœ ๋ฉ”๋ชจ๋ฆฌ์— ์ €์žฅ๋˜์ง€๋งŒ, DB, Redis ๋“ฑ๋„ ๊ฐ€๋Šฅ)
Trigger (ํŠธ๋ฆฌ๊ฑฐ) ์ž‘์—…์ด ์‹คํ–‰๋  ์กฐ๊ฑด์„ ์ •์˜ (ํŠน์ • ์‹œ๊ฐ„, ์ฃผ๊ธฐ์ ์œผ๋กœ ์‹คํ–‰ ๋“ฑ)
Executor (์‹คํ–‰๊ธฐ) ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ์‹ (์Šค๋ ˆ๋“œ, ํ”„๋กœ์„ธ์Šค, ๋น„๋™๊ธฐ ๋“ฑ)
Scheduler (์Šค์ผ€์ค„๋Ÿฌ) ์ „์ฒด ์ž‘์—…์„ ๊ด€๋ฆฌํ•˜๊ณ  ์‹คํ–‰ํ•˜๋Š” ์—ญํ• 

 


๐Ÿ“Œ 3. APScheduler ์‚ฌ์šฉ ์˜ˆ์ œ

1๏ธโƒฃ ๊ธฐ๋ณธ ์Šค์ผ€์ค„๋Ÿฌ ์‹คํ–‰

from apscheduler.schedulers.background import BackgroundScheduler
import time

def my_task():
	print("์ž‘์—…์ด ์‹คํ–‰๋˜์—ˆ์Šต๋‹ˆ๋‹ค!")
    
scheduler = BackgroundScheduler()
scheduler.add_job(my_task, 'interval', seconds=10) # 10์ดˆ๋งˆ๋‹ค ์‹คํ–‰
scheduler.start()

try:
	while True:
    	time.sleep(1) # ํ”„๋กœ๊ทธ๋žจ์ด ์ข…๋ฃŒ๋˜์ง€ ์•Š๋„๋ก ์œ ์ง€
except KeyboardInterrupt:
	scheduler.shutdown()

โœ… ์„ค๋ช…:

  • `BackgroundScheduler()`๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ์‹คํ–‰
  • `add_job()`์„ ์‚ฌ์šฉํ•ด 10์ดˆ๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” ์ž‘์—…์„ ์ถ”๊ฐ€
  • `scheduler.start()`๋กœ ์‹คํ–‰ ์‹œ์ž‘

2๏ธโƒฃ ํŠน์ • ์‹œ๊ฐ„์— ํ•œ ๋ฒˆ ์‹คํ–‰

from apscheduler.schedulers.blocking import BlockingScheduler
from datetime import datetime

def my_task():
	print(f"์ž‘์—… ์‹คํ–‰๋จ: {datetime.now()}")

scheduler = BlockingScheduler()
scheduler.add_job(my_task, 'date', run_date='2025-02-20 15:30:00')
scheduler.start()

 

โœ… ์„ค๋ช…:

  • `date` ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•ด ํŠน์ • ์‹œ๊ฐ„(2025๋…„ 2์›” 20์ผ 15์‹œ 30๋ถ„)์— ์‹คํ–‰
  • `BlockingScheduler()`๋Š” ํ”„๋กœ๊ทธ๋žจ์ด ์‹คํ–‰๋œ ์ฑ„๋กœ ์œ ์ง€๋จ

3๏ธโƒฃ ๋งค์ผ ํŠน์ • ์‹œ๊ฐ„์— ์‹คํ–‰

from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()

scheduler.add_job(my_task, 'cron', hour=0, minute=30) # ๋งค์ผ ์˜ค์ „ 9์‹œ 30๋ถ„ ์‹คํ–‰
scheduler.start()

โœ… ์„ค๋ช…:

  • `cron` ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์‚ฌ์šฉํ•ด ๋งค์ผ ํŠน์ • ์‹œ๊ฐ„ (9์‹œ 30๋ถ„) ์— ์‹คํ–‰

4๏ธโƒฃ ๋งค์ฃผ ์›”์š”์ผ, ์ˆ˜์š”์ผ, ๊ธˆ์š”์ผ ์‹คํ–‰

scheduler.add_job(my_task, 'cron', day_of_week = 'mon,wed,fri', hour=10, minute=0)

โœ… ์„ค๋ช…:

  • `day_of_week` ๋ฅผ ์‚ฌ์šฉํ•ด ๋งค์ฃผ ์›”/์ˆ˜/๊ธˆ ์˜ค์ „ 10์‹œ์— ์‹คํ–‰

5๏ธโƒฃ ๋™์ ์œผ๋กœ ์ž‘์—… ์ถ”๊ฐ€ ๋ฐ ์ œ๊ฑฐ

job = scheduler.add_job(my_task, 'interval', minute=5)
job.remove() # ์ž‘์—… ์‚ญ์ œ

โœ… ์„ค๋ช…:

  • `add_job` ์„ ํ†ตํ•ด ๋™์ ์œผ๋กœ ์ž‘์—… ์ถ”๊ฐ€
  • `remove()`๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ํŠน์ • ์ž‘์—… ์‚ญ์ œ ๊ฐ€๋Šฅ

6๏ธโƒฃ ์‹คํ–‰ ์ค‘์ธ ๋ชจ๋“  ์ž‘์—… ์กฐํšŒ

for job in scheduler.get_jobs():
	print(f"๋“ฑ๋ก๋œ ์ž‘์—…: {job}")

โœ… ์„ค๋ช…:

  • `get_jobs()`๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ํ˜„์žฌ ๋“ฑ๋ก๋œ ๋ชจ๋“  ์ž‘์—… ๋ชฉ๋ก์„ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ

๐Ÿ“Œ 4. APScheduler์—์„œ Job Store ์‚ฌ์šฉํ•˜๊ธฐ

๊ธฐ๋ณธ์ ์œผ๋กœ APScheduler๋Š” ๋ฉ”๋ชจ๋ฆฌ์— ์ž‘์—…์„ ์ €์žฅํ•˜์ง€๋งŒ, ํŒŒ์ผ ๋˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅํ•  ์ˆ˜๋„ ์žˆ์Œ.

1๏ธโƒฃ SQLite์— ์ž‘์—… ์ €์žฅ

from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore

jobstores = {
    'default': SQLAlchemyJobStore(url='sqlite:///jobs.sqlite')
}

scheduler = BackgroundScheduler(jobstores=jobstores)
scheduler.start()

 

โœ… ์„ค๋ช…:

  • APScheduler์˜ Job Store๋ฅผ SQLite๋กœ ์„ค์ •ํ•˜๋ฉด, ์ž‘์—…์ด ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋จ
  • `sqlite:///jobs.sqlite`๋Š” ํ˜„์žฌ ํด๋”์— jobs.sqlite ํŒŒ์ผ์„ ์ƒ์„ฑํ•จ

2๏ธโƒฃ Redis์— ์ž‘์—… ์ €์žฅ

from apscheduler.jobstores.redis import RedisJobStore

jobstores = {
    'default': RedisJobStore(host='localhost', port=6379)
}

scheduler = BackgroundScheduler(jobstores=jobstores)
scheduler.start()

โœ… ์„ค๋ช…:

  • Redis๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Job Store๋ฅผ ์„ค์ •ํ•˜๋ฉด, ์ž‘์—…์ด Redis์— ์ €์žฅ๋จ
  • ์ด ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ์„œ๋ฒ„์—์„œ ๋™์ผํ•œ ์ž‘์—…์„ ๊ณต์œ ํ•  ๋•Œ ์œ ์šฉํ•จ

๐Ÿ“Œ 5. APScheduler์—์„œ Executor ์„ค์ •ํ•˜๊ธฐ

Executor๋Š” ์ž‘์—…์„ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ์‹์„ ๊ฒฐ์ •ํ•จ

1๏ธโƒฃ ๊ธฐ๋ณธ ์„ค์ • (ThreadPoolExecutor)

from apscheduler.executors.pool import ThreadPoolExecutor

executors = {
    'default': ThreadPoolExecutor(max_workers=5)  # ์ตœ๋Œ€ 5๊ฐœ์˜ ์ž‘์—… ๋™์‹œ ์‹คํ–‰
}

scheduler = BackgroundScheduler(executors=executors)
scheduler.start()

โœ… ์„ค๋ช…:

  • ThreadPoolExecutor(max_workers=5)๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ตœ๋Œ€ 5๊ฐœ์˜ ์ž‘์—…์„ ๋™์‹œ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Œ

2๏ธโƒฃ ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค ์‹คํ–‰ (ProcessPoolExecutor)

from apscheduler.executors.pool import ProcessPoolExecutor

executors = {
    'default': ProcessPoolExecutor(max_workers=3)  # ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค๋กœ ์‹คํ–‰
}

scheduler = BackgroundScheduler(executors=executors)
scheduler.start()

โœ… ์„ค๋ช…:

  • ProcessPoolExecutor๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ€ํ‹ฐ ํ”„๋กœ์„ธ์Šค๋ฅผ ํ™œ์šฉํ•˜์—ฌ ์‹คํ–‰ ๊ฐ€๋Šฅ
  • CPU ๋ถ€ํ•˜๊ฐ€ ๋†’์€ ์ž‘์—…์— ์ ํ•ฉ

๐Ÿ“Œ 6. APScheduler์˜ ์‹คํ–‰ ๋ฐฉ์‹

APScheduler๋Š” 4๊ฐ€์ง€ ๋ฐฉ์‹์œผ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Œ.

์‹คํ–‰ ๋ฐฉ์‹   ์„ค๋ช…
BlockingScheduler   ํ”„๋กœ๊ทธ๋žจ์ด ์ข…๋ฃŒ๋˜์ง€ ์•Š๊ณ  ์œ ์ง€๋จ (๋‹จ์ผ ํ”„๋กœ์„ธ์Šค์—์„œ ์‹คํ–‰ํ•  ๋•Œ ์‚ฌ์šฉ)
BackgroundScheduler   ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰๋จ (์ผ๋ฐ˜์ ์ธ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์ ํ•ฉ)
AsyncIOScheduler   asyncio ์ด๋ฒคํŠธ ๋ฃจํ”„์—์„œ ์‹คํ–‰๋จ (๋น„๋™๊ธฐ ์ž‘์—…์— ์ ํ•ฉ)
TornadoScheduler   Tornado ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ์‹คํ–‰๋จ

 

โœ… ์ผ๋ฐ˜์ ์ธ ์‚ฌ์šฉ:

scheduler = BackgroundScheduler()
scheduler.start()

 

โœ… FastAPI ๋˜๋Š” Flask ๊ฐ™์€ ์›น ์„œ๋ฒ„์—์„œ๋Š”:

scheduler = BackgroundScheduler()
scheduler.start(paused=True)  # ์›น ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜๊ณผ ํ•จ๊ป˜ ์‹คํ–‰

๐Ÿ“Œ 7. ๊ฒฐ๋ก 

APScheduler๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์Šค์ผ€์ค„๋ง์ด ํ•„์š”ํ•œ ์ž‘์—…์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ž๋™ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • `interval`, `date`, `cron` ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ํ™œ์šฉํ•˜์—ฌ ๋‹ค์–‘ํ•œ ์‹คํ–‰ ์ฃผ๊ธฐ๋ฅผ ์„ค์ • ๊ฐ€๋Šฅ
  • SQLite, Redis ๊ฐ™์€ Job Store๋ฅผ ์ด์šฉํ•˜์—ฌ ์ž‘์—…์„ ์ €์žฅ ๊ฐ€๋Šฅ
  • Thread, Process, Async ๋“ฑ์„ ํ™œ์šฉํ•ด ์‹คํ–‰ ๋ฐฉ์‹ ์กฐ์ • ๊ฐ€๋Šฅ

 

 

 

 

 

 

 

728x90