返回博客

Pipeline 开发指南

Pipeline 是 coomia-dip 平台的数据处理核心。本文介绍如何使用 Python SDK 和 YAML 声明式配置构建端到端数据管道:从数据源接入、清洗转换、到 Ontology 对象写入。涵盖批处理 Pipeline、实时 Pipeline、增量同步三种模式,以及错误处理、监控和性能调优。

Coomia发布于 2026年1月16日11 分钟阅读
分享本文Twitter / X

系列:S12 开发者教程 · 第 7 篇 | 难度:中级 | 阅读时间:15 分钟

Pipeline 开发指南

#TL;DR

Pipeline 是 coomia-dip 平台的数据处理核心。本文介绍如何使用 Python SDK 和 YAML 声明式配置构建端到端数据管道:从数据源接入、清洗转换、到 Ontology 对象写入。涵盖批处理 Pipeline、实时 Pipeline、增量同步三种模式,以及错误处理、监控和性能调优。

#1. Pipeline 概述

#1.1 什么是 Pipeline?

Pipeline 是 coomia-dip 中将原始数据转化为 Ontology 对象的自动化流程。它运行在 Data Layer(Data Layer)上,基于 Quarkus 3.x 构建,通过 gRPC 与 Control Layer 协调。

Code
数据源 → 抽取(Extract) → 转换(Transform) → 加载(Load) → Ontology 对象
         ↓                ↓                 ↓
       Source           Transform          Sink
       Connector        Stage              Writer

#1.2 Pipeline 类型

类型场景调度方式延迟
批处理(Batch)历史数据导入、全量同步定时/手动分钟~小时
实时(Streaming)实时数据接入、CDC持续运行秒~毫秒
增量(Incremental)定期增量同步定时分钟

#1.3 架构设计

Code
┌───────────────────────────────────────┐
│            Pipeline Manager           │
│         (Control Layer - B)           │
├───────────────────────────────────────┤
│  Pipeline Registry │ Schedule Manager │
│  Version Control   │ Dependency Graph │
└────────┬──────────────────┬───────────┘
         │ gRPC             │ gRPC
         ▼                  ▼
┌─────────────────┐ ┌──────────────────┐
│  Batch Executor │ │ Stream Executor  │
│  (Data Layer-C) │ │ (Flink/CDC)      │
├─────────────────┤ ├──────────────────┤
│ Source Connectors│ │ Kafka Consumer   │
│ Transform Engine │ │ Transform Engine │
│ Sink Writers     │ │ Sink Writers     │
└─────────────────┘ └──────────────────┘
         │                  │
         ▼                  ▼
┌───────────────────────────────────────┐
│     Storage Layer (Doris + Iceberg)   │
└───────────────────────────────────────┘

#2. 环境准备

#2.1 安装依赖

Bash
pip install ontology-sdk>=1.0.0
pip install ontology-sdk[pipeline]  # Pipeline 扩展包

#2.2 初始化

Python
from ontology_sdk import OntoPlatform
from ontology_sdk.pipeline import PipelineBuilder, BatchPipeline

platform = OntoPlatform(
    control_plane_url="localhost:50051",
    data_plane_url="localhost:50052"
)

pipeline_manager = platform.pipelines

#3. 批处理 Pipeline

#3.1 YAML 声明式定义

YAML
# pipelines/employee_import.yaml
name: employee_import
display_name: 员工数据导入
description:  HR 系统批量导入员工数据到 Ontology
version: "1.0.0"
type: batch

source:
  type: jdbc
  config:
    driver: mysql
    url: jdbc:mysql://hr-db:3306/hr_system
    username: ${HR_DB_USER}
    password: ${HR_DB_PASS}
    query: |
      SELECT
        employee_id, name, email, department_code,
        salary, hire_date, job_level, manager_id
      FROM employees
      WHERE updated_at > :last_sync_time

transform:
  stages:
    - name: clean_data
      type: data_quality
      rules:
        - field: email
          validate: email_format
          on_fail: reject
        - field: name
          validate: not_empty
          on_fail: reject
        - field: salary
          validate: range
          min: 0
          max: 10000000
          on_fail: flag

    - name: enrich
      type: mapping
      mappings:
        - source: employee_id
          target: external_id
        - source: name
          target: name
        - source: email
          target: email
        - source: department_code
          target: department
          lookup:
            type: reference
            object_type: Department
            match_field: code
            return_field: rid
        - source: salary
          target: salary
          transform: "round(value, 2)"
        - source: hire_date
          target: hire_date
          transform: "parse_date(value, 'yyyy-MM-dd')"
        - source: job_level
          target: level
          transform: |
            mapping = {
              'J1': 'junior', 'J2': 'junior',
              'M1': 'mid', 'M2': 'mid',
              'S1': 'senior', 'S2': 'senior',
              'P1': 'staff', 'P2': 'principal'
            }
            mapping.get(value, 'unknown')

    - name: dedup
      type: deduplication
      key: external_id
      strategy: latest_wins

sink:
  type: ontology
  config:
    object_type: Employee
    upsert_key: external_id
    batch_size: 500
    on_conflict: update

schedule:
  cron: "0 2 * * *"   # 每天凌晨 2 点
  timezone: Asia/Shanghai
  retry:
    max_attempts: 3
    backoff: exponential
    initial_delay: 60s

monitoring:
  alerts:
    - type: failure
      channel: dingtalk
      recipients: [data-team]
    - type: data_quality
      threshold: 0.95   # 数据质量低于 95% 告警
      channel: email
      recipients: [data-owner]

#3.2 Python API 定义

Python
from ontology_sdk.pipeline import (
    PipelineBuilder, JdbcSource, OntologySink,
    CleanStage, MappingStage, DedupStage
)

pipeline = (
    PipelineBuilder("employee_import")
    .display_name("员工数据导入")
    .type("batch")
    .source(
        JdbcSource(
            driver="mysql",
            url="jdbc:mysql://hr-db:3306/hr_system",
            query="SELECT * FROM employees WHERE updated_at > :last_sync_time"
        )
    )
    .transform(
        CleanStage("clean_data")
        .validate("email", "email_format", on_fail="reject")
        .validate("name", "not_empty", on_fail="reject")
        .validate("salary", "range", min=0, max=10000000, on_fail="flag")
    )
    .transform(
        MappingStage("enrich")
        .map("employee_id", "external_id")
        .map("name", "name")
        .map("email", "email")
        .map("department_code", "department",
             lookup={"object_type": "Department", "match_field": "code", "return_field": "rid"})
        .map("salary", "salary", transform="round(value, 2)")
        .map("hire_date", "hire_date", transform="parse_date(value, 'yyyy-MM-dd')")
    )
    .transform(
        DedupStage("dedup", key="external_id", strategy="latest_wins")
    )
    .sink(
        OntologySink(
            object_type="Employee",
            upsert_key="external_id",
            batch_size=500,
            on_conflict="update"
        )
    )
    .schedule(cron="0 2 * * *", timezone="Asia/Shanghai")
    .build()
)

# 注册 Pipeline
pipeline_manager.register(pipeline)
print(f"Pipeline 已注册: {pipeline.name}")

#3.3 执行与监控

Python
# 手动触发
run = pipeline_manager.trigger("employee_import")
print(f"运行 ID: {run.run_id}")

# 等待完成
result = run.wait(timeout=600)
print(f"状态: {result.status}")
print(f"处理记录: {result.records_processed}")
print(f"成功: {result.records_succeeded}")
print(f"失败: {result.records_failed}")
print(f"耗时: {result.duration_seconds}s")

# 查看失败记录
if result.records_failed > 0:
    failures = pipeline_manager.get_failures(run.run_id)
    for f in failures[:10]:
        print(f"  行 {f.row_number}: {f.error_message}")
        print(f"  原始数据: {f.source_data}")

#4. 实时 Pipeline

#4.1 Kafka 数据源

YAML
# pipelines/realtime_order_sync.yaml
name: realtime_order_sync
display_name: 实时订单同步
type: streaming

source:
  type: kafka
  config:
    bootstrap_servers: kafka:9092
    topic: order-events
    group_id: coomia-dip-order-sync
    auto_offset_reset: latest
    value_deserializer: json

transform:
  stages:
    - name: parse_event
      type: custom
      handler: |
        def transform(event):
            data = event['payload']
            return {
                'order_id': data['id'],
                'customer_rid': lookup_customer(data['customer_id']),
                'total_amount': float(data['total']),
                'status': data['status'].lower(),
                'items': data['line_items'],
                'created_at': parse_timestamp(data['created_at']),
                'updated_at': parse_timestamp(data['updated_at'])
            }

    - name: validate
      type: data_quality
      rules:
        - field: order_id
          validate: not_empty
        - field: total_amount
          validate: positive
        - field: status
          validate: enum
          values: [pending, confirmed, shipped, delivered, cancelled]

sink:
  type: ontology
  config:
    object_type: Order
    upsert_key: order_id
    batch_size: 100
    flush_interval: 5s
    on_conflict: update

monitoring:
  metrics:
    - name: throughput
      type: counter
      description: 每秒处理事件数
    - name: lag
      type: gauge
      description: 消费延迟
  alerts:
    - type: lag_threshold
      threshold: 10000
      channel: dingtalk

#4.2 Python 自定义 Transform

Python
from ontology_sdk.pipeline import StreamPipeline, KafkaSource, OntologySink
from ontology_sdk.pipeline.transforms import CustomTransform

class OrderTransform(CustomTransform):
    """订单事件转换器"""

    def __init__(self, platform):
        self.platform = platform
        self._customer_cache = {}

    def transform(self, record: dict) -> dict:
        payload = record.get("payload", record)

        # 客户 RID 查找(带缓存)
        customer_id = payload["customer_id"]
        if customer_id not in self._customer_cache:
            customer = self.platform.objects.get(
                "Customer",
                filters={"external_id": customer_id}
            )
            self._customer_cache[customer_id] = customer.rid if customer else None

        return {
            "order_id": payload["id"],
            "customer_rid": self._customer_cache.get(customer_id),
            "total_amount": float(payload["total"]),
            "status": payload["status"].lower(),
            "item_count": len(payload.get("line_items", [])),
            "created_at": payload["created_at"],
        }

    def on_error(self, record: dict, error: Exception) -> str:
        """错误处理策略:skip / retry / dead_letter"""
        if isinstance(error, ValidationError):
            return "dead_letter"
        return "retry"

# 构建流式 Pipeline
stream_pipeline = (
    StreamPipeline("realtime_order_sync")
    .source(KafkaSource(
        bootstrap_servers="kafka:9092",
        topic="order-events",
        group_id="coomia-dip-order-sync"
    ))
    .transform(OrderTransform(platform))
    .sink(OntologySink(
        object_type="Order",
        upsert_key="order_id",
        batch_size=100,
        flush_interval_seconds=5
    ))
    .build()
)

# 启动流式处理
pipeline_manager.start_stream(stream_pipeline)

#5. 增量同步 Pipeline

#5.1 基于水位线的增量同步

YAML
# pipelines/incremental_project_sync.yaml
name: incremental_project_sync
display_name: 项目数据增量同步
type: incremental

source:
  type: jdbc
  config:
    driver: postgresql
    url: jdbc:postgresql://pm-db:5432/project_mgmt
    query: |
      SELECT * FROM projects
      WHERE updated_at > :watermark
      ORDER BY updated_at ASC
    watermark:
      field: updated_at
      type: timestamp
      initial: "2024-01-01T00:00:00Z"

transform:
  stages:
    - name: map_fields
      type: mapping
      mappings:
        - source: project_id
          target: external_id
        - source: project_name
          target: name
        - source: project_status
          target: status
          transform: |
            status_map = {
              'ACTIVE': 'in_progress',
              'PLANNED': 'planning',
              'COMPLETED': 'done',
              'ON_HOLD': 'paused',
              'CANCELLED': 'cancelled'
            }
            status_map.get(value, 'unknown')
        - source: budget_amount
          target: budget
        - source: start_date
          target: start_date
        - source: end_date
          target: end_date

    - name: soft_delete_check
      type: custom
      handler: |
        def transform(record):
            if record.get('is_deleted'):
                record['_action'] = 'delete'
            return record

sink:
  type: ontology
  config:
    object_type: Project
    upsert_key: external_id
    handle_deletes: true

schedule:
  cron: "*/15 * * * *"   # 每 15 分钟
  timezone: Asia/Shanghai

#5.2 水位线管理

Python
# 查看水位线状态
watermark = pipeline_manager.get_watermark("incremental_project_sync")
print(f"当前水位线: {watermark.value}")
print(f"上次同步: {watermark.last_updated}")
print(f"同步记录数: {watermark.records_since_initial}")

# 重置水位线(重新全量同步)
pipeline_manager.reset_watermark(
    "incremental_project_sync",
    new_value="2024-01-01T00:00:00Z"
)

# 手动设置水位线
pipeline_manager.set_watermark(
    "incremental_project_sync",
    value="2025-06-01T00:00:00Z"
)

#6. 多源数据融合

#6.1 多源 Pipeline

YAML
# pipelines/customer_360_fusion.yaml
name: customer_360_fusion
display_name: 客户360数据融合
type: batch

sources:
  - name: crm_data
    type: jdbc
    config:
      driver: mysql
      url: jdbc:mysql://crm-db:3306/crm
      query: "SELECT * FROM customers WHERE updated_at > :last_sync_time"

  - name: billing_data
    type: jdbc
    config:
      driver: postgresql
      url: jdbc:postgresql://billing-db:5432/billing
      query: |
        SELECT customer_id,
               SUM(amount) AS total_revenue,
               COUNT(*) AS transaction_count,
               MAX(transaction_date) AS last_transaction
        FROM transactions
        WHERE transaction_date > :last_sync_time
        GROUP BY customer_id

  - name: support_data
    type: api
    config:
      url: https://support-api.internal/customers/export
      method: GET
      headers:
        Authorization: "Bearer ${SUPPORT_API_TOKEN}"
      pagination:
        type: cursor
        cursor_field: next_cursor

transform:
  stages:
    - name: join_sources
      type: join
      config:
        primary: crm_data
        joins:
          - source: billing_data
            on: crm_data.customer_id = billing_data.customer_id
            type: left
          - source: support_data
            on: crm_data.customer_id = support_data.customer_id
            type: left

    - name: compute_tier
      type: custom
      handler: |
        def transform(record):
            revenue = record.get('total_revenue', 0)
            if revenue > 10000000:
                record['tier'] = 'platinum'
            elif revenue > 5000000:
                record['tier'] = 'gold'
            elif revenue > 1000000:
                record['tier'] = 'silver'
            else:
                record['tier'] = 'bronze'
            return record

    - name: compute_health_score
      type: custom
      handler: |
        def transform(record):
            scores = []
            if record.get('last_transaction'):
                days_since = (now() - record['last_transaction']).days
                scores.append(max(0, 100 - days_since))
            if record.get('open_tickets', 0) > 5:
                scores.append(30)
            elif record.get('open_tickets', 0) > 0:
                scores.append(70)
            else:
                scores.append(100)
            record['health_score'] = sum(scores) / len(scores) if scores else 50
            return record

sink:
  type: ontology
  config:
    object_type: Customer
    upsert_key: external_id
    batch_size: 200

#7. 错误处理与重试

#7.1 错误处理策略

Python
from ontology_sdk.pipeline import ErrorHandler, DeadLetterQueue

# 配置错误处理
error_handler = ErrorHandler(
    max_retries=3,
    retry_backoff="exponential",
    initial_delay_seconds=5,
    dead_letter_queue=DeadLetterQueue(
        type="kafka",
        topic="pipeline-dead-letters"
    ),
    on_schema_error="reject",       # Schema 不匹配:拒绝
    on_transform_error="retry",     # 转换错误:重试
    on_sink_error="retry",          # 写入错误:重试
    on_source_error="fail_pipeline" # 数据源错误:终止
)

pipeline = (
    PipelineBuilder("robust_import")
    .error_handler(error_handler)
    # ... source, transform, sink 配置
    .build()
)

#7.2 Dead Letter Queue 处理

Python
# 查看 DLQ 中的失败记录
dlq_records = pipeline_manager.get_dead_letters(
    pipeline_name="employee_import",
    limit=50
)

for record in dlq_records:
    print(f"失败时间: {record.failed_at}")
    print(f"错误: {record.error_message}")
    print(f"原始数据: {record.original_data}")
    print(f"重试次数: {record.retry_count}")
    print("---")

# 重新处理 DLQ 记录
reprocess_result = pipeline_manager.reprocess_dead_letters(
    pipeline_name="employee_import",
    filter={"error_type": "TransientError"}
)
print(f"重新处理: {reprocess_result.total} 条, "
      f"成功: {reprocess_result.succeeded}")

#8. Pipeline 版本管理

#8.1 版本控制

Python
# 注册新版本
pipeline_v2 = (
    PipelineBuilder("employee_import")
    .version("2.0.0")
    .changelog("新增 manager_id 字段映射,优化去重策略")
    # ... 更新后的配置
    .build()
)
pipeline_manager.register(pipeline_v2)

# 查看版本历史
versions = pipeline_manager.list_versions("employee_import")
for v in versions:
    print(f"v{v.version}: {v.changelog} (注册于 {v.registered_at})")

# 回滚到指定版本
pipeline_manager.rollback("employee_import", target_version="1.0.0")

#9. 监控与告警

#9.1 Pipeline 仪表盘

Python
# 获取运行状态
status = pipeline_manager.get_status("employee_import")
print(f"当前状态: {status.state}")         # running / idle / failed
print(f"上次运行: {status.last_run_at}")
print(f"上次结果: {status.last_result}")
print(f"下次调度: {status.next_scheduled}")

# 获取运行历史
history = pipeline_manager.get_history(
    "employee_import",
    limit=10,
    since="2025-01-01"
)

for run in history:
    print(f"  {run.started_at} | {run.status} | "
          f"{run.records_processed} 条 | {run.duration}s")

# 获取指标
metrics = pipeline_manager.get_metrics("employee_import")
print(f"平均吞吐: {metrics.avg_throughput} records/s")
print(f"平均延迟: {metrics.avg_latency_ms} ms")
print(f"成功率: {metrics.success_rate:.1%}")
print(f"数据质量: {metrics.data_quality_score:.1%}")

#10. 完整实战:从零构建客户数据管道

Python
from ontology_sdk import OntoPlatform
from ontology_sdk.pipeline import PipelineBuilder, JdbcSource, OntologySink

platform = OntoPlatform(
    control_plane_url="localhost:50051",
    data_plane_url="localhost:50052"
)

# 第一步:定义 Object Type(如果尚未存在)
platform.schema.create_object_type(
    name="Customer",
    properties={
        "external_id": {"type": "string", "required": True, "indexed": True},
        "name": {"type": "string", "required": True},
        "industry": {"type": "string"},
        "region": {"type": "string"},
        "annual_revenue": {"type": "decimal"},
        "tier": {"type": "string", "enum": ["platinum", "gold", "silver", "bronze"]},
        "health_score": {"type": "integer", "min": 0, "max": 100},
    }
)

# 第二步:构建 Pipeline
pipeline = (
    PipelineBuilder("customer_sync")
    .display_name("客户数据同步")
    .type("incremental")
    .source(JdbcSource(
        driver="mysql",
        url="jdbc:mysql://crm-db:3306/crm",
        query="SELECT * FROM customers WHERE updated_at > :watermark",
        watermark_field="updated_at",
        watermark_type="timestamp"
    ))
    .transform_mapping({
        "id": "external_id",
        "company_name": "name",
        "industry_code": ("industry", lambda v: INDUSTRY_MAP.get(v, "Other")),
        "country": ("region", lambda v: REGION_MAP.get(v, v)),
        "revenue": ("annual_revenue", lambda v: round(float(v), 2)),
    })
    .transform_custom(compute_customer_tier)
    .sink(OntologySink(
        object_type="Customer",
        upsert_key="external_id",
        batch_size=500
    ))
    .schedule(cron="0 */4 * * *")  # 每 4 小时
    .build()
)

# 第三步:注册并测试
platform.pipelines.register(pipeline)

# 试运行(不写入)
dry_run = platform.pipelines.dry_run("customer_sync", limit=10)
print(f"试运行结果: {dry_run.records_would_process} 条")
for sample in dry_run.sample_output[:3]:
    print(f"  {sample}")

# 正式运行
run = platform.pipelines.trigger("customer_sync")
result = run.wait(timeout=300)
print(f"同步完成: {result.records_succeeded} 条成功")

#Key Takeaways

  1. 三种 Pipeline 模式:批处理适合全量同步,实时适合事件驱动,增量适合定期差量
  2. 声明式优先:优先使用 YAML 声明式定义,复杂逻辑用 Python 自定义 Transform
  3. 错误不丢失:配置 Dead Letter Queue 保证失败记录可追溯、可重处理
  4. 水位线机制:增量同步基于水位线,支持重置和手动调整
  5. 监控先行:每个 Pipeline 应配置告警和指标监控
  6. 版本管理:Pipeline 变更必须版本化,支持回滚

#Next Article

下一篇:S12-08 自定义函数开发指南 — 学习如何编写 Python 自定义函数,扩展 Ontology 的计算能力。

Tags: Pipeline ETL 数据管道 批处理 实时处理 增量同步 coomia-dip