dataops-sdk 0.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dataops-sdk might be problematic. Click here for more details.
- dataops_sdk/__init__.py +84 -0
- dataops_sdk/constants.py +6 -0
- dataops_sdk/exceptions.py +31 -0
- dataops_sdk/runner/__init__.py +46 -0
- dataops_sdk/runner/datasource_factory.py +83 -0
- dataops_sdk/runner/schemas/nacos/__init__.py +5 -0
- dataops_sdk/runner/schemas/nacos/config.py +40 -0
- dataops_sdk/runner/schemas/nacos/constants.py +235 -0
- dataops_sdk/runner/schemas/nacos/instance.py +103 -0
- dataops_sdk/runner/schemas/nacos/nacos_exception.py +44 -0
- dataops_sdk/runner/schemas/nacos/naming_param.py +62 -0
- dataops_sdk/runner/schemas/nacos/naming_request.py +84 -0
- dataops_sdk/runner/schemas/nacos/naming_response.py +50 -0
- dataops_sdk/runner/schemas/nacos/preserved_metadata_key.py +10 -0
- dataops_sdk/runner/schemas/nacos/service.py +119 -0
- dataops_sdk/runner/utils/__init__.py +42 -0
- dataops_sdk/runner/utils/common.py +209 -0
- dataops_sdk/runner/utils/dbConn_base.py +256 -0
- dataops_sdk/runner/utils/dbConn_sync.py +485 -0
- dataops_sdk/runner/utils/duckdb_manager.py +203 -0
- dataops_sdk/runner/utils/file_transfer_base.py +307 -0
- dataops_sdk/runner/utils/ftp_file_transfer.py +242 -0
- dataops_sdk/runner/utils/log.py +50 -0
- dataops_sdk/runner/utils/nacos.py +96 -0
- dataops_sdk/runner/utils/neo4j_sync.py +236 -0
- dataops_sdk/runner/utils/queue_base.py +224 -0
- dataops_sdk/runner/utils/queue_kafka.py +150 -0
- dataops_sdk/runner/utils/sftp_file_transfer.py +243 -0
- dataops_sdk/schemas/__init__.py +32 -0
- dataops_sdk/schemas/alarm.py +38 -0
- dataops_sdk/schemas/api.py +37 -0
- dataops_sdk/schemas/data_table.py +437 -0
- dataops_sdk/schemas/datasource.py +26 -0
- dataops_sdk/schemas/dict_.py +13 -0
- dataops_sdk/schemas/enums.py +120 -0
- dataops_sdk/schemas/group.py +40 -0
- dataops_sdk/schemas/hazard.py +151 -0
- dataops_sdk/schemas/meta.py +175 -0
- dataops_sdk/schemas/query.py +175 -0
- dataops_sdk/schemas/report.py +22 -0
- dataops_sdk/schemas/skill.py +70 -0
- dataops_sdk/schemas/table.py +65 -0
- dataops_sdk/schemas/user.py +52 -0
- dataops_sdk/schemas/widget.py +651 -0
- dataops_sdk/schemas/workflow.py +63 -0
- dataops_sdk/services/__init__.py +110 -0
- dataops_sdk/services/alarm.py +324 -0
- dataops_sdk/services/api.py +28 -0
- dataops_sdk/services/auth.py +120 -0
- dataops_sdk/services/data_table.py +485 -0
- dataops_sdk/services/dataset.py +127 -0
- dataops_sdk/services/datasource.py +57 -0
- dataops_sdk/services/dict_.py +69 -0
- dataops_sdk/services/hazard.py +260 -0
- dataops_sdk/services/meta.py +523 -0
- dataops_sdk/services/metrics.py +42 -0
- dataops_sdk/services/ref.py +66 -0
- dataops_sdk/services/report.py +36 -0
- dataops_sdk/services/skill.py +276 -0
- dataops_sdk/services/table.py +340 -0
- dataops_sdk/services/widget.py +143 -0
- dataops_sdk/services/workflow.py +102 -0
- dataops_sdk/session.py +292 -0
- dataops_sdk/tiptap/__init__.py +30 -0
- dataops_sdk/tiptap/builder.py +75 -0
- dataops_sdk/tiptap/constants.py +133 -0
- dataops_sdk/tiptap/markdown_parser.py +294 -0
- dataops_sdk/tiptap/models.py +43 -0
- dataops_sdk/tiptap/parse_utils.py +889 -0
- dataops_sdk/tiptap/render_utils.py +846 -0
- dataops_sdk/tiptap/renderers.py +51 -0
- dataops_sdk/tiptap/tool.py +629 -0
- dataops_sdk/utils/__init__.py +15 -0
- dataops_sdk/utils/common.py +211 -0
- dataops_sdk/utils/utils.py +120 -0
- dataops_sdk-0.3.0.dist-info/METADATA +25 -0
- dataops_sdk-0.3.0.dist-info/RECORD +79 -0
- dataops_sdk-0.3.0.dist-info/WHEEL +5 -0
- dataops_sdk-0.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"""Neo4j 图数据库连接模块。
|
|
2
|
+
|
|
3
|
+
提供 Neo4j 数据库连接管理,支持上下文管理器和 Cypher 语句执行。
|
|
4
|
+
|
|
5
|
+
Classes:
|
|
6
|
+
Neo4jConn: Neo4j 图数据库连接类
|
|
7
|
+
|
|
8
|
+
Example:
|
|
9
|
+
>>> with Neo4jConn(task_id="t1", host="localhost", port=7687,
|
|
10
|
+
... user="neo4j", password="123", db="neo4j") as conn:
|
|
11
|
+
... result = conn.select("MATCH (n) RETURN n LIMIT 10")
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from collections.abc import Iterable
|
|
15
|
+
|
|
16
|
+
from .log import getLogger
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Neo4jConn:
|
|
20
|
+
"""Neo4j 图数据库连接类。
|
|
21
|
+
|
|
22
|
+
支持上下文管理器(with 语句)、Cypher 语句执行和结果查询。
|
|
23
|
+
|
|
24
|
+
Attributes:
|
|
25
|
+
driver: Neo4j 数据库驱动
|
|
26
|
+
session: Neo4j 数据库会话
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
>>> with Neo4jConn(task_id="t1", host="localhost", port=7687,
|
|
30
|
+
... user="neo4j", password="123", db="neo4j") as conn:
|
|
31
|
+
... result = conn.select("MATCH (n) RETURN n LIMIT 10")
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
host: str,
|
|
37
|
+
port: int | str,
|
|
38
|
+
user: str,
|
|
39
|
+
password: str,
|
|
40
|
+
db: str,
|
|
41
|
+
log_sql: bool | None = None,
|
|
42
|
+
name: str | None = None,
|
|
43
|
+
):
|
|
44
|
+
from neo4j import GraphDatabase
|
|
45
|
+
|
|
46
|
+
self._host = host
|
|
47
|
+
self._port = port
|
|
48
|
+
self._user = user
|
|
49
|
+
self._password = password
|
|
50
|
+
self._database = db
|
|
51
|
+
# 实例级 log_sql 默认值:None 表示关闭(与 LoggerConfig.sql=False 一致);
|
|
52
|
+
# 各方法的 log_sql=None 时使用此值;显式传 bool 则覆盖。
|
|
53
|
+
self._log_sql: bool = bool(log_sql) if log_sql is not None else False
|
|
54
|
+
self._driver = GraphDatabase.driver(
|
|
55
|
+
f"bolt://{host}:{port}",
|
|
56
|
+
auth=(user, password),
|
|
57
|
+
database=db,
|
|
58
|
+
)
|
|
59
|
+
self._session = self._driver.session()
|
|
60
|
+
self._name = name or f"neo4j_{host}"
|
|
61
|
+
self._logger = getLogger(self._name)
|
|
62
|
+
|
|
63
|
+
def _should_log_sql(self, log_sql: bool | None) -> bool:
|
|
64
|
+
"""
|
|
65
|
+
解析方法级 log_sql 参数:
|
|
66
|
+
- None(未传)→ 使用实例级 self._log_sql
|
|
67
|
+
- 显式 bool → 使用调用方值
|
|
68
|
+
"""
|
|
69
|
+
return self._log_sql if log_sql is None else bool(log_sql)
|
|
70
|
+
|
|
71
|
+
def executemany(
|
|
72
|
+
self,
|
|
73
|
+
sql: str,
|
|
74
|
+
datas: Iterable[tuple | list],
|
|
75
|
+
*,
|
|
76
|
+
auto_commit: bool = False,
|
|
77
|
+
log_sql: bool | None = None,
|
|
78
|
+
**kwargs,
|
|
79
|
+
):
|
|
80
|
+
"""
|
|
81
|
+
执行多条 Cypher 语句
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
sql: 待执行的 Cypher 语句
|
|
85
|
+
datas: 待执行的语句参数列表
|
|
86
|
+
auto_commit: 是否自动提交,默认为 True
|
|
87
|
+
log_sql: 是否打印SQL日志,默认为 None(使用实例级 self._log_sql)
|
|
88
|
+
"""
|
|
89
|
+
if self._should_log_sql(log_sql):
|
|
90
|
+
self._logger.debug(f"executemany:\n {sql}")
|
|
91
|
+
for data in datas:
|
|
92
|
+
if hasattr(self, "_transaction") and self._transaction is not None:
|
|
93
|
+
self._transaction.run(sql, data)
|
|
94
|
+
else:
|
|
95
|
+
self._session.run(sql, data)
|
|
96
|
+
|
|
97
|
+
def execute(
|
|
98
|
+
self,
|
|
99
|
+
sql: str,
|
|
100
|
+
args: dict | list | tuple | None = None,
|
|
101
|
+
*,
|
|
102
|
+
auto_commit: bool = False,
|
|
103
|
+
log_sql: bool | None = None,
|
|
104
|
+
**kwargs,
|
|
105
|
+
):
|
|
106
|
+
"""
|
|
107
|
+
执行 Cypher 语句
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
sql: 待执行的 Cypher 语句
|
|
111
|
+
args: 待执行的语句参数
|
|
112
|
+
auto_commit: 是否自动提交,默认为 True
|
|
113
|
+
log_sql: 是否打印SQL日志,默认为 None(使用实例级 self._log_sql)
|
|
114
|
+
"""
|
|
115
|
+
if self._should_log_sql(log_sql):
|
|
116
|
+
self._logger.debug(f"execute:\n {sql}")
|
|
117
|
+
if hasattr(self, "_transaction") and self._transaction is not None:
|
|
118
|
+
self._transaction.run(sql, args)
|
|
119
|
+
else:
|
|
120
|
+
self._session.run(sql, args)
|
|
121
|
+
|
|
122
|
+
def select(
|
|
123
|
+
self,
|
|
124
|
+
sql: str,
|
|
125
|
+
args: dict | list | tuple | None = None,
|
|
126
|
+
size: int | None = None,
|
|
127
|
+
*,
|
|
128
|
+
log_sql: bool | None = None,
|
|
129
|
+
**kwargs,
|
|
130
|
+
) -> list[dict]:
|
|
131
|
+
"""
|
|
132
|
+
查询数据
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
cypher: 待执行的 Cypher 查询语句
|
|
136
|
+
args: 查询参数
|
|
137
|
+
size: 返回结果数量限制,None 表示返回全部
|
|
138
|
+
log_sql: 是否打印SQL日志,默认为 None(使用实例级 self._log_sql)
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
查询结果列表
|
|
142
|
+
"""
|
|
143
|
+
if self._should_log_sql(log_sql):
|
|
144
|
+
self._logger.debug(f"select:\n {sql}")
|
|
145
|
+
if hasattr(self, "_transaction") and self._transaction is not None:
|
|
146
|
+
result = self._transaction.run(sql, args).data()
|
|
147
|
+
else:
|
|
148
|
+
result = self._session.run(sql, args).data()
|
|
149
|
+
|
|
150
|
+
if size is not None and size > 0:
|
|
151
|
+
result = result[:size]
|
|
152
|
+
|
|
153
|
+
return result
|
|
154
|
+
|
|
155
|
+
# def begin(self):
|
|
156
|
+
# """
|
|
157
|
+
# 开始事务
|
|
158
|
+
# """
|
|
159
|
+
# if not hasattr(self, "_transaction") or self._transaction is None:
|
|
160
|
+
# self._transaction = self._session.begin_transaction()
|
|
161
|
+
|
|
162
|
+
# def commit(self):
|
|
163
|
+
# """
|
|
164
|
+
# 提交事务
|
|
165
|
+
# """
|
|
166
|
+
# if hasattr(self, "_transaction") and self._transaction is not None:
|
|
167
|
+
# self._transaction.commit()
|
|
168
|
+
# self._transaction = None
|
|
169
|
+
|
|
170
|
+
# def rollback(self):
|
|
171
|
+
# """
|
|
172
|
+
# 回滚事务
|
|
173
|
+
# """
|
|
174
|
+
# if hasattr(self, "_transaction") and self._transaction is not None:
|
|
175
|
+
# self._transaction.rollback()
|
|
176
|
+
# self._transaction = None
|
|
177
|
+
|
|
178
|
+
def close(self):
|
|
179
|
+
"""
|
|
180
|
+
关闭数据库连接
|
|
181
|
+
"""
|
|
182
|
+
if hasattr(self, "_session") and self._session is not None:
|
|
183
|
+
self._session.close()
|
|
184
|
+
if hasattr(self, "_driver") and self._driver is not None:
|
|
185
|
+
self._driver.close()
|
|
186
|
+
|
|
187
|
+
def __enter__(self):
|
|
188
|
+
"""
|
|
189
|
+
上下文管理器入口
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
self
|
|
193
|
+
"""
|
|
194
|
+
return self
|
|
195
|
+
|
|
196
|
+
def __exit__(self, exc_type, exc_val: Exception, exc_tb):
|
|
197
|
+
"""
|
|
198
|
+
上下文管理器退出,自动关闭连接
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
exc_type: 异常类型
|
|
202
|
+
exc_val: 异常值
|
|
203
|
+
exc_tb: 异常回溯
|
|
204
|
+
"""
|
|
205
|
+
self.close()
|
|
206
|
+
if exc_type:
|
|
207
|
+
raise exc_val
|
|
208
|
+
|
|
209
|
+
def __del__(self):
|
|
210
|
+
"""
|
|
211
|
+
析构函数,确保连接被关闭
|
|
212
|
+
"""
|
|
213
|
+
self.close()
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def driver(self):
|
|
217
|
+
"""
|
|
218
|
+
获取数据库连接
|
|
219
|
+
|
|
220
|
+
Returns:
|
|
221
|
+
数据库连接
|
|
222
|
+
"""
|
|
223
|
+
return self._driver
|
|
224
|
+
|
|
225
|
+
def get_session(self):
|
|
226
|
+
"""
|
|
227
|
+
获取数据库会话
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
数据库会话
|
|
231
|
+
"""
|
|
232
|
+
return self._driver.session()
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def name(self) -> str:
|
|
236
|
+
return self._name
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
"""
|
|
2
|
+
队列操作抽象基类模块。
|
|
3
|
+
|
|
4
|
+
提供统一的队列操作接口,支持 Kafka、RabbitMQ、Redis 等多种消息队列。
|
|
5
|
+
使用抽象基类定义统一的队列操作规范。
|
|
6
|
+
|
|
7
|
+
Example:
|
|
8
|
+
>>> class KafkaQueue(QueueBase):
|
|
9
|
+
... def post_init(self):
|
|
10
|
+
... from kafka import KafkaProducer, KafkaConsumer
|
|
11
|
+
... self._producer = KafkaProducer(bootstrap_servers=self._bootstrap_servers)
|
|
12
|
+
... self._consumer = KafkaConsumer(self._topic,
|
|
13
|
+
... bootstrap_servers=self._bootstrap_servers,
|
|
14
|
+
... group_id=self._group_id)
|
|
15
|
+
... def send(self, value, key=None):
|
|
16
|
+
... self._producer.send(self._topic, value=value, key=key)
|
|
17
|
+
... self._producer.flush()
|
|
18
|
+
... def recv(self, timeout=None):
|
|
19
|
+
... msg = self._consumer.poll(timeout_ms=int((timeout or 1) * 1000))
|
|
20
|
+
... for tp, msgs in msg.items():
|
|
21
|
+
... for m in msgs:
|
|
22
|
+
... return m.value, m.key
|
|
23
|
+
... return None
|
|
24
|
+
... def close(self):
|
|
25
|
+
... self._producer.close()
|
|
26
|
+
... self._consumer.close()
|
|
27
|
+
...
|
|
28
|
+
>>> with KafkaQueue(task_id="task1", bootstrap_servers="localhost:9092",
|
|
29
|
+
... topic="test", group_id="test-group") as queue:
|
|
30
|
+
... queue.send(b"hello", key=b"k1")
|
|
31
|
+
... msg = queue.recv(timeout=5)
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from abc import ABC, abstractmethod
|
|
35
|
+
from logging import Logger
|
|
36
|
+
|
|
37
|
+
from .log import getLogger
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class QueueBase(ABC):
|
|
41
|
+
"""
|
|
42
|
+
队列操作抽象基类。
|
|
43
|
+
|
|
44
|
+
定义队列操作的标准接口,包括发送消息、接收消息和关闭连接等方法。
|
|
45
|
+
使用抽象基类定义统一的队列操作规范。
|
|
46
|
+
|
|
47
|
+
继承此类需要实现以下抽象方法:
|
|
48
|
+
- post_init: 初始化具体队列连接对象
|
|
49
|
+
- send: 发送消息
|
|
50
|
+
- recv: 接收消息
|
|
51
|
+
- close: 关闭连接
|
|
52
|
+
|
|
53
|
+
可以作为上下文管理器使用,会自动在退出时关闭连接。
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
logger: 日志记录器,用于记录队列操作日志。
|
|
57
|
+
|
|
58
|
+
Example:
|
|
59
|
+
>>> class MyQueue(QueueBase):
|
|
60
|
+
... def post_init(self):
|
|
61
|
+
... pass # 初始化具体连接
|
|
62
|
+
... def send(self, value, key=None):
|
|
63
|
+
... pass
|
|
64
|
+
... def recv(self, timeout=None):
|
|
65
|
+
... return None
|
|
66
|
+
... def close(self):
|
|
67
|
+
... pass
|
|
68
|
+
...
|
|
69
|
+
>>> with MyQueue(task_id="t1", bootstrap_servers="localhost:9092",
|
|
70
|
+
... topic="test") as queue:
|
|
71
|
+
... queue.send(b"hello")
|
|
72
|
+
... msg = queue.recv(timeout=5)
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
def __init__(
|
|
76
|
+
self,
|
|
77
|
+
bootstrap_servers: str,
|
|
78
|
+
name: str | None = None,
|
|
79
|
+
):
|
|
80
|
+
"""
|
|
81
|
+
初始化队列基类。
|
|
82
|
+
|
|
83
|
+
将传入的连接参数保存为实例属性,并初始化日志记录器。
|
|
84
|
+
子类应在 post_init 方法中创建实际的队列连接。
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
bootstrap_servers: 消息队列服务器地址。
|
|
88
|
+
name: 名称。
|
|
89
|
+
"""
|
|
90
|
+
self._bootstrap_servers = bootstrap_servers
|
|
91
|
+
|
|
92
|
+
self._name = name or "queue_unknown"
|
|
93
|
+
|
|
94
|
+
self._logger = getLogger(name)
|
|
95
|
+
|
|
96
|
+
self.post_init()
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def post_init(self):
|
|
100
|
+
"""
|
|
101
|
+
初始化具体队列连接对象。
|
|
102
|
+
|
|
103
|
+
子类应在此方法中创建并返回实际的队列连接对象。
|
|
104
|
+
该方法在 __init__ 中被调用,用于延迟初始化连接。
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
None
|
|
108
|
+
"""
|
|
109
|
+
pass
|
|
110
|
+
|
|
111
|
+
@abstractmethod
|
|
112
|
+
def send(self, topic: str, value: bytes | str, key: bytes | str | None = None):
|
|
113
|
+
"""
|
|
114
|
+
发送消息到队列。
|
|
115
|
+
|
|
116
|
+
将消息发送到指定的主题。
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
topic: 消息主题。
|
|
120
|
+
value: 消息内容,支持字节串或字符串。
|
|
121
|
+
key: 消息键,用于分区路由,可选。
|
|
122
|
+
"""
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
@abstractmethod
|
|
126
|
+
def recv(
|
|
127
|
+
self, topic: str, group_id: str | None = None, timeout: float | None = None
|
|
128
|
+
) -> tuple[bytes | str, bytes | str] | None:
|
|
129
|
+
"""
|
|
130
|
+
从队列接收消息。
|
|
131
|
+
|
|
132
|
+
从指定主题消费消息。
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
topic: 消息主题。
|
|
136
|
+
group_id: 消费者组 ID,可选;为 None 时使用初始化时的 group_id。
|
|
137
|
+
timeout: 等待超时时间(秒),None 表示阻塞等待。
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
tuple[bytes | str, bytes | str] | None: 消息的 (value, key) 元组,无消息时返回 None。
|
|
141
|
+
"""
|
|
142
|
+
pass
|
|
143
|
+
|
|
144
|
+
@abstractmethod
|
|
145
|
+
def close(self):
|
|
146
|
+
"""
|
|
147
|
+
关闭队列连接。
|
|
148
|
+
|
|
149
|
+
释放队列连接资源,应在不再需要连接时调用。
|
|
150
|
+
支持多次调用,子类应实现幂等性。
|
|
151
|
+
"""
|
|
152
|
+
pass
|
|
153
|
+
|
|
154
|
+
@property
|
|
155
|
+
def name(self) -> str:
|
|
156
|
+
"""
|
|
157
|
+
队列连接名称属性。
|
|
158
|
+
|
|
159
|
+
返回连接的名称,用于标识连接。
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
str: 队列连接名称。
|
|
163
|
+
"""
|
|
164
|
+
return self._name
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def logger(self) -> Logger:
|
|
168
|
+
"""
|
|
169
|
+
日志记录器属性。
|
|
170
|
+
|
|
171
|
+
提供对内部日志记录器的只读访问,用于记录队列操作日志。
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Logger: Python logging 模块的日志记录器实例。
|
|
175
|
+
"""
|
|
176
|
+
return self._logger
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def bootstrap_servers(self) -> str:
|
|
180
|
+
"""
|
|
181
|
+
服务器地址属性。
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
str: 队列服务器地址。
|
|
185
|
+
"""
|
|
186
|
+
return self._bootstrap_servers
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def __enter__(self):
|
|
190
|
+
"""
|
|
191
|
+
上下文管理器入口方法。
|
|
192
|
+
|
|
193
|
+
使 QueueBase 的子类可以作为 with 语句使用。
|
|
194
|
+
进入 with 块时返回 self 引用。
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
self: 返回当前实例的引用。
|
|
198
|
+
"""
|
|
199
|
+
return self
|
|
200
|
+
|
|
201
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
202
|
+
"""
|
|
203
|
+
上下文管理器退出方法。
|
|
204
|
+
|
|
205
|
+
退出 with 块时自动调用,异常会被记录后关闭连接。
|
|
206
|
+
确保队列连接被正确关闭。
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
exc_type: 异常类型,若无异常则为 None。
|
|
210
|
+
exc_val: 异常值/实例,若无异常则为 None。
|
|
211
|
+
exc_tb: 异常回溯对象,若无异常则为 None。
|
|
212
|
+
"""
|
|
213
|
+
if exc_type:
|
|
214
|
+
self._logger.exception(exc_val)
|
|
215
|
+
self.close()
|
|
216
|
+
|
|
217
|
+
def __del__(self):
|
|
218
|
+
"""
|
|
219
|
+
析构函数。
|
|
220
|
+
|
|
221
|
+
当对象被垃圾回收时自动调用,确保连接被关闭。
|
|
222
|
+
这是一个安全网机制,不能替代显式调用 close()。
|
|
223
|
+
"""
|
|
224
|
+
self.close()
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"""
|
|
2
|
+
基于 Kafka 的消息队列实现。
|
|
3
|
+
|
|
4
|
+
继承自 QueueBase,提供统一的队列操作接口。
|
|
5
|
+
使用 kafka-python 作为底层实现,支持消息的可靠发送和消费者组模式。
|
|
6
|
+
|
|
7
|
+
Classes:
|
|
8
|
+
KafkaQueue: 基于 kafka-python 的 Kafka 消息队列类
|
|
9
|
+
|
|
10
|
+
Example:
|
|
11
|
+
>>> with KafkaQueue(task_id="t1", bootstrap_servers="localhost:9092",
|
|
12
|
+
... topic="test", group_id="test-group") as queue:
|
|
13
|
+
... queue.send(b"hello", key=b"k1")
|
|
14
|
+
... msg = queue.recv(timeout=5)
|
|
15
|
+
... if msg:
|
|
16
|
+
... value, key = msg
|
|
17
|
+
... print(f"Received: {value}")
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from kafka import KafkaConsumer, KafkaProducer
|
|
21
|
+
from kafka.errors import KafkaError
|
|
22
|
+
|
|
23
|
+
from .queue_base import QueueBase
|
|
24
|
+
|
|
25
|
+
__all__ = ["KafkaQueue"]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class KafkaQueue(QueueBase):
|
|
29
|
+
"""
|
|
30
|
+
基于 kafka-python 的 Kafka 消息队列类。
|
|
31
|
+
|
|
32
|
+
支持:
|
|
33
|
+
- 可靠发送消息(等待 broker 确认)
|
|
34
|
+
- 消费者组模式(多实例自动负载均衡)
|
|
35
|
+
- 上下文管理器(自动关闭连接)
|
|
36
|
+
|
|
37
|
+
继承自 QueueBase,实现所有抽象方法。
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
producer: KafkaProducer 实例。
|
|
41
|
+
logger: 日志记录器。
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> with KafkaQueue(task_id="t1", bootstrap_servers="localhost:9092",
|
|
45
|
+
... group_id="test-group") as queue:
|
|
46
|
+
... # 发送消息
|
|
47
|
+
... queue.send("test-topic", b"hello world", key=b"greeting")
|
|
48
|
+
... # 接收消息
|
|
49
|
+
... msg = queue.recv(timeout=5.0)
|
|
50
|
+
... if msg:
|
|
51
|
+
... value, key = msg
|
|
52
|
+
... print(f"key={key}, value={value}")
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
def post_init(self):
|
|
56
|
+
"""
|
|
57
|
+
初始化 KafkaProducer。
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
None
|
|
61
|
+
"""
|
|
62
|
+
self.logger.info(f"Creating Kafka producer: bootstrap_servers={self._bootstrap_servers}")
|
|
63
|
+
|
|
64
|
+
self._producer: KafkaProducer = KafkaProducer(
|
|
65
|
+
bootstrap_servers=self._bootstrap_servers.split(","),
|
|
66
|
+
acks=1,
|
|
67
|
+
retries=3,
|
|
68
|
+
max_in_flight_requests_per_connection=1,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
def send(self, topic: str, value: bytes | str, key: bytes | str | None = None):
|
|
72
|
+
"""
|
|
73
|
+
发送消息到 Kafka 主题。
|
|
74
|
+
|
|
75
|
+
使用同步发送模式,等待 broker 确认消息已写入。
|
|
76
|
+
|
|
77
|
+
Args:
|
|
78
|
+
topic: 消息主题。
|
|
79
|
+
value: 消息内容,支持字节串或字符串。
|
|
80
|
+
key: 消息键,用于分区路由,可选。
|
|
81
|
+
"""
|
|
82
|
+
try:
|
|
83
|
+
if isinstance(value, str):
|
|
84
|
+
value = value.encode("utf-8")
|
|
85
|
+
if isinstance(key, str):
|
|
86
|
+
key = key.encode("utf-8")
|
|
87
|
+
|
|
88
|
+
future = self._producer.send(topic, value=value, key=key)
|
|
89
|
+
record_metadata = future.get(timeout=10)
|
|
90
|
+
|
|
91
|
+
self.logger.info(
|
|
92
|
+
f"Message sent to {record_metadata.topic}:{record_metadata.partition}:"
|
|
93
|
+
f"{record_metadata.offset}"
|
|
94
|
+
)
|
|
95
|
+
except KafkaError as e:
|
|
96
|
+
self.logger.error(f"Failed to send message: {e}")
|
|
97
|
+
raise
|
|
98
|
+
|
|
99
|
+
def recv(
|
|
100
|
+
self, topic: str, group_id: str | None = None, timeout: float | None = None
|
|
101
|
+
) -> tuple[bytes, bytes] | None:
|
|
102
|
+
"""
|
|
103
|
+
从 Kafka 主题接收消息。
|
|
104
|
+
|
|
105
|
+
使用消费者组模式,支持多实例自动负载均衡。
|
|
106
|
+
|
|
107
|
+
Args:
|
|
108
|
+
topic: 消息主题。
|
|
109
|
+
group_id: 消费者组 ID,可选;为 None 时使用初始化时的 group_id。
|
|
110
|
+
timeout: 等待超时时间(秒),None 表示使用默认超时(1秒)。
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
tuple[bytes, bytes] | None: 消息的 (value, key) 元组,无消息时返回 None。
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
with KafkaConsumer(
|
|
117
|
+
topic,
|
|
118
|
+
bootstrap_servers=self._bootstrap_servers.split(","),
|
|
119
|
+
group_id=group_id,
|
|
120
|
+
auto_offset_reset="earliest",
|
|
121
|
+
enable_auto_commit=True,
|
|
122
|
+
) as consumer:
|
|
123
|
+
msg = consumer.poll(timeout_ms=int((timeout or 1.0) * 1000))
|
|
124
|
+
|
|
125
|
+
for _, messages in msg.items():
|
|
126
|
+
for m in messages:
|
|
127
|
+
return m.value, m.key
|
|
128
|
+
|
|
129
|
+
return None
|
|
130
|
+
except Exception as e:
|
|
131
|
+
self.logger.error(f"Failed to receive message: {e}")
|
|
132
|
+
return None
|
|
133
|
+
|
|
134
|
+
def close(self):
|
|
135
|
+
"""
|
|
136
|
+
关闭 Kafka 连接。
|
|
137
|
+
|
|
138
|
+
关闭 Producer,释放资源。
|
|
139
|
+
支持多次调用,子类应实现幂等性。
|
|
140
|
+
"""
|
|
141
|
+
if hasattr(self, "_producer") and self._producer is not None:
|
|
142
|
+
try:
|
|
143
|
+
self._producer.flush()
|
|
144
|
+
self._producer.close()
|
|
145
|
+
self.logger.info("Producer closed")
|
|
146
|
+
except Exception as e:
|
|
147
|
+
self.logger.error(f"Failed to close producer: {e}")
|
|
148
|
+
self._producer = None
|
|
149
|
+
|
|
150
|
+
self.logger.info("closed")
|