本文目录导读:

我来详细介绍Python脚本生成Beam管道配置的几种方法:
直接配置读取(最常用)
import json
import yaml
from typing import Dict, Any
import apache_beam as beam
from apache_beam.options.pipeline_options import PipelineOptions
# 从JSON文件读取配置
def load_config_from_json(json_path: str) -> Dict[str, Any]:
with open(json_path, 'r') as f:
return json.load(f)
# 从YAML文件读取配置
def load_config_from_yaml(yaml_path: str) -> Dict[str, Any]:
with open(yaml_path, 'r') as f:
return yaml.safe_load(f)
# 应用配置到管道
def create_pipeline_with_config(config: Dict[str, Any]):
options = PipelineOptions(**config['pipeline_options'])
with beam.Pipeline(options=options) as p:
# 根据配置创建数据源
input_data = p | 'ReadFromSource' >> beam.io.ReadFromText(
config['input']['path']
)
# 应用转换
transformed = input_data | 'Transform' >> beam.Map(
lambda x: apply_transformations(x, config['transformations'])
)
# 写入目标
transformed | 'WriteToSink' >> beam.io.WriteToText(
config['output']['path']
)
def apply_transformations(data: str, transformations: list):
result = data
for trans in transformations:
if trans['type'] == 'filter':
if trans['condition'] not in result:
return None
elif trans['type'] == 'map':
result = result.replace(trans['from'], trans['to'])
return result
# 配置示例
config = {
"pipeline_options": {
"project": "my-project",
"region": "us-central1",
"runner": "DataflowRunner",
"staging_location": "gs://my-bucket/staging",
"temp_location": "gs://my-bucket/temp",
},
"input": {
"type": "text",
"path": "gs://my-bucket/input/*.txt"
},
"transformations": [
{"type": "filter", "condition": "ERROR"},
{"type": "map", "from": "old", "to": "new"}
],
"output": {
"type": "text",
"path": "gs://my-bucket/output/"
}
}
模板化配置生成
from jinja2 import Template
import os
class BeamConfigGenerator:
"""Beam管道配置生成器"""
def __init__(self, template_path: str = None):
self.template_path = template_path or os.path.join(
os.path.dirname(__file__), 'templates'
)
def generate_pipeline_code(self, config: Dict[str, Any]) -> str:
"""生成管道Python代码"""
template = Template("""
import apache_beam as beam
from apache_beam.options.pipeline_options import PipelineOptions
def run():
options = PipelineOptions(
project='{{ config.project }}',
runner='{{ config.runner }}',
staging_location='{{ config.staging_location }}',
temp_location='{{ config.temp_location }}'
)
with beam.Pipeline(options=options) as p:
{% for source in config.sources %}
input_data = p | 'ReadFrom{{ source.name }}' >>
beam.io.ReadFrom{{ source.type }}(
'{{ source.path }}'
)
{% endfor %}
{% for transform in config.transforms %}
transformed_data = input_data | '{{ transform.name }}' >>
{{ transform.code }}
{% endfor %}
{% for sink in config.sinks %}
transformed_data | 'WriteTo{{ sink.name }}' >>
beam.io.WriteTo{{ sink.type }}(
'{{ sink.path }}'
)
{% endfor %}
if __name__ == '__main__':
run()
""")
return template.render(config=config)
def generate_yaml_config(self, config: Dict[str, Any]) -> str:
"""生成YAML配置文件"""
import yaml
return yaml.dump(config, default_flow_style=False)
def save_config(self, config: Dict[str, Any], output_path: str):
"""保存配置到文件"""
import json
ext = os.path.splitext(output_path)[1].lower()
if ext in ['.yaml', '.yml']:
with open(output_path, 'w') as f:
yaml.dump(config, f)
elif ext == '.json':
with open(output_path, 'w') as f:
json.dump(config, f, indent=2)
elif ext == '.py':
code = self.generate_pipeline_code(config)
with open(output_path, 'w') as f:
f.write(code)
# 使用示例
generator = BeamConfigGenerator()
config = {
"project": "my-project",
"runner": "DataflowRunner",
"staging_location": "gs://my-bucket/staging",
"temp_location": "gs://my-bucket/temp",
"sources": [
{
"name": "Input",
"type": "Text",
"path": "gs://my-bucket/input/*.csv"
}
],
"transforms": [
{
"name": "ParseCSV",
"code": "beam.Map(lambda line: line.split(','))"
},
{
"name": "FilterErrors",
"code": "beam.Filter(lambda x: x[0] != 'ERROR')"
}
],
"sinks": [
{
"name": "Output",
"type": "Text",
"path": "gs://my-bucket/output/"
}
]
}
# 生成不同格式的配置
generator.save_config(config, "pipeline_config.yaml")
generator.save_config(config, "pipeline_config.json")
generator.save_config(config, "pipeline_code.py")
动态配置工厂模式
from abc import ABC, abstractmethod
from typing import List, Dict, Any
import apache_beam as beam
class PipelineComponent(ABC):
"""管道组件基类"""
@abstractmethod
def create(self, config: Dict[str, Any]):
pass
class SourceFactory(PipelineComponent):
"""数据源工厂"""
def create(self, config: Dict[str, Any]):
source_type = config['type']
if source_type == 'text':
return beam.io.ReadFromText(
config['path'],
skip_header_lines=config.get('skip_header', 0)
)
elif source_type == 'avro':
return beam.io.ReadFromAvro(config['path'])
elif source_type == 'parquet':
return beam.io.ReadFromParquet(config['path'])
elif source_type == 'bigquery':
return beam.io.ReadFromBigQuery(
query=config['query'],
use_standard_sql=True
)
else:
raise ValueError(f"Unsupported source type: {source_type}")
class TransformFactory(PipelineComponent):
"""转换工厂"""
def create(self, config: List[Dict[str, Any]]):
transforms = []
for trans_config in config:
trans_type = trans_config['type']
if trans_type == 'map':
transforms.append(
beam.Map(lambda x: x, **trans_config.get('params', {}))
)
elif trans_type == 'filter':
transforms.append(
beam.Filter(**trans_config.get('params', {}))
)
elif trans_type == 'flatmap':
transforms.append(
beam.FlatMap(**trans_config.get('params', {}))
)
elif trans_type == 'combine':
transforms.append(
beam.CombineGlobally(**trans_config.get('params', {}))
)
return transforms
class SinkFactory(PipelineComponent):
"""输出工厂"""
def create(self, config: Dict[str, Any]):
sink_type = config['type']
if sink_type == 'text':
return beam.io.WriteToText(
config['path'],
file_name_suffix=config.get('suffix', '.txt')
)
elif sink_type == 'avro':
return beam.io.WriteToAvro(
config['path'],
schema=config.get('schema')
)
elif sink_type == 'bigquery':
return beam.io.WriteToBigQuery(
table=config['table'],
schema=config.get('schema'),
write_disposition=config.get('write_disposition', 'WRITE_APPEND')
)
else:
raise ValueError(f"Unsupported sink type: {sink_type}")
class PipelineBuilder:
"""管道构建器"""
def __init__(self):
self.source_factory = SourceFactory()
self.transform_factory = TransformFactory()
self.sink_factory = SinkFactory()
def build(self, config: Dict[str, Any]):
"""根据配置构建管道"""
# 创建管道选项
options = PipelineOptions(**config.get('pipeline_options', {}))
with beam.Pipeline(options=options) as p:
# 创建数据源
source = self.source_factory.create(config['source'])
data = p | 'Read' >> source
# 应用转换
transforms = self.transform_factory.create(
config.get('transforms', [])
)
for i, transform in enumerate(transforms):
data = data | f'Transform_{i}' >> transform
# 写入目标
sink = self.sink_factory.create(config['sink'])
data | 'Write' >> sink
# 使用示例
builder = PipelineBuilder()
config = {
"pipeline_options": {
"project": "my-project",
"runner": "DirectRunner" # 本地测试用
},
"source": {
"type": "text",
"path": "gs://my-bucket/input/*.txt",
"skip_header": 1
},
"transforms": [
{
"type": "map",
"params": {"fn": lambda x: x.strip()}
},
{
"type": "filter",
"params": {"fn": lambda x: x != ""}
}
],
"sink": {
"type": "text",
"path": "gs://my-bucket/output/",
"suffix": ".csv"
}
}
# 构建并运行管道
builder.build(config)
命令行配置生成器
import argparse
import json
import sys
from typing import Dict, Any
class ConfigGeneratorCLI:
"""配置生成器命令行工具"""
def __init__(self):
self.parser = argparse.ArgumentParser(
description='Beam管道配置生成器'
)
self._setup_arguments()
def _setup_arguments(self):
self.parser.add_argument(
'--input-type',
choices=['text', 'avro', 'parquet', 'bigquery', 'pubsub'],
required=True,
help='输入数据类型'
)
self.parser.add_argument(
'--input-path',
required=True,
help='输入数据路径'
)
self.parser.add_argument(
'--output-type',
choices=['text', 'avro', 'parquet', 'bigquery', 'pubsub'],
required=True,
help='输出数据类型'
)
self.parser.add_argument(
'--output-path',
required=True,
help='输出数据路径'
)
self.parser.add_argument(
'--runner',
choices=['DirectRunner', 'DataflowRunner', 'SparkRunner', 'FlinkRunner'],
default='DirectRunner',
help='管道运行器'
)
self.parser.add_argument(
'--transforms',
nargs='+',
help='转换操作列表 (格式: type:param1=value1,param2=value2)'
)
self.parser.add_argument(
'--output-format',
choices=['json', 'yaml', 'python'],
default='json',
help='输出配置格式'
)
self.parser.add_argument(
'--output-file',
help='输出文件路径 (默认输出到stdout)'
)
def generate_config(self, args) -> Dict[str, Any]:
"""从命令行参数生成配置"""
config = {
"pipeline_options": {
"runner": args.runner
},
"source": {
"type": args.input_type,
"path": args.input_path
},
"sink": {
"type": args.output_type,
"path": args.output_path
},
"transforms": []
}
# 解析转换操作
if args.transforms:
for trans_str in args.transforms:
parts = trans_str.split(':')
trans_type = parts[0]
params = {}
if len(parts) > 1:
for param in parts[1].split(','):
key, value = param.split('=')
params[key] = value
config["transforms"].append({
"type": trans_type,
"params": params
})
return config
def save_config(self, config: Dict[str, Any], output_format: str,
output_file: str = None):
"""保存配置到文件或stdout"""
if output_format == 'json':
output = json.dumps(config, indent=2)
elif output_format == 'yaml':
import yaml
output = yaml.dump(config, default_flow_style=False)
elif output_format == 'python':
output = self._generate_python_code(config)
else:
raise ValueError(f"Unsupported format: {output_format}")
if output_file:
with open(output_file, 'w') as f:
f.write(output)
print(f"配置已保存到: {output_file}")
else:
print(output)
def _generate_python_code(self, config: Dict[str, Any]) -> str:
"""生成Python代码"""
# 使用之前定义的模板生成代码
generator = BeamConfigGenerator()
return generator.generate_pipeline_code(config)
def run(self):
"""运行命令行工具"""
args = self.parser.parse_args()
try:
config = self.generate_config(args)
self.save_config(config, args.output_format, args.output_file)
except Exception as e:
print(f"错误: {e}", file=sys.stderr)
sys.exit(1)
# 使用示例
if __name__ == '__main__':
cli = ConfigGeneratorCLI()
# 模拟命令行参数
sys.argv = [
'config_generator.py',
'--input-type', 'text',
'--input-path', 'gs://my-bucket/input/*.txt',
'--output-type', 'bigquery',
'--output-path', 'my_project:my_dataset.my_table',
'--runner', 'DataflowRunner',
'--transforms', 'map:fn=lambda x:x.strip()', 'filter:fn=lambda x:len(x)>0',
'--output-format', 'json',
'--output-file', 'pipeline_config.json'
]
cli.run()
环境变量配置
import os
from typing import Dict, Any, Optional
class EnvironmentConfigManager:
"""环境变量配置管理器"""
@staticmethod
def get_config_from_env() -> Dict[str, Any]:
"""从环境变量读取配置"""
config = {
"pipeline_options": {
"project": os.getenv('BEAM_PROJECT', 'default-project'),
"runner": os.getenv('BEAM_RUNNER', 'DirectRunner'),
"region": os.getenv('BEAM_REGION', 'us-central1'),
"staging_location": os.getenv('BEAM_STAGING_LOCATION'),
"temp_location": os.getenv('BEAM_TEMP_LOCATION'),
"max_num_workers": int(os.getenv('BEAM_MAX_WORKERS', '5')),
"machine_type": os.getenv('BEAM_MACHINE_TYPE', 'n1-standard-4')
},
"source": {
"type": os.getenv('SOURCE_TYPE', 'text'),
"path": os.getenv('SOURCE_PATH'),
"schema": os.getenv('SOURCE_SCHEMA')
},
"transforms": EnvironmentConfigManager._parse_transforms(
os.getenv('TRANSFORMS', '')
),
"sink": {
"type": os.getenv('SINK_TYPE', 'text'),
"path": os.getenv('SINK_PATH'),
"schema": os.getenv('SINK_SCHEMA')
}
}
return config
@staticmethod
def _parse_transforms(transforms_str: str) -> list:
"""解析转换配置字符串"""
if not transforms_str:
return []
transforms = []
for trans in transforms_str.split(';'):
parts = trans.split(',')
if len(parts) >= 1:
transform = {
"type": parts[0].strip(),
"params": {}
}
for param in parts[1:]:
if '=' in param:
key, value = param.split('=', 1)
transform["params"][key.strip()] = value.strip()
transforms.append(transform)
return transforms
@staticmethod
def validate_config(config: Dict[str, Any]) -> bool:
"""验证配置是否完整"""
required_fields = [
('source', 'path'),
('sink', 'path'),
]
for section, field in required_fields:
if not config.get(section, {}).get(field):
print(f"缺少必填配置: {section}.{field}")
return False
return True
@staticmethod
def print_config(config: Dict[str, Any]):
"""打印当前配置"""
print("=" * 50)
print("Beam管道配置:")
print("=" * 50)
for section, values in config.items():
print(f"\n[{section}]")
for key, value in values.items():
if value:
print(f" {key}: {value}")
# 使用示例
config_manager = EnvironmentConfigManager()
# 设置环境变量(实际使用中应在shell或部署脚本中设置)
os.environ['SOURCE_PATH'] = 'gs://my-bucket/input/*.txt'
os.environ['SINK_PATH'] = 'gs://my-bucket/output/'
os.environ['TRANSFORMS'] = 'map,fn=lambda x:x.upper();filter,fn=lambda x:len(x)>0'
# 读取配置
config = config_manager.get_config_from_env()
# 验证配置
if config_manager.validate_config(config):
config_manager.print_config(config)
# 使用配置构建管道
builder = PipelineBuilder()
builder.build(config)
else:
print("配置验证失败,请检查环境变量设置")
最佳实践建议
# 1. 使用配置文件结合环境变量
class HybridConfigLoader:
"""混合配置加载器"""
@staticmethod
def load_config(config_files: list = None):
"""从多个源加载配置"""
config = {}
# 从文件加载
if config_files:
for file_path in config_files:
file_config = load_file_config(file_path)
config = deep_merge(config, file_config)
# 环境变量覆盖
env_config = EnvironmentConfigManager.get_config_from_env()
config = deep_merge(config, env_config)
return config
# 2. 配置版本管理
class ConfigVersionManager:
"""配置版本管理"""
def __init__(self, storage_path: str):
self.storage_path = storage_path
def save_version(self, config: Dict[str, Any], version: str):
"""保存配置版本"""
version_path = f"{self.storage_path}/v{version}/"
os.makedirs(version_path, exist_ok=True)
with open(f"{version_path}/config.json", 'w') as f:
json.dump(config, f, indent=2)
def load_version(self, version: str) -> Dict[str, Any]:
"""加载特定版本的配置"""
version_path = f"{self.storage_path}/v{version}/config.json"
with open(version_path, 'r') as f:
return json.load(f)
# 3. 配置验证装饰器
def validate_config(func):
"""配置验证装饰器"""
def wrapper(config: Dict[str, Any], *args, **kwargs):
required_keys = ['source', 'sink', 'pipeline_options']
for key in required_keys:
if key not in config:
raise ValueError(f"缺少必填配置项: {key}")
return func(config, *args, **kwargs)
return wrapper
这些方法可以根据您的具体需求选择和组合使用,最常用的是第一种直接配置读取方式,配合YAML或JSON配置文件,既灵活又易于维护。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播