misakadb 0.1.1__tar.gz

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.
misakadb-0.1.1/LICENSE ADDED
@@ -0,0 +1,132 @@
1
+ 木兰宽松许可证, 第2版
2
+
3
+ 木兰宽松许可证, 第2版
4
+
5
+ 2020年1月 http://license.coscl.org.cn/MulanPSL2
6
+
7
+ 您对“软件”的复制、使用、修改及分发受木兰宽松许可证,第2版(“本许可证”)的如下条款的约束:
8
+
9
+ 0. 定义
10
+
11
+ “软件” 是指由“贡献”构成的许可在“本许可证”下的程序和相关文档的集合。
12
+
13
+ “贡献” 是指由任一“贡献者”许可在“本许可证”下的受版权法保护的作品。
14
+
15
+ “贡献者” 是指将受版权法保护的作品许可在“本许可证”下的自然人或“法人实体”。
16
+
17
+ “法人实体” 是指提交贡献的机构及其“关联实体”。
18
+
19
+ “关联实体” 是指,对“本许可证”下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
20
+
21
+ 1. 授予版权许可
22
+
23
+ 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、分发其“贡献”,不论修改与否。
24
+
25
+ 2. 授予专利许可
26
+
27
+ 每个“贡献者”根据“本许可证”授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其“贡献”或以其他方式转移其“贡献”。前述专利许可仅限于“贡献者”现在或将来拥有或控制的其“贡献”本身或其“贡献”与许可“贡献”时的“软件”结合而将必然会侵犯的专利权利要求,不包括对“贡献”的修改或包含“贡献”的其他结合。如果您或您的“关联实体”直接或间接地,就“软件”或其中的“贡献”对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则“本许可证”授予您对“软件”的专利许可自您提起诉讼或发起维权行动之日终止。
28
+
29
+ 3. 无商标许可
30
+
31
+ “本许可证”不提供对“贡献者”的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
32
+
33
+ 4. 分发限制
34
+
35
+ 您可以在任何媒介中将“软件”以源程序形式或可执行形式重新分发,不论修改与否,但您必须向接收者提供“本许可证”的副本,并保留“软件”中的版权、商标、专利及免责声明。
36
+
37
+ 5. 免责声明与责任限制
38
+
39
+ “软件”及其中的“贡献”在提供时不带任何明示或默示的担保。在任何情况下,“贡献者”或版权所有者不对任何人因使用“软件”或其中的“贡献”而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
40
+
41
+ 6. 语言
42
+
43
+ “本许可证”以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
44
+
45
+ 条款结束
46
+
47
+ 如何将木兰宽松许可证,第2版,应用到您的软件
48
+
49
+ 如果您希望将木兰宽松许可证,第2版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
50
+
51
+ 1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
52
+
53
+ 2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
54
+
55
+ 3, 请将如下声明文本放入每个源文件的头部注释中。
56
+
57
+ Copyright (c) [Year] [name of copyright holder]
58
+ [Software Name] is licensed under Mulan PSL v2.
59
+ You can use this software according to the terms and conditions of the Mulan PSL v2.
60
+ You may obtain a copy of Mulan PSL v2 at:
61
+ http://license.coscl.org.cn/MulanPSL2
62
+ THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
63
+ EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
64
+ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
65
+ See the Mulan PSL v2 for more details.
66
+
67
+
68
+ Mulan Permissive Software License,Version 2
69
+
70
+ Mulan Permissive Software License,Version 2 (Mulan PSL v2)
71
+
72
+ January 2020 http://license.coscl.org.cn/MulanPSL2
73
+
74
+ Your reproduction, use, modification and distribution of the Software shall be subject to Mulan PSL v2 (this License) with the following terms and conditions:
75
+
76
+ 0. Definition
77
+
78
+ Software means the program and related documents which are licensed under this License and comprise all Contribution(s).
79
+
80
+ Contribution means the copyrightable work licensed by a particular Contributor under this License.
81
+
82
+ Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
83
+
84
+ Legal Entity means the entity making a Contribution and all its Affiliates.
85
+
86
+ Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
87
+
88
+ 1. Grant of Copyright License
89
+
90
+ Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or distribute its Contribution, with modification or not.
91
+
92
+ 2. Grant of Patent License
93
+
94
+ Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, or by combination of the Contribution with the Software to which the Contribution was contributed. The patent license shall not apply to any modification of the Contribution, and any other combination which includes the Contribution. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that the Software or any Contribution in it infringes patents, then any patent license granted to you under this License for the Software shall terminate as of the date such litigation or activity is filed or taken.
95
+
96
+ 3. No Trademark License
97
+
98
+ No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in section 4.
99
+
100
+ 4. Distribution Restriction
101
+
102
+ You may distribute the Software in any medium with or without modification, whether in source or executable forms, provided that you provide recipients with a copy of this License and retain copyright, patent, trademark and disclaimer statements in the Software.
103
+
104
+ 5. Disclaimer of Warranty and Limitation of Liability
105
+
106
+ THE SOFTWARE AND CONTRIBUTION IN IT ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE SOFTWARE OR THE CONTRIBUTION IN IT, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
107
+
108
+ 6. Language
109
+
110
+ THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
111
+
112
+ END OF THE TERMS AND CONDITIONS
113
+
114
+ How to Apply the Mulan Permissive Software License,Version 2 (Mulan PSL v2) to Your Software
115
+
116
+ To apply the Mulan PSL v2 to your work, for easy identification by recipients, you are suggested to complete following three steps:
117
+
118
+ Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
119
+
120
+ Create a file named "LICENSE" which contains the whole context of this License in the first directory of your software package;
121
+
122
+ Attach the statement to the appropriate annotated syntax at the beginning of each source file.
123
+
124
+ Copyright (c) [Year] [name of copyright holder]
125
+ [Software Name] is licensed under Mulan PSL v2.
126
+ You can use this software according to the terms and conditions of the Mulan PSL v2.
127
+ You may obtain a copy of Mulan PSL v2 at:
128
+ http://license.coscl.org.cn/MulanPSL2
129
+ THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
130
+ EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
131
+ MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
132
+ See the Mulan PSL v2 for more details.
@@ -0,0 +1,441 @@
1
+ Metadata-Version: 2.4
2
+ Name: misakadb
3
+ Version: 0.1.1
4
+ Summary: MisakaDB Python client, MiQL query builder and command-line interface
5
+ License-Expression: MulanPSL-2.0
6
+ Project-URL: Repository, https://gitee.com/hello_kuma/misakadb
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ # MisakaDB Python Client
13
+
14
+ 这是 MisakaDB 的 Python 客户端文档,主要面向客户端调用方。
15
+
16
+ 包名和导入名均为 `misakadb`,需要 Python 3.11 或更高版本。直接入口为 `conn()` 和 `getCurrent()`;同时保留 `MisakaDBClient`、`MiQL` 等原有接口。客户端只依赖 Python 标准库。
17
+
18
+ ## 功能概览
19
+
20
+ - 连接 MisakaDB 服务端
21
+ - 提供身份验证 (Login) 与安全拦截
22
+ - 获取服务信息与动态面板展示
23
+ - 执行文本命令
24
+ - 查询服务版本、网络配置、允许命令
25
+ - 自动发送心跳包
26
+ - 手动启动、停止心跳
27
+ - 查询心跳次数、失败次数、丢包率
28
+
29
+ ## 目录
30
+
31
+ - 客户端 API: `misakadb/apis/api.py`
32
+ - 命令行入口: `misakadb/cli.py`
33
+ - 底层 socket 实现: `misakadb/network/sock.py`
34
+ - 命令格式化输出: `misakadb/network/command_send.py`
35
+ - 压测示例: `usage/demo1.py`
36
+
37
+ ## 构建与安装
38
+
39
+ 从 PyPI 安装客户端:
40
+
41
+ ```bash
42
+ python -m pip install misakadb
43
+ ```
44
+
45
+ 在仓库根目录安装源码包:
46
+
47
+ ```bash
48
+ python -m pip install ./client
49
+ ```
50
+
51
+ 构建 wheel 和源码分发包(构建工具使用仓库已有的 uv):
52
+
53
+ ```bash
54
+ uv build client --out-dir client/dist/packages
55
+ python -m pip install client/dist/packages/misakadb-0.1.1-py3-none-any.whl
56
+ ```
57
+
58
+ 已有 wheel 时可直接拷贝到其他机器,用 `python -m pip install <wheel路径>` 安装,无需访问包索引。发布目标为 [PyPI 的 misakadb 项目](https://pypi.org/project/misakadb/),公开发布成功后可直接通过包名安装。
59
+
60
+ 安装后可运行:
61
+
62
+ ```bash
63
+ misakadb --version
64
+ misakadb --address 127.0.0.1 --port 10032 --mode shell
65
+ # 等价入口
66
+ python -m misakadb --address 127.0.0.1 --port 10032 --mode shell
67
+ ```
68
+
69
+ 使用虚拟环境时先激活环境;项目现有环境可用 `source client/.venv/bin/activate`。
70
+
71
+ ## 直接使用 API
72
+
73
+ ```python
74
+ import misakadb as misaka
75
+
76
+ conn = misaka.conn(ip="127.0.0.1", port=10032)
77
+ # 如果服务端启用了鉴权:
78
+ conn.login("your_user", "your_password")
79
+ mq = misaka.getCurrent()
80
+
81
+ # 数据库 test 和表 people 需要已经存在。
82
+ result = mq.use("test").table("people").join({"id": 1, "name": "御坂", "age": 20})
83
+ print(result) # {"id": 1, "affected": 1}
84
+ conn.close()
85
+ ```
86
+
87
+ `conn()` 立即建立连接,也可以通过 `username=...、password=...` 在连接后自动登录。它返回连接对象并设置当前上下文的连接。无需创建 `MiQL`,无需调用 `.shot()`,操作执行后直接返回 Python 字典。`join()` 不传数据时会插入一个只有自动生成 id 的文档。
88
+
89
+ 创建数据库、创建表及完整 CRUD:
90
+
91
+ ```python
92
+ import misakadb as misaka
93
+
94
+ with misaka.conn(ip="127.0.0.1", port=10032,
95
+ username="your_user", password="your_password") as conn:
96
+ mq = misaka.getCurrent()
97
+ mq.createDB("example")
98
+ db = mq.use("example")
99
+ db.createTable("people")
100
+ table = db.table("people")
101
+
102
+ table.join({"id": 1, "age": 20})
103
+ rows = table.show(where={"id": 1})["data"]
104
+ table.show(magic=["+age", "$age>18"], limit=100, offset=0)
105
+ table.update({"age": 21}, where={"id": 1})
106
+ table.remove(where={"id": 1})
107
+ ```
108
+
109
+ - `mq.use(database)` 选择数据库并返回数据库作用域,`.table(name)` 选择表并返回表作用域。两者仅在服务端确认成功后更新该连接的 session。
110
+ - 选择后可以直接调用 `mq.join(...)`、`mq.show(...)`、`mq.update(...)`、`mq.remove(...)`,使用 session 的当前表。
111
+ - 保存的 `table` 对象绑定原来的连接、数据库和表。之后切换当前选择不会改变该对象的目标,重复操作无需重复执行 `use/table`。
112
+ - `show(where=None, *, magic=None, limit=None, offset=0)` 立即查询。`magic` 可以是单个字符串或字符串列表,`where` 与 `magic` 同时提供时取 AND。
113
+ - `update(document, *, where=None, magic=None, all=False)` 立即按补丁更新;`remove(where=None, *, magic=None, all=False)` 立即删除。全表修改必须显式使用 `all=True`。
114
+ - `join()` 返回 id 和 affected;其他 CRUD 返回包含 data、matched、affected、revision、total 和 next_offset 的字典,`show()["data"]` 取得记录列表。
115
+ - `getCurrent()` 的当前连接指针按线程/协程上下文保存;新线程应单独调用 `conn()`。继承或显式共享同一连接时,共用该连接的 session 和请求锁。已保存的 `conn.getCurrent()` 可随时取得该连接的 API。
116
+ - 未连接或连接已关闭时抛出 `ConnectionError`,服务端拒绝操作时抛出 `misaka.MisakaError`,非法 API 参数抛出 `ValueError`。新连接或登录失败不会替换已有的当前连接。
117
+
118
+ 需要原来的延迟构造语法时,可以用 `table.query().show().where({"id": 1}).shot()`;它仍返回原始 bytes。
119
+
120
+ ## 原有 MiQL 构造接口
121
+
122
+ ```python
123
+ from misakadb import MisakaDBClient, MiQL, commandSend
124
+
125
+ with MisakaDBClient("127.0.0.1", 10032) as client:
126
+ # 使用服务端已有的用户名与密码。
127
+ if not commandSend(client.client).login("your_user", "your_password"):
128
+ raise PermissionError("登录失败")
129
+ mq = MiQL(client.client)
130
+ print(mq.useDB("test").shot().decode())
131
+ print(mq.createTable("people").shot().decode())
132
+ print(mq.join({"id": 1, "name": "御坂", "age": 20}).shot().decode())
133
+ print(mq.show().magic("$age>18").shot().decode())
134
+ print(mq.update().where({"id": 1}).new({"age": 21}).shot().decode())
135
+ print(mq.remove().where({"id": 1}).shot().decode())
136
+ ```
137
+
138
+ 示例要求数据库 `test` 已存在且表 `people` 尚未创建。已有表可用 `mq.use("people").shot()` 选择。原生 Python SDK 的字典键需要引号;Shell 字符串解析器允许不带引号的键。查询返回值保持现有的 bytes 协议格式,`.shot()` 执行请求,成功后的数据库和表选择保存在该连接的 session 中。
139
+
140
+ 仓库中原有的 `from apis.api import MisakaDBClient`、`from mql.MQ import MiQL` 和 `python client/misaka-cli.py` 继续通过兼容入口使用同一份实现。安装包本身只提供 `misakadb` 命名空间。
141
+
142
+ ## 快速开始
143
+
144
+ ### 1. 创建客户端
145
+
146
+ ```python
147
+ from misakadb import MisakaDBClient
148
+
149
+ client = MisakaDBClient("127.0.0.1", 10032)
150
+ ```
151
+
152
+ 默认参数:
153
+
154
+ - `host="127.0.0.1"`
155
+ - `port=10032`
156
+ - `heartbeat_interval=1.0`
157
+
158
+ ### 2. 连接服务端与鉴权
159
+
160
+ ```python
161
+ ok = client.connect()
162
+ if not ok:
163
+ print("连接失败")
164
+
165
+ # 如果服务端开启了鉴权,需要发送 login 命令
166
+ # 在交互模式下,你可以使用 command_send 模块:
167
+ # command_send.login(username, password)
168
+ ```
169
+
170
+ 也可以指定重试参数:
171
+
172
+ ```python
173
+ ok = client.connect(retries=3, retry_delay=1.0)
174
+ ```
175
+
176
+ ### 3. 获取服务信息
177
+
178
+ ```python
179
+ service_info = client.get_service_info()
180
+ print(service_info)
181
+ ```
182
+
183
+ 典型返回值:
184
+
185
+ ```python
186
+ {
187
+ "network": {
188
+ "port": 10032,
189
+ "address": "0.0.0.0",
190
+ "max_conn": 2,
191
+ "retry_count": 3,
192
+ "retry_delay": 30
193
+ }
194
+ }
195
+ ```
196
+
197
+ ### 4. 关闭连接
198
+
199
+ ```python
200
+ client.close()
201
+ ```
202
+
203
+ ## 推荐写法
204
+
205
+ 推荐使用上下文管理器,避免忘记关闭连接:
206
+
207
+ ```python
208
+ from misakadb import MisakaDBClient
209
+
210
+ with MisakaDBClient("127.0.0.1", 10032) as client:
211
+ info = client.get_service_info()
212
+ print(info)
213
+ ```
214
+
215
+ ## API 说明
216
+
217
+ ### `MisakaDBClient(host="127.0.0.1", port=10032, heartbeat_interval=1.0)`
218
+
219
+ 创建客户端对象。
220
+
221
+ 参数说明:
222
+
223
+ - `host`: 服务端地址
224
+ - `port`: 服务端端口
225
+ - `heartbeat_interval`: 心跳发送间隔,单位秒
226
+
227
+ 注意:
228
+
229
+ - 初始化时会创建 `client.heart` 心跳控制器
230
+ - 初始化时会启动心跳线程
231
+ - 真正发送心跳前仍要求连接已建立
232
+
233
+ ### `connect(retries=3, retry_delay=1.0) -> bool`
234
+
235
+ 连接到服务端。
236
+
237
+ 参数说明:
238
+
239
+ - `retries`: 失败重试次数
240
+ - `retry_delay`: 重试间隔,单位秒
241
+
242
+ 返回值:
243
+
244
+ - `True`: 连接成功
245
+ - `False`: 连接失败
246
+
247
+ *注:此方法仅负责底层 TCP 连接。如果需要身份验证,请在连接成功后调用 `execute_command("login <user> <pass>")` 或使用 `commandSend.login()`。*
248
+
249
+ ### `close() -> None`
250
+
251
+ 关闭当前连接,并停止当前心跳控制器。
252
+
253
+ ### `get_service_info() -> dict | None`
254
+
255
+ 获取服务端信息。
256
+
257
+ 成功时返回字典,失败时返回 `None`。
258
+
259
+ ### `execute_command(command: str) -> str | dict | list | None`
260
+
261
+ 发送一条文本命令到服务端。
262
+
263
+ 行为说明:
264
+
265
+ - 如果返回内容是 JSON,会自动解析成 `dict` 或 `list`
266
+ - 如果返回内容不是 JSON,会返回字符串
267
+ - 如果请求失败,返回 `None`
268
+
269
+ 示例:
270
+
271
+ ```python
272
+ result = client.execute_command("get-service-info")
273
+ print(result)
274
+ ```
275
+
276
+ ### `ping() -> bool`
277
+
278
+ 用于检查当前服务是否可达。
279
+
280
+ 返回值:
281
+
282
+ - `True`: 当前请求成功
283
+ - `False`: 当前请求失败
284
+
285
+ 注意:
286
+
287
+ - 这里是业务层命令检测
288
+ - 不是底层 TCP keepalive
289
+ - 也不是服务端心跳回包确认
290
+
291
+ ### `get_server_version() -> str | None`
292
+
293
+ 获取服务端版本号。
294
+
295
+ ### `get_allowed_commands() -> list[str] | None`
296
+
297
+ 获取服务端允许的命令列表。
298
+
299
+ ### `is_command_allowed(command: str) -> bool`
300
+
301
+ 检查某条命令是否被服务端允许。
302
+
303
+ ### `get_network_config() -> dict | None`
304
+
305
+ 获取服务端网络配置。
306
+
307
+ ## 心跳控制
308
+
309
+ 客户端暴露了一个心跳控制对象:
310
+
311
+ ```python
312
+ client.heart
313
+ ```
314
+
315
+ ### 自动行为
316
+
317
+ - `MisakaDBClient` 初始化时会启动心跳线程
318
+ - `connect()` 时会确保心跳处于启动状态
319
+ - 已连接状态下,心跳线程会定时发送心跳帧
320
+
321
+ ### 手动启动和停止
322
+
323
+ ```python
324
+ client.heart.start()
325
+ client.heart.stop()
326
+ ```
327
+
328
+ 说明:
329
+
330
+ - `start()` 启动或恢复心跳发送
331
+ - `stop()` 停止发送心跳,但不会关闭连接
332
+
333
+ ### 心跳统计字段
334
+
335
+ ```python
336
+ client.heart.count
337
+ client.heart.success_count
338
+ client.heart.failure_count
339
+ client.heart.loss_rate
340
+ client.heart.running
341
+ client.heart.last_error
342
+ client.heart.last_sent_at
343
+ ```
344
+
345
+ 字段说明:
346
+
347
+ - `count`: 心跳发送尝试总次数
348
+ - `success_count`: 心跳发送成功次数
349
+ - `failure_count`: 心跳发送失败次数
350
+ - `loss_rate`: 当前统计口径下的失败率,单位百分比
351
+ - `running`: 当前是否处于启用状态
352
+ - `last_error`: 最近一次失败错误
353
+ - `last_sent_at`: 最近一次成功发送心跳的时间戳
354
+
355
+ ### 获取完整统计
356
+
357
+ ```python
358
+ stats = client.heart.stats()
359
+ print(stats)
360
+ ```
361
+
362
+ 返回示例:
363
+
364
+ ```python
365
+ {
366
+ "running": True,
367
+ "interval": 10.0,
368
+ "count": 12,
369
+ "success_count": 12,
370
+ "failure_count": 0,
371
+ "loss_rate": 0.0,
372
+ "last_error": None,
373
+ "last_sent_at": 1710000000.123
374
+ }
375
+ ```
376
+
377
+ ### 关于“丢包率”的说明
378
+
379
+ 当前协议没有单独的心跳响应包,因此这里的“丢包率”并不是网络层真实丢包率,而是:
380
+
381
+ ```text
382
+ 心跳发送失败次数 / 心跳发送尝试总次数
383
+ ```
384
+
385
+ 如果后续服务端增加了心跳应答包,这里的统计口径可以再升级为真正的收发确认率。
386
+
387
+ ## 命令行交互模式 (CLI)
388
+
389
+ 客户端提供了一个功能丰富的交互式终端入口 (`misakadb` 命令),支持以下特性:
390
+
391
+ - **密码回显关闭**:安全的密码输入体验(与 Linux 终端一致)
392
+ - **华丽面板**:针对 `get-service-info` 等特殊命令提供了对齐的 JSON 展平和 Misaka ASCII Logo 渲染
393
+ - **自动耗时统计**:所有命令自动显示执行延迟 (绿色 < 0.2ms,黄色 >= 0.2ms)
394
+ - **多行输入**:支持以 `\` 结尾进行多行命令拼接
395
+
396
+ ### 启动 CLI
397
+
398
+ ```bash
399
+ misakadb --address 127.0.0.1 --port 10032 --mode shell
400
+ ```
401
+
402
+ **可选参数**:
403
+ - `--username <name>`: 预设用户名
404
+ - `--password <pass>`: 预设密码
405
+ - `--mode onlyConn`: 仅建立连接测试,跳过交互式终端 (适合作为 Windows 后台服务启动)
406
+
407
+ ## 完整示例
408
+
409
+ ```python
410
+ from misakadb import MisakaDBClient
411
+
412
+ with MisakaDBClient("127.0.0.1", 10032, heartbeat_interval=5.0) as client:
413
+ info = client.get_service_info()
414
+ print("服务信息:", info)
415
+
416
+ print("服务版本:", client.get_server_version())
417
+ print("网络配置:", client.get_network_config())
418
+ print("允许命令:", client.get_allowed_commands())
419
+
420
+ print("心跳是否运行:", client.heart.running)
421
+ print("心跳次数:", client.heart.count)
422
+ print("心跳失败率:", client.heart.loss_rate)
423
+
424
+ client.heart.stop()
425
+ print("手动停止心跳:", client.heart.running)
426
+
427
+ client.heart.start()
428
+ print("重新启动心跳:", client.heart.running)
429
+
430
+ result = client.execute_command("get-service-info")
431
+ print("命令结果:", result)
432
+ ```
433
+
434
+
435
+
436
+ ## 注意事项
437
+
438
+ - `execute_command()` 和心跳发送共用同一个 socket,并通过锁保证不会交叉写入
439
+ - 当前客户端是长连接模型,建议复用连接,不要高频短连接压测
440
+ - 如果你要做压测,优先参考 `usage/demo1.py` 的“单连接多请求”模式
441
+ - 如果服务端返回的内容不是合法 JSON,客户端会自动退回为字符串返回