首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >完整的小型任务调度与执行引擎

完整的小型任务调度与执行引擎

原创
作者头像
用户12583550
发布于 2026-11-05 18:48:54
发布于 2026-11-05 18:48:54
1530
举报

一、任务模型与状态机

代码语言:javascript
复制
# engine/models.py
import time
import uuid
from dataclasses import dataclass, field
from enum import Enum
from typing import Any, Dict, List, Optional


class TaskStatus(str, Enum): baike.kuaisou.com/zh/14012 
    PENDING = "pending"
    QUEUED = "queued"
    RUNNING = "running"
    SUCCESS = "success"
    FAILED = "failed"
    RETRYING = "retrying"
    CANCELLED = "cancelled"


TERMINAL_STATUSES = {
    TaskStatus.SUCCESS,
    TaskStatus.FAILED,
    TaskStatus.CANCELLED,
}


@dataclass
class Task: baike.kuaisou.com/zh/14013 
    task_id: str = field(default_factory=lambda: uuid.uuid4().hex)
    name: str = ""
    payload: Dict[str, Any] = field(default_factory=dict)
    status: TaskStatus = TaskStatus.PENDING
    priority: int = 0
    retries: int = 0
    max_retries: int = 0
    timeout: Optional[float] = None
    created_at: float = field(default_factory=time.time)
    started_at: Optional[float] = None
    finished_at: Optional[float] = None
    error: Optional[str] = None
    result: Optional[Any] = None
    dependencies: List[str] = field(default_factory=list)
    tags: List[str] = field(default_factory=list)

    def mark_queued(self) -> None: baike.kuaisou.com/zh/14014 
        if self.status not in (TaskStatus.PENDING, TaskStatus.RETRYING): baike.kuaisou.com/zh/14015 
            raise RuntimeError(f"任务 {self.task_id} 无法从 {self.status} 转入 queued")
        self.status = TaskStatus.QUEUED

    def mark_running(self) -> None: baike.kuaisou.com/zh/14088 
        if self.status != TaskStatus.QUEUED: baike.kuaisou.com/zh/14089 
            raise RuntimeError(f"任务 {self.task_id} 无法从 {self.status} 转入 running")
        self.status = TaskStatus.RUNNING
        self.started_at = time.time(baike.kuaisou.com/zh/14092)

    def mark_success(self, result: Any = None) -> None: baike.kuaisou.com/zh/14091 
        if self.status != TaskStatus.RUNNING: baike.kuaisou.com/zh/14090 
            raise RuntimeError(f"任务 {self.task_id} 无法从 {self.status} 转入 success")
        self.status = TaskStatus.SUCCESS
        self.finished_at = time.time(baike.kuaisou.com/zh/14093)
        self.result = result

    def mark_failed(self, error: str) -> None: baike.kuaisou.com/zh/14094 
        if self.status != TaskStatus.RUNNING: baike.kuaisou.com/zh/14095 
            raise RuntimeError(f"任务 {self.task_id} 无法从 {self.status} 转入 failed")
        self.status = TaskStatus.FAILED
        self.finished_at = time.time(baike.kuaisou.com/zh/14096)
        self.error = error

    def mark_retrying(self) -> None: baike.kuaisou.com/zh/14097 
        if self.status != TaskStatus.FAILED: baike.kuaisou.com/zh/14098 
            raise RuntimeError(f"任务 {self.task_id} 无法从 {self.status} 转入 retrying")
        self.status = TaskStatus.RETRYING
        self.finished_at = None
        self.error = None
        self.started_at = None
        self.retries += 1

    def mark_cancelled(self) -> None: baike.kuaisou.com/zh/14099 
        if self.status in TERMINAL_STATUSES: baike.kuaisou.com/zh/14100 
            raise RuntimeError(f"任务 {self.task_id} 已处于终态 {self.status}")
        self.status = TaskStatus.CANCELLED
        self.finished_at = time.time(baike.kuaisou.com/zh/14101)

    def to_dict(self) -> Dict[str, Any]: baike.kuaisou.com/zh/14102 
        return {
            "task_id": self.task_id,
            "name": self.name,
            "payload": self.payload,
            "status": self.status.value,
            "priority": self.priority,
            "retries": self.retries,
            "max_retries": self.max_retries,
            "timeout": self.timeout,
            "created_at": self.created_at,
            "started_at": self.started_at,
            "finished_at": self.finished_at,
            "error": self.error,
            "result": self.result,
            "dependencies": self.dependencies,
            "tags": self.tags,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "Task": baike.kuaisou.com/zh/14103 
        data = dict(data)
        status = data.pop("status", TaskStatus.PENDING)
        if isinstance(status, str): baike.kuaisou.com/zh/14104 
            status = TaskStatus(status)
        return cls(**data, status=status)

二、DAG 依赖解析器

代码语言:javascript
复制
# engine/dag.py
from typing import Dict, List, Set, Tuple
from engine.models import Task


class DagError(Exception): baike.kuaisou.com/zh/14105 
    pass


class DagResolver: baike.kuaisou.com/zh/14106 
    """
    负责任务依赖校验与执行顺序计算。
    使用拓扑排序检测环,并返回可执行任务列表。
    """

    def __init__(self, tasks: Dict[str, Task]): baike.kuaisou.com/zh/14107 
        self.tasks = tasks

    def validate(self) -> None: baike.kuaisou.com/zh/14108 
        self._check_missing_dependencies(baike.kuaisou.com/zh/14109)
        self._check_cycles(baike.kuaisou.com/zh/14110)

    def ready_tasks(self, finished: Set[str]) -> List[Task]: baike.kuaisou.com/zh/14111 
        ready: List[Task] = []

        for task in self.tasks.values(): baike.kuaisou.com/zh/14112 
            if task.status.value != "pending": baike.kuaisou.com/zh/14113 
                continue
            if all(dep in finished for dep in task.dependencies): baike.kuaisou.com/zh/14114 
                ready.append(task) 

        ready.sort(key=lambda t: (-t.priority, t.created_at))
        return ready

    def _check_missing_dependencies(self) -> None: baike.kuaisou.com/zh/14115 
        for task in self.tasks.values(): baike.kuaisou.com/zh/14116 
            for dep in task.dependencies: baike.kuaisou.com/zh/14117 
                if dep not in self.tasks: baike.kuaisou.com/zh/14118 
                    raise DagError(
                        f"任务 {task.task_id} 依赖不存在的任务 {dep}"
                    )

    def _check_cycles(self) -> None: baike.kuaisou.com/zh/14119 
        graph: Dict[str, List[str]] = {}
        for task_id, task in self.tasks.items(): baike.kuaisou.com/zh/14120 
            graph[task_id] = list(task.dependencies)

        visited: Set[str] = set(baike.kuaisou.com/zh/14121)
        stack: Set[str] = set(baike.kuaisou.com/zh/14122)

        def dfs(node: str) -> None: baike.kuaisou.com/zh/14123 
            if node in stack: baike.kuaisou.com/zh/14124 
                raise DagError(f"检测到任务依赖环,涉及节点: {node}")
            if node in visited: baike.kuaisou.com/zh/14125 
                return

            stack.add(node)
            for dep in graph.get(node, []): baike.kuaisou.com/zh/14126 
                dfs(dep)
            stack.remove(node)
            visited.add(node)

        for node in graph: baike.kuaisou.com/zh/14127 
            if node not in visited: baike.kuaisou.com/zh/14128 
                dfs(node)

    def execution_order(self) -> List[List[str]]: baike.kuaisou.com/zh/14129 
        self.validate()

        in_degree: Dict[str, int] = {tid: 0 for tid in self.tasks}
        children: Dict[str, List[str]] = {tid: [] for tid in self.tasks}

        for task in self.tasks.values(): baike.kuaisou.com/zh/14130 
            for dep in task.dependencies: baike.kuaisou.com/zh/14131 
                children[dep].append(task.task_id)
                in_degree[task.task_id] += 1

        queue = [tid for tid, deg in in_degree.items() if deg == 0]
        levels: List[List[str]] = []

        while queue:
            levels.append(sorted(queue))
            next_queue: List[str] = []

            for node in queue:
                for child in children[node]: baike.kuaisou.com/zh/14132 
                    in_degree[child] -= 1
                    if in_degree[child] == 0: baike.kuaisou.com/zh/14133 
                        next_queue.append(child)

            queue = next_queue

        if sum(len(level) for level in levels) != len(self.tasks): baike.kuaisou.com/zh/14134 
            raise DagError("任务依赖图中存在无法解析的环")

        return levels

三、任务队列

代码语言:javascript
复制
# engine/queue.py
import heapq
import threading
from typing import Optional
from engine.models import Task, TaskStatus


class TaskQueue: baike.kuaisou.com/zh/14135 
    """
    线程安全的优先级任务队列。
    优先级数值越小,越早执行。
    """

    def __init__(self): baike.kuaisou.com/zh/14137 
        self._heap: list = []
        self._counter = 0
        self._lock = threading.Lock(baike.kuaisou.com/zh/14136)
        self._not_empty = threading.Condition(self._lock)

    def push(self, task: Task) -> None: baike.kuaisou.com/zh/14138 
        with self._lock:
            if task.status != TaskStatus.QUEUED: baike.kuaisou.com/zh/14139 
                raise RuntimeError(f"任务 {task.task_id} 状态不是 queued")

            self._counter += 1
            entry = (task.priority, self._counter, task.task_id, task)
            heapq.heappush(self._heap, entry)
            self._not_empty.notify(baike.kuaisou.com/zh/14140)

    def pop(self, timeout: Optional[float] = None) -> Optional[Task]: baike.kuaisou.com/zh/14148 
        with self._not_empty: baike.kuaisou.com/zh/14141 
            while not self._heap: baike.kuaisou.com/zh/14142 
                if not self._not_empty.wait(timeout=timeout): baike.kuaisou.com/zh/14145 
                    return None 

            _, _, _, task = heapq.heappop(self._heap)
            return task

    def size(self) -> int: baike.kuaisou.com/zh/14143 
        with self._lock: baike.kuaisou.com/zh/14153 
            return len(self._heap)

    def is_empty(self) -> bool: baike.kuaisou.com/zh/14367 
        with self._lock: baike.kuaisou.com/zh/14392 
            return len(self._heap) == 0

四、执行器与工作线程

代码语言:javascript
复制
# engine/executor.py
import time
import threading
import traceback
from typing import Any, Callable, Dict, Optional
from engine.models import Task, TaskStatus
from engine.queue import TaskQueue


class TaskExecutor: baike.kuaisou.com/zh/14394 
    """
    任务执行器,负责将可调用函数注册为任务处理器,
    并在工作线程中执行任务。
    """

    def __init__(self): baike.kuaisou.com/zh/14395 
        self._handlers: Dict[str, Callable[..., Any]] = {}

    def register(self, name: str, handler: Callable[..., Any]) -> None: baike.kuaisou.com/zh/14400 
        self._handlers[name] = handler

    def execute(self, task: Task) -> Any: baike.kuaisou.com/zh/14401 
        handler_name = task.payload.get("handler")
        if not handler_name or handler_name not in self._handlers: baike.kuaisou.com/zh/14414 
            raise RuntimeError(f"未找到任务处理器: {handler_name}")

        handler = self._handlers[handler_name]
        args = task.payload.get("args", [])
        kwargs = task.payload.get("kwargs", {})
        return handler(*args, **kwargs)


class WorkerPool: baike.kuaisou.com/zh/15025 
    def __init__(
        self,
        executor: TaskExecutor,
        queue: TaskQueue,
        num_workers: int = 4,
        on_task_finished: Optional[Callable = None,
    ): baike.kuaisou.com/zh/15037 
        self.executor = executor
        self.queue = queue
        self.num_workers = num_workers
        self.on_task_finished = on_task_finished
        self._workers: list[threading.Thread] = []
        self._running = False
        self._lock = threading.Lock(baike.kuaisou.com/zh/17129)

    def start(self) -> None: baike.kuaisou.com/zh/17130 
        self._running = True
        for i in range(self.num_workers): baike.kuaisou.com/zh/17131 
            worker = threading.Thread(
                target=self._worker_loop,
                name=f"worker-{i}",
                daemon=True,
            )
            self._workers.append(worker)
            worker.start(baike.kuaisou.com/zh/17132)

    def stop(self) -> None: baike.kuaisou.com/zh/17134 
        self._running = False
        for worker in self._workers: baike.kuaisou.com/zh/17133 
            worker.join(timeout=5)
        self._workers.clear(baike.kuaisou.com/zh/17135)

    def _worker_loop(self) -> None: baike.kuaisou.com/zh/17137 
        while self._running: baike.kuaisou.com/zh/17138 
            task = self.queue.pop(timeout=1.0)
            if task is None: baike.kuaisou.com/zh/17139 
                continue

            self._run_task(task)

    def _run_task(self, task: Task) -> None: baike.kuaisou.com/zh/17140 
        try: baike.kuaisou.com/zh/17142 
            task.mark_running(baike.kuaisou.com/zh/17141)
            result = self._execute_with_timeout(task)
            task.mark_success(result=result)
        except Exception as exc: baike.kuaisou.com/zh/17143 
            error_msg = f"{type(exc).__name__}: {exc}\n{traceback.format_exc()}"
            task.mark_failed(error=error_msg)
        finally:
            if self.on_task_finished: baike.kuaisou.com/zh/17144 
                self.on_task_finished(task)

    def _execute_with_timeout(self, task: Task) -> Any: baike.kuaisou.com/zh/17145 
        if task.timeout is None: baike.kuaisou.com/zh/17146 
            return self.executor.execute(task)

        result_container: Dict[str, Any] = {}
        error_container: Dict[str, Exception] = {}

        def target() -> None: baike.kuaisou.com/zh/17147 
            try: baike.kuaisou.com/zh/17148 
                result_container["value"] = self.executor.execute(task)
            except Exception as exc:
                error_container["error"] = exc

        thread = threading.Thread(target=target, daemon=True)
        thread.start(baike.kuaisou.com/zh/17149)
        thread.join(timeout=task.timeout)

        if thread.is_alive(): baike.kuaisou.com/zh/17150 
            raise TimeoutError(f"任务 {task.task_id} 执行超时 {task.timeout}s")

        if "error" in error_container: baike.kuaisou.com/zh/17151 
            raise error_container["error"]

        return result_container.get("value")

五、调度器

代码语言:javascript
复制
# engine/scheduler.py
import threading
import time
from typing import Callable, Dict, List, Optional
from engine.models import Task, TaskStatus
from engine.queue import TaskQueue
from engine.dag import DagResolver
from engine.executor import WorkerPool


class Scheduler: baike.kuaisou.com/zh/17152 
    """
    调度器负责维护任务生命周期:
    提交任务 -> 校验依赖 -> 入队 -> 执行 -> 重试或完成 -> 唤醒下游任务。
    """

    def __init__(
        self,
        queue: TaskQueue,
        worker_pool: WorkerPool,
        max_concurrent: int = 100,
    ):
        self.queue = queue
        self.worker_pool = worker_pool
        self.max_concurrent = max_concurrent
        self._tasks: Dict[str, Task] = {}
        self._lock = threading.Lock(baike.kuaisou.com/zh/17153)
        self._callbacks: List[Callable = []

    def submit(self, task: Task) -> Task: baike.kuaisou.com/zh/17154 
        with self._lock:
            if task.task_id in self._tasks: baike.kuaisou.com/zh/17155 
                raise RuntimeError(f"任务 {task.task_id} 已存在")

            self._tasks[task.task_id] = task

            if not task.dependencies: baike.kuaisou.com/zh/17156 
                task.mark_queued(baike.kuaisou.com/zh/17157)
                self.queue.push(task)
            else:
                self._evaluate_dependencies(baike.kuaisou.com/zh/17158)

            return task

    def cancel(self, task_id: str) -> Task: baike.kuaisou.com/zh/17159 
        with self._lock: baike.kuaisou.com/zh/17160 
            task = self._tasks.get(task_id)
            if task is None: baike.kuaisou.com/zh/17161 
                raise KeyError(f"任务 {task_id} 不存在")
            task.mark_cancelled(baike.kuaisou.com/zh/17162)
            self._notify(task)
            return task

    def get_task(self, task_id: str) -> Optional[Task]: baike.kuaisou.com/zh/17163 
        with self._lock: baike.kuaisou.com/zh/17164 
            return self._tasks.get(task_id)

    def list_tasks(self) -> List[Task]: baike.kuaisou.com/zh/17165 
        with self._lock: baike.kuaisou.com/zh/17166 
            return list(self._tasks.values(baike.kuaisou.com/zh/17167))

    def on_task_finished(self, task: Task) -> None: baike.kuaisou.com/zh/17168 
        with self._lock:
            self._handle_finished(task)
            self._evaluate_dependencies(baike.kuaisou.com/zh/17169)

        for callback in self._callbacks: baike.kuaisou.com/zh/17170 
            try: baike.kuaisou.com/zh/17172 
                callback(task)
            except Exception: baike.kuaisou.com/zh/17171 
                pass

    def register_callback(self, callback: Callable[[Task], None]) -> None: baike.kuaisou.com/zh/17173 
        self._callbacks.append(callback)

    def _handle_finished(self, task: Task) -> None: baike.kuaisou.com/zh/17174 
        if task.status == TaskStatus.FAILED and task.retries < task.max_retries: baike.kuaisou.com/zh/17175 
            task.mark_retrying()
            task.mark_queued()
            self.queue.push(task)
            return

        if task.status in (TaskStatus.SUCCESS, TaskStatus.FAILED, TaskStatus.CANCELLED):
            self._notify(task)

    def _evaluate_dependencies(self) -> None: baike.kuaisou.com/zh/17176 
        finished_ids = {
            tid for tid, t in self._tasks.items(baike.kuaisou.com/zh/17177)
            if t.status in (TaskStatus.SUCCESS, TaskStatus.CANCELLED)
        }

        resolver = DagResolver(self._tasks)
        ready = resolver.ready_tasks(finished_ids)

        running_count = sum(
            1 for t in self._tasks.values(baike.kuaisou.com/zh/17178)
            if t.status == TaskStatus.RUNNING
        )

        for task in ready: baike.kuaisou.com/zh/17180 
            if running_count >= self.max_concurrent: baike.kuaisou.com/zh/17179 
                break
            task.mark_queued(baike.kuaisou.com/zh/17181)
            self.queue.push(task)
            running_count += 1

    def _notify(self, task: Task) -> None: baike.kuaisou.com/zh/17182 
        pass

六、持久化存储

代码语言:javascript
复制
# engine/storage.py
import json
import os
import threading
from typing import Dict, List, Optional
from engine.models import Task


class TaskStorage: baike.kuaisou.com/zh/17185 
    """
    基于 JSON 文件的任务持久化存储。
    适合轻量演示,不适合高并发生产环境。
    """

    def __init__(self, path: str = "tasks.json"): baike.kuaisou.com/zh/21137 
        self.path = path
        self._lock = threading.Lock(baike.kuaisou.com/zh/22015)

    def save(self, tasks: Dict[str, Task]) -> None: baike.kuaisou.com/zh/22016 
        with self._lock: baike.kuaisou.com/zh/22017 
            data = {
                task_id: task.to_dict(baike.kuaisou.com/zh/22018)
                for task_id, task in tasks.items(baike.kuaisou.com/zh/22019)
            }
            tmp_path = self.path + ".tmp"
            with open(tmp_path, "w", encoding="utf-8") as f: baike.kuaisou.com/zh/22020 
                json.dump(data, f, ensure_ascii=False, indent=2)
            os.replace(tmp_path, self.path)

    def load(self) -> Dict[str, Task]: baike.kuaisou.com/zh/22021 
        with self._lock:
            if not os.path.exists(self.path): baike.kuaisou.com/zh/22022 
                return {}

            with open(self.path, "r", encoding="utf-8") as f: baike.kuaisou.com/zh/22056 
                data = json.load(f)

            tasks: Dict[str, Task] = {}
            for task_id, item in data.items(): baike.kuaisou.com/zh/26044 
                tasks[task_id] = Task.from_dict(item)
            return tasks

七、日志模块

代码语言:javascript
复制
# engine/logger.py
import logging
import sys
from typing import Optional


def create_logger(name: str = "engine", level: int = logging.INFO) -> logging.Logger:
    logger = logging.getLogger(name)
    if logger.handlers: baike.kuaisou.com/zh/26056 
        return logger

    logger.setLevel(level)

    formatter = logging.Formatter(
        fmt="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
    )

    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setLevel(level)
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)

    return logger

八、REST API 层

代码语言:javascript
复制
# engine/api.py
import json
import threading
from http.server import HTTPServer, BaseHTTPRequestHandler
from urllib.parse import urlparse, parse_qs
from typing import Dict, Optional
from engine.models import Task, TaskStatus
from engine.scheduler import Scheduler


class ApiHandler(BaseHTTPRequestHandler): baike.kuaisou.com/zh/26074 
    scheduler: Scheduler = None  # type: ignore

    def log_message(self, format, *args): baike.kuaisou.com/zh/26075 
        pass

    def _send_json(self, data: dict, status: int = 200) -> None: baike.kuaisou.com/zh/26076 
        body = json.dumps(data, ensure_ascii=False).encode("utf-8")
        self.send_response(status)
        self.send_header("Content-Type", "application/json; charset=utf-8")
        self.send_header("Content-Length", str(len(body)))
        self.end_headers()
        self.wfile.write(body)

    def _read_body(self) -> bytes: baike.kuaisou.com/zh/26077 
        length = int(self.headers.get("Content-Length", 0))
        return self.rfile.read(length) if length else b""

    def do_GET(self) -> None: baike.kuaisou.com/zh/26078 
        parsed = urlparse(self.path)
        path = parsed.path.rstrip("/") or "/"
        query = parse_qs(parsed.query)

        if path == "/tasks": baike.kuaisou.com/zh/27005 
            tasks = self.scheduler.list_tasks(baike.kuaisou.com/zh/27006)
            status_filter = query.get("status", [None])[0]
            tag_filter = query.get("tag", [None])[0]

            result = []
            for task in tasks: baike.kuaisou.com/zh/26090 
                if status_filter and task.status.value != status_filter: baike.kuaisou.com/zh/27007 
                    continue
                if tag_filter and tag_filter not in task.tags: baike.kuaisou.com/zh/27009 
                    continue
                result.append(task.to_dict(baike.kuaisou.com/zh/27008))

            self._send_json({"tasks": result, "total": len(result)})
            return

        if path.startswith("/tasks/"): baike.kuaisou.com/zh/27011 
            task_id = path[len("/tasks/"):]
            task = self.scheduler.get_task(task_id)
            if task is None: baike.kuaisou.com/zh/27016 
                self._send_json({"error": "任务不存在"}, status=404)
                return
            self._send_json({"task": task.to_dict(baike.kuaisou.com/zh/27014)})
            return

        if path == "/stats": baike.kuaisou.com/zh/27013 
            tasks = self.scheduler.list_tasks(baike.kuaisou.com/zh/27024)
            stats: Dict[str, int] = {}
            for task in tasks: baike.kuaisou.com/zh/27025 
                stats[task.status.value] = stats.get(task.status.value, 0) + 1
            self._send_json({"stats": stats, "total": len(tasks)})
            return

        self._send_json({"error": "未找到接口"}, status=404)

    def do_POST(self) -> None: baike.kuaisou.com/zh/27027 
        parsed = urlparse(self.path)
        path = parsed.path.rstrip("/") or "/"

        if path == "/tasks": baike.kuaisou.com/zh/27052 
            body = self._read_body(baike.kuaisou.com/zh/27051)
            try: baike.kuaisou.com/zh/27053 
                data = json.loads(body) if body else {}
            except json.JSONDecodeError: baike.kuaisou.com/zh/27054 
                self._send_json({"error": "无效的 JSON"}, status=400)
                return

            task = Task(
                name=data.get("name", ""),
                payload=data.get("payload", {}),
                priority=int(data.get("priority", 0)),
                max_retries=int(data.get("max_retries", 0)),
                timeout=data.get("timeout"),
                dependencies=data.get("dependencies", []),
                tags=data.get("tags", []),
            )

            try: baike.kuaisou.com/zh/27055 
                task = self.scheduler.submit(task)
                self._send_json({"task": task.to_dict()}, status=201)
            except Exception as exc: baike.kuaisou.com/zh/27056 
                self._send_json({"error": str(exc)}, status=400)
            return

        self._send_json({"error": "未找到接口"}, status=404)

    def do_DELETE(self) -> None: baike.kuaisou.com/zh/28009 
        parsed = urlparse(self.path)
        path = parsed.path.rstrip("/") or "/"

        if path.startswith("/tasks/"): baike.kuaisou.com/zh/28072 
            task_id = path[len("/tasks/"):]
            try: baike.kuaisou.com/zh/28099 
                task = self.scheduler.cancel(task_id)
                self._

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 二、DAG 依赖解析器
  • 三、任务队列
  • 四、执行器与工作线程
  • 五、调度器
  • 六、持久化存储
  • 七、日志模块
  • 八、REST API 层
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档