Prefect工作流编排实战:从Python脚本到企业级数据管道

发布时间:2026/7/16 3:19:19
Prefect工作流编排实战:从Python脚本到企业级数据管道 在数据工程和机器学习项目中我们经常需要处理复杂的数据流水线但传统的脚本方式缺乏重试、调度、监控等生产级功能。Prefect 作为 Python 生态中的工作流编排框架能够将普通脚本快速升级为具备企业级可靠性的数据管道。本文将完整介绍 Prefect 的核心概念、安装配置、实战案例以及生产环境最佳实践。无论你是数据工程师、机器学习工程师还是后端开发者只要需要构建可靠的数据处理流程Prefect 都能提供强大的编排能力。通过本文你将掌握从基础概念到生产部署的全流程并能够独立构建具备容错、调度和监控的数据流水线。1. Prefect 核心概念与架构解析1.1 什么是工作流编排工作流编排Workflow Orchestration是指对多个任务或步骤进行协调、调度和监控的过程。在数据工程领域这通常涉及数据提取、转换、加载ETL等操作的自动化管理。传统的数据脚本虽然功能完整但缺乏以下关键能力自动重试机制网络波动或临时错误导致的任务失败依赖管理任务之间的先后执行关系调度能力按时间或事件触发执行状态监控实时了解流水线运行状态错误追踪快速定位和诊断问题Prefect 正是为了解决这些问题而设计的现代化编排框架。1.2 Prefect 架构组成Prefect 采用客户端-服务器架构主要包含以下组件Prefect SDKPython 库用于定义工作流Flows和任务TasksPrefect Server后端服务负责调度、状态管理和UI展示Prefect Agent执行代理负责实际运行工作流Prefect UIWeb 界面提供可视化监控和管理这种架构分离了工作流定义和执行环境使得开发、测试和生产部署能够保持一致性。1.3 核心概念详解Flow工作流Flow 是 Prefect 中的最高层级概念代表一个完整的工作流程。它由多个 Task 组成并定义了这些 Task 之间的执行顺序和依赖关系。Task任务Task 是工作流中的基本执行单元每个 Task 代表一个具体的操作如数据提取、数据转换或数据加载。Task 可以配置重试策略、超时设置等。Deployment部署Deployment 将 Flow 与具体的执行环境绑定包括调度策略、参数配置和基础设施设置。Parameter参数允许在运行时动态传入配置值使工作流更加灵活。2. 环境准备与安装配置2.1 系统要求与版本兼容性Prefect 要求 Python 3.10 或更高版本。在开始安装前请确保你的开发环境满足以下要求Python 3.10pip 或 uv 包管理器网络连接用于下载依赖包至少 2GB 可用内存建议使用虚拟环境来管理依赖避免与系统Python环境冲突。2.2 Prefect 安装步骤Prefect 提供多种安装方式以下是常用的两种方法使用 pip 安装# 创建并激活虚拟环境 python -m venv prefect_env source prefect_env/bin/activate # Linux/Mac # prefect_env\Scripts\activate # Windows # 安装最新版 Prefect pip install -U prefect使用 uv 安装推荐速度更快# 安装 uv 包管理器 curl -LsSf https://astral.sh/uv/install.sh | sh # 使用 uv 安装 Prefect uv add prefect验证安装是否成功python -c import prefect; print(fPrefect version: {prefect.__version__})2.3 Prefect Server 本地部署对于开发和测试环境我们可以使用本地 Prefect Server# 启动本地服务器 prefect server start服务器启动后可以通过 http://localhost:4200 访问 Prefect UI。首次访问时需要完成初始设置。2.4 配置 Prefect 配置文件创建配置文件~/.prefect/config.toml进行个性化设置[api] url http://localhost:4200/api [cloud] api_key your-api-key-if-using-cloud [logging] level INFO format [%(asctime)s] %(levelname)s - %(name)s | %(message)s3. Prefect 核心语法与 API 详解3.1 Task 装饰器与参数配置Task 是 Prefect 的基本构建块使用task装饰器定义from prefect import task from typing import List, Dict import time task( namedata_processing_task, description处理原始数据并返回清洗结果, tags[etl, processing], retries3, retry_delay_seconds10, timeout_seconds300 ) def process_data(raw_data: List[Dict]) - List[Dict]: 数据处理任务示例 # 模拟数据处理逻辑 processed_data [] for item in raw_data: # 数据清洗和转换 cleaned_item { id: item.get(id), name: item.get(name, ).strip().title(), value: float(item.get(value, 0)), timestamp: item.get(timestamp) } processed_data.append(cleaned_item) # 模拟耗时操作 time.sleep(2) return processed_data关键参数说明retries任务失败时的重试次数retry_delay_seconds重试间隔时间timeout_seconds任务超时时间tags用于分类和筛选的标签3.2 Flow 装饰器与工作流定义Flow 是任务的容器使用flow装饰器定义from prefect import flow, task from typing import List, Dict import requests task def extract_data(api_url: str) - List[Dict]: 数据提取任务 response requests.get(api_url) response.raise_for_status() return response.json() task def transform_data(raw_data: List[Dict]) - List[Dict]: 数据转换任务 return [{id: item[id], name: item[name].upper()} for item in raw_data] task def load_data(processed_data: List[Dict], db_url: str) - bool: 数据加载任务 # 模拟数据库写入操作 print(f将 {len(processed_data)} 条数据写入数据库) return True flow( nameetl_pipeline, description完整的ETL数据流水线, version1.0.0, log_printsTrue ) def etl_pipeline(api_url: str, db_url: str): ETL工作流主函数 print(开始执行ETL流水线...) # 执行数据提取 raw_data extract_data(api_url) print(f提取到 {len(raw_data)} 条原始数据) # 执行数据转换 transformed_data transform_data(raw_data) print(数据转换完成) # 执行数据加载 success load_data(transformed_data, db_url) if success: print(ETL流水线执行成功) else: raise Exception(数据加载失败) return len(transformed_data)3.3 参数化工作流Prefect 支持参数化的工作流使流水线更加灵活from prefect import flow, task from pydantic import BaseModel class ETLConfig(BaseModel): api_url: str db_url: str batch_size: int 100 enable_validation: bool True flow def parameterized_etl(config: ETLConfig) - int: 参数化ETL工作流 print(f使用配置执行ETL: {config.dict()}) # 基于配置执行不同的处理逻辑 if config.enable_validation: print(启用数据验证) # 模拟处理过程 processed_count min(config.batch_size, 1000) return processed_count # 使用示例 if __name__ __main__: config ETLConfig( api_urlhttps://api.example.com/data, db_urlpostgresql://user:passlocalhost/db, batch_size200, enable_validationTrue ) result parameterized_etl(config) print(f处理了 {result} 条数据)4. 完整实战案例GitHub 仓库监控系统4.1 项目需求分析我们将构建一个监控 GitHub 仓库状态的流水线功能包括定期获取指定仓库的星标数、提交数等信息检测数据变化并发送通知生成统计报告异常情况自动告警4.2 项目结构设计github_monitor/ ├── src/ │ ├── flows/ │ │ └── github_monitor.py │ ├── tasks/ │ │ ├── data_extraction.py │ │ ├── data_analysis.py │ │ └── notification.py │ └── models/ │ └── github_models.py ├── config/ │ └── settings.py ├── tests/ │ └── test_flows.py └── requirements.txt4.3 核心代码实现数据模型定义src/models/github_models.pyfrom pydantic import BaseModel from typing import List, Optional from datetime import datetime class RepositoryInfo(BaseModel): name: str stars: int forks: int open_issues: int last_commit: Optional[datetime] description: Optional[str] class RepositoryStats(BaseModel): repository: str timestamp: datetime stars: int stars_change: int forks: int forks_change: int class MonitorConfig(BaseModel): repositories: List[str] check_interval_minutes: int 60 notify_on_changes: bool True webhook_url: Optional[str] None数据提取任务src/tasks/data_extraction.pyfrom prefect import task import httpx from typing import List from src.models.github_models import RepositoryInfo from datetime import datetime task( namefetch_github_stats, retries3, retry_delay_seconds30 ) async def fetch_github_stats(repository: str) - RepositoryInfo: 获取GitHub仓库统计信息 url fhttps://api.github.com/repos/{repository} async with httpx.AsyncClient() as client: response await client.get(url) response.raise_for_status() data response.json() return RepositoryInfo( namerepository, starsdata.get(stargazers_count, 0), forksdata.get(forks_count, 0), open_issuesdata.get(open_issues_count, 0), last_commitdatetime.fromisoformat(data[pushed_at].replace(Z, 00:00)) if data.get(pushed_at) else None, descriptiondata.get(description) ) task def compare_repository_stats(current: RepositoryInfo, previous: RepositoryInfo) - dict: 比较仓库统计数据变化 return { repository: current.name, timestamp: datetime.now(), stars_change: current.stars - previous.stars, forks_change: current.forks - previous.forks, significant_change: abs(current.stars - previous.stars) 10 }数据分析任务src/tasks/data_analysis.pyfrom prefect import task from typing import List, Dict from src.models.github_models import RepositoryStats task def analyze_trends(stats_history: List[RepositoryStats]) - Dict: 分析仓库趋势 if not stats_history: return {message: 无足够数据进行趋势分析} latest stats_history[-1] first stats_history[0] days_span (latest.timestamp - first.timestamp).days daily_star_growth (latest.stars - first.stars) / max(days_span, 1) return { total_repositories: len(set(stat.repository for stat in stats_history)), total_stars_growth: latest.stars - first.stars, daily_star_growth: round(daily_star_growth, 2), most_popular_repo: max(stats_history, keylambda x: x.stars).repository, analysis_period_days: days_span } task def detect_anomalies(current_stats: RepositoryStats, historical_data: List[RepositoryStats]) - List[Dict]: 检测数据异常 anomalies [] # 获取该仓库的历史数据 repo_history [s for s in historical_data if s.repository current_stats.repository] if len(repo_history) 5: recent_avg_star_change sum( s.stars_change for s in repo_history[-5:] ) / 5 # 检测异常波动 if abs(current_stats.stars_change - recent_avg_star_change) 3 * recent_avg_star_change: anomalies.append({ repository: current_stats.repository, type: star_change_anomaly, current_change: current_stats.stars_change, expected_range: recent_avg_star_change, severity: high }) return anomalies通知任务src/tasks/notification.pyfrom prefect import task from typing import List, Dict import smtplib from email.mime.text import MimeText import json task def send_email_notification(subject: str, content: str, recipients: List[str]) - bool: 发送邮件通知 try: # 配置邮件服务器示例配置 smtp_server smtp.gmail.com port 587 sender_email monitorexample.com password your-app-password # 创建邮件内容 msg MimeText(content, html) msg[Subject] subject msg[From] sender_email msg[To] , .join(recipients) # 发送邮件 server smtplib.SMTP(smtp_server, port) server.starttls() server.login(sender_email, password) server.send_message(msg) server.quit() return True except Exception as e: print(f邮件发送失败: {e}) return False task def send_slack_webhook(message: str, webhook_url: str) - bool: 发送Slack webhook通知 try: import requests payload {text: message} response requests.post(webhook_url, jsonpayload) return response.status_code 200 except Exception as e: print(fSlack通知发送失败: {e}) return False task def format_weekly_report(analysis_results: Dict, top_repos: List) - str: 格式化周报 report f # GitHub仓库监控周报 ## 概览 - 监控仓库数量: {analysis_results.get(total_repositories, 0)} - 总星标增长: {analysis_results.get(total_stars_growth, 0)} - 日均增长: {analysis_results.get(daily_star_growth, 0)} ## 最受欢迎仓库 {analysis_results.get(most_popular_repo, N/A)} ## 重点关注 for repo in top_repos[:3]: report f- {repo[name]}: {repo[stars]} stars ({repo[change]})\n return report主工作流定义src/flows/github_monitor.pyfrom prefect import flow, task from typing import List, Dict from src.models.github_models import MonitorConfig, RepositoryStats from src.tasks.data_extraction import fetch_github_stats, compare_repository_stats from src.tasks.data_analysis import analyze_trends, detect_anomalies from src.tasks.notification import send_email_notification, format_weekly_report import asyncio from datetime import datetime, timedelta from prefect.task_runners import ConcurrentTaskRunner flow( namegithub_repository_monitor, description监控GitHub仓库状态的自动化工作流, task_runnerConcurrentTaskRunner(), log_printsTrue ) async def github_repository_monitor(config: MonitorConfig): GitHub仓库监控主工作流 print(f开始监控 {len(config.repositories)} 个仓库) # 并发获取所有仓库的当前状态 current_stats await asyncio.gather( *[fetch_github_stats(repo) for repo in config.repositories] ) # 这里应该从数据库或文件加载历史数据进行比较 # 为示例简化我们模拟历史数据 historical_data [ RepositoryStats( repositoryrepo, timestampdatetime.now() - timedelta(days1), starsstats.stars - 2, # 模拟历史数据 stars_change2, forksstats.forks - 1, forks_change1 ) for repo, stats in zip(config.repositories, current_stats) ] # 比较数据变化 changes [] for i, repo in enumerate(config.repositories): change compare_repository_stats( current_stats[i], historical_data[i] # 简化处理实际应从存储中获取 ) changes.append(change) # 检测异常 all_anomalies [] for i, stats in enumerate(current_stats): repo_stats RepositoryStats( repositoryconfig.repositories[i], timestampdatetime.now(), starsstats.stars, stars_changechanges[i][stars_change], forksstats.forks, forks_changechanges[i][forks_change] ) anomalies detect_anomalies(repo_stats, historical_data [repo_stats]) all_anomalies.extend(anomalies) # 如果有异常发送通知 if all_anomalies and config.notify_on_changes: anomaly_message 检测到以下异常:\n \n.join( f- {anomaly[repository]}: {anomaly[type]} for anomaly in all_anomalies ) print(anomaly_message) # 发送邮件通知 if config.webhook_url: send_email_notification( GitHub监控异常告警, anomaly_message, [adminexample.com] ) # 生成周报如果是周日 if datetime.now().weekday() 6: # 周日 trends analyze_trends(historical_data) report format_weekly_report(trends, [ {name: repo, stars: stats.stars, change: change[stars_change]} for repo, stats, change in zip(config.repositories, current_stats, changes) ]) send_email_notification( GitHub监控周报, report, [teamexample.com] ) return { monitored_repositories: len(config.repositories), significant_changes: len([c for c in changes if c[significant_change]]), anomalies_detected: len(all_anomalies), timestamp: datetime.now() } flow(namescheduled_github_monitor) def scheduled_github_monitor(): 定时执行的监控工作流 config MonitorConfig( repositories[ PrefectHQ/prefect, pallets/flask, django/django, psf/requests ], check_interval_minutes60, notify_on_changesTrue, webhook_urlhttps://hooks.slack.com/services/your/webhook ) return asyncio.run(github_repository_monitor(config))4.4 运行与测试创建测试脚本test_flow.pyfrom src.flows.github_monitor import scheduled_github_monitor from src.models.github_models import MonitorConfig import asyncio def test_flow_locally(): 本地测试工作流 try: result scheduled_github_monitor() print(f工作流执行成功: {result}) except Exception as e: print(f工作流执行失败: {e}) if __name__ __main__: test_flow_locally()运行测试python test_flow.py4.5 部署到 Prefect Server创建部署脚本deploy.pyfrom prefect import flow from src.flows.github_monitor import scheduled_github_monitor if __name__ __main__: # 创建部署 deployment scheduled_github_monitor.serve( namegithub-monitor-production, cron0 * * * *, # 每小时执行一次 tags[monitoring, github, production], description生产环境GitHub仓库监控, version1.0.0 ) print(部署创建成功) print(f部署ID: {deployment.id})执行部署python deploy.py5. 高级特性与最佳实践5.1 错误处理与重试策略Prefect 提供了强大的错误处理机制from prefect import flow, task from prefect.blocks.system import JSON import random from typing import Optional class CircuitBreaker: 简单的熔断器实现 def __init__(self, failure_threshold5, reset_timeout60): self.failure_threshold failure_threshold self.reset_timeout reset_timeout self.failure_count 0 self.last_failure_time None def should_break(self) - bool: if (self.last_failure_time and (time.time() - self.last_failure_time) self.reset_timeout and self.failure_count self.failure_threshold): return True return False def record_failure(self): self.failure_count 1 self.last_failure_time time.time() def record_success(self): self.failure_count 0 self.last_failure_time None task( retries3, retry_delay_seconds10, retry_jitter_factor0.5 # 添加随机延迟避免惊群效应 ) def unreliable_api_call(endpoint: str) - Optional[dict]: 模拟不可靠的API调用 circuit_breaker CircuitBreaker() if circuit_breaker.should_break(): raise Exception(熔断器开启跳过API调用) try: # 模拟API调用 if random.random() 0.3: # 30%失败率 raise Exception(API调用失败) # 模拟成功响应 circuit_breaker.record_success() return {status: success, data: 示例数据} except Exception as e: circuit_breaker.record_failure() raise e flow def resilient_data_pipeline(): 具备弹性的数据流水线 try: # 使用指数退避策略 result unreliable_api_call.with_retry( retries5, delay_seconds2, backoff_factor2 # 指数退避 )(https://api.example.com/data) return result except Exception as e: # 优雅降级 print(f主流程失败使用备用数据源: {e}) return fallback_data_source() task def fallback_data_source() - dict: 备用数据源 return {status: fallback, data: 备用数据}5.2 性能优化与并发控制from prefect import flow, task from prefect.task_runners import ConcurrentTaskRunner, SequentialTaskRunner import asyncio from typing import List import time task def cpu_intensive_processing(data: List) - List: CPU密集型任务 # 模拟复杂计算 processed [x * 2 for x in data] time.sleep(1) # 模拟计算耗时 return processed task def io_intensive_operation(url: str) - str: I/O密集型任务 # 模拟网络请求 time.sleep(2) return fData from {url} flow(task_runnerConcurrentTaskRunner()) async def optimized_pipeline(urls: List[str], data_batches: List[List]): 优化的工作流并发执行I/O操作 # 并发执行所有I/O操作 io_results await asyncio.gather( *[io_intensive_operation(url) for url in urls] ) # 顺序执行CPU密集型任务避免资源竞争 cpu_results [] for batch in data_batches: result cpu_intensive_processing(batch) cpu_results.append(result) return { io_results: io_results, cpu_results: cpu_results } # 配置并发限制 task(task_run_namebatch-process-{batch_number}, concurrency_limit5) def batch_processing(batch_number: int, data: List) - List: 限制并发数的批处理任务 return [x ** 2 for x in data]5.3 配置管理与环境隔离from prefect.blocks.core import Block from pydantic import BaseModel from typing import Dict, Any import os class DatabaseConfig(BaseModel): host: str port: int database: str username: str password: str property def connection_string(self) - str: return fpostgresql://{self.username}:{self.password}{self.host}:{self.port}/{self.database} class APIConfig(BaseModel): base_url: str api_key: str timeout: int 30 retry_attempts: int 3 class EnvironmentConfig(Block): 环境配置块 environment: str # dev, staging, production database: DatabaseConfig api: APIConfig features: Dict[str, bool] class Config: arbitrary_types_allowed True # 使用配置块 flow def environment_aware_flow(): 环境感知的工作流 config EnvironmentConfig.load(production-config) print(f运行环境: {config.environment}) print(f数据库: {config.database.connection_string}) # 根据环境启用不同功能 if config.features.get(advanced_logging, False): enable_advanced_logging() if config.environment production: enable_production_safeguards() def get_environment_config(env: str None) - EnvironmentConfig: 获取环境配置 env env or os.getenv(PREFECT_ENVIRONMENT, dev) configs { dev: EnvironmentConfig( environmentdev, databaseDatabaseConfig( hostlocalhost, port5432, databasedev_db, usernamedev_user, passworddev_pass ), apiAPIConfig( base_urlhttps://dev.api.example.com, api_keydev_key ), features{debug: True, advanced_logging: False} ), production: EnvironmentConfig( environmentproduction, databaseDatabaseConfig( hostprod-db.example.com, port5432, databaseprod_db, usernameos.getenv(DB_USER), passwordos.getenv(DB_PASSWORD) ), apiAPIConfig( base_urlhttps://api.example.com, api_keyos.getenv(API_KEY), timeout60, retry_attempts5 ), features{debug: False, advanced_logging: True} ) } return configs[env]6. 常见问题与故障排查6.1 安装与配置问题问题1Prefect 安装失败现象pip install prefect命令执行失败出现依赖冲突或编译错误。解决方案# 1. 使用更新的pip版本 pip install --upgrade pip # 2. 使用conda环境推荐用于解决依赖冲突 conda create -n prefect-env python3.10 conda activate prefect-env pip install prefect # 3. 使用uv安装器避免依赖冲突 curl -LsSf https://astral.sh/uv/install.sh | sh uv add prefect问题2Prefect Server 启动失败现象prefect server start命令执行后服务无法正常启动。排查步骤检查端口占用lsof -i :4200检查Docker服务状态docker ps查看详细日志prefect server start --verbose解决方案# 停止并清理现有容器 docker stop $(docker ps -aq) # 停止所有容器 docker system prune -f # 清理资源 # 重新启动 prefect server start6.2 工作流执行问题问题3任务一直处于排队状态现象任务提交后始终显示为Queued状态无法开始执行。可能原因没有可用的执行代理Agent任务资源要求超过可用资源并发限制设置过低解决方案# 检查代理状态 prefect agent list # 启动本地代理 prefect agent start --work-queue default # 检查工作队列 prefect work-queue ls问题4任务执行超时现象任务运行时间超过配置的超时时间被自动取消。解决方案task(timeout_seconds3600) # 增加超时时间 def long_running_task(): # 任务逻辑 pass # 或者使用异步任务避免阻塞 task async def async_long_running_task(): await asyncio.sleep(300) # 异步等待6.3 监控与调试技巧启用详细日志import logging from prefect.logging import get_run_logger flow def debug_flow(): logger get_run_logger() logger.info(开始执行流程) try: # 业务逻辑 logger.debug(详细调试信息) except Exception as e: logger.error(f执行失败: {e}, exc_infoTrue) raise使用 Prefect UI 进行调试访问 http://localhost:4200查看流程运行历史检查任务输入输出查看执行时间线6.4 性能优化问题问题5工作流执行速度慢优化策略from prefect import flow, task from prefect.task_runners import ConcurrentTaskRunner import asyncio task(cache_key_fnlambda *args, **kwargs: static-key) def expensive_computation(): 使用缓存避免重复计算 pass flow(task_runnerConcurrentTaskRunner()) async def optimized_flow(): 使用并发执行器 # 并发执行独立任务 results await asyncio.gather( task1(), task2(), task3() ) return results7. 生产环境部署最佳实践7.1 基础设施配置使用 Docker 容器化部署FROM python:3.10-slim WORKDIR /app # 安装系统依赖 RUN apt-get update apt-get install -y \ gcc \ rm -rf /var/lib/apt/lists/* # 复制依赖文件 COPY requirements.txt . # 安装Python依赖 RUN pip install --no-cache-dir -r requirements.txt # 复制应用代码 COPY src/ ./src/ # 设置环境变量 ENV PYTHONPATH/app/src ENV PREFECT_HOME/app/.prefect # 启动命令 CMD [python, -m, prefect.engine]Kubernetes 部署配置apiVersion: apps/v1 kind: Deployment metadata: name: prefect-agent spec: replicas: 3 selector: matchLabels: app: prefect-agent template: metadata: labels: app: prefect-agent spec: containers: - name: agent image: prefecthq/prefect:2-python3.10 command: [prefect, agent, start, -q, kubernetes] env: - name: PREFECT_API_URL value: http://prefect-server:4200/api resources: requests: memory: 256Mi cpu: 250m limits: memory: 512Mi cpu: 500m7.2 安全配置敏感信息管理from prefect.blocks.system import Secret from prefect.blocks.core import Block import os # 使用Prefect Secret块存储敏感信息 db_password Secret.load(database-password) # 或者使用环境变量 flow def secure_flow(): api_key os.getenv(API_KEY) database_url os.getenv(DATABASE_URL) # 验证配置 if not api_key or not database_url: raise ValueError(缺少必要的环境变量配置) # 创建Secret块 from prefect.blocks.system import Secret secret_block Secret(valuemy-secret-password) secret_block.save(namedatabase-password, overwriteTrue)网络安全配置from prefect import flow import ssl import httpx flow def secure_api_flow(): 使用安全连接的API调用 ssl_context ssl.create_default_context() ssl_context.check_hostname True ssl_context.verify_mode ssl.CERT_REQUIRED async with httpx.AsyncClient(verifyssl_context) as client: response await client.get(https://secure-api.example.com) return response.json()7.3 监控与告警集成 Prometheus 监控from prometheus_client import Counter, Histogram, start_http_server import time # 定义指标 FLOW_SUCCESS Counter(prefect_flow_success, 成功执行的流程数, [flow_name]) FLOW_FAILURE Counter(prefect_flow_failure, 失败的