toms-fast 0.2.1__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.
Files changed (60) hide show
  1. toms_fast-0.2.1.dist-info/METADATA +467 -0
  2. toms_fast-0.2.1.dist-info/RECORD +60 -0
  3. toms_fast-0.2.1.dist-info/WHEEL +4 -0
  4. toms_fast-0.2.1.dist-info/entry_points.txt +2 -0
  5. tomskit/__init__.py +0 -0
  6. tomskit/celery/README.md +693 -0
  7. tomskit/celery/__init__.py +4 -0
  8. tomskit/celery/celery.py +306 -0
  9. tomskit/celery/config.py +377 -0
  10. tomskit/cli/__init__.py +207 -0
  11. tomskit/cli/__main__.py +8 -0
  12. tomskit/cli/scaffold.py +123 -0
  13. tomskit/cli/templates/__init__.py +42 -0
  14. tomskit/cli/templates/base.py +348 -0
  15. tomskit/cli/templates/celery.py +101 -0
  16. tomskit/cli/templates/extensions.py +213 -0
  17. tomskit/cli/templates/fastapi.py +400 -0
  18. tomskit/cli/templates/migrations.py +281 -0
  19. tomskit/cli/templates_config.py +122 -0
  20. tomskit/logger/README.md +466 -0
  21. tomskit/logger/__init__.py +4 -0
  22. tomskit/logger/config.py +106 -0
  23. tomskit/logger/logger.py +290 -0
  24. tomskit/py.typed +0 -0
  25. tomskit/redis/README.md +462 -0
  26. tomskit/redis/__init__.py +6 -0
  27. tomskit/redis/config.py +85 -0
  28. tomskit/redis/redis_pool.py +87 -0
  29. tomskit/redis/redis_sync.py +66 -0
  30. tomskit/server/__init__.py +47 -0
  31. tomskit/server/config.py +117 -0
  32. tomskit/server/exceptions.py +412 -0
  33. tomskit/server/middleware.py +371 -0
  34. tomskit/server/parser.py +312 -0
  35. tomskit/server/resource.py +464 -0
  36. tomskit/server/server.py +276 -0
  37. tomskit/server/type.py +263 -0
  38. tomskit/sqlalchemy/README.md +590 -0
  39. tomskit/sqlalchemy/__init__.py +20 -0
  40. tomskit/sqlalchemy/config.py +125 -0
  41. tomskit/sqlalchemy/database.py +125 -0
  42. tomskit/sqlalchemy/pagination.py +359 -0
  43. tomskit/sqlalchemy/property.py +19 -0
  44. tomskit/sqlalchemy/sqlalchemy.py +131 -0
  45. tomskit/sqlalchemy/types.py +32 -0
  46. tomskit/task/README.md +67 -0
  47. tomskit/task/__init__.py +4 -0
  48. tomskit/task/task_manager.py +124 -0
  49. tomskit/tools/README.md +63 -0
  50. tomskit/tools/__init__.py +18 -0
  51. tomskit/tools/config.py +70 -0
  52. tomskit/tools/warnings.py +37 -0
  53. tomskit/tools/woker.py +81 -0
  54. tomskit/utils/README.md +666 -0
  55. tomskit/utils/README_SERIALIZER.md +644 -0
  56. tomskit/utils/__init__.py +35 -0
  57. tomskit/utils/fields.py +434 -0
  58. tomskit/utils/marshal_utils.py +137 -0
  59. tomskit/utils/response_utils.py +13 -0
  60. tomskit/utils/serializers.py +447 -0
@@ -0,0 +1,462 @@
1
+ # Redis Module Guide
2
+
3
+ 该模块提供了异步和同步 Redis 客户端支持,支持单机、Sentinel 和 Cluster 模式,适用于 FastAPI 异步环境。
4
+
5
+ ## 模块概述
6
+
7
+ Redis 模块基于 `redis.asyncio` 和 `redis` 库,提供了完整的异步和同步 Redis 客户端支持。主要特性包括:
8
+
9
+ - ⚡ **完全异步**:基于 `redis.asyncio` 实现异步 Redis 客户端
10
+ - 🔄 **多种模式**:支持单机、Sentinel 和 Cluster 模式
11
+ - 🔒 **SSL 支持**:支持 SSL/TLS 加密连接
12
+ - 🛠️ **配置管理**:基于 Pydantic Settings 的配置类
13
+ - 🔧 **连接池管理**:自动管理连接池,支持高并发场景
14
+ - 📦 **类型安全**:使用泛型提供类型安全的客户端访问
15
+
16
+ **Import Path:**
17
+ ```python
18
+ from tomskit.redis import (
19
+ RedisClientWrapper,
20
+ redis_client,
21
+ RedisConfig,
22
+ redis_sync_client
23
+ )
24
+ ```
25
+
26
+ ## 核心类和函数
27
+
28
+ ### RedisConfig
29
+
30
+ Redis 配置类,继承自 `pydantic_settings.BaseSettings`,用于管理 Redis 连接配置。
31
+
32
+ ```python
33
+ class RedisConfig(BaseSettings):
34
+ REDIS_HOST: str = Field(default="localhost", ...)
35
+ REDIS_PORT: PositiveInt = Field(default=6379, ...)
36
+ REDIS_USERNAME: Optional[str] = Field(default=None, ...)
37
+ REDIS_PASSWORD: Optional[str] = Field(default=None, ...)
38
+ REDIS_DB: NonNegativeInt = Field(default=0, ...)
39
+ REDIS_USE_SSL: bool = Field(default=False, ...)
40
+ REDIS_USE_SENTINEL: Optional[bool] = Field(default=False, ...)
41
+ REDIS_SENTINELS: Optional[str] = Field(default=None, ...)
42
+ REDIS_SENTINEL_SERVICE_NAME: Optional[str] = Field(default=None, ...)
43
+ REDIS_SENTINEL_USERNAME: Optional[str] = Field(default=None, ...)
44
+ REDIS_SENTINEL_PASSWORD: Optional[str] = Field(default=None, ...)
45
+ REDIS_SENTINEL_SOCKET_TIMEOUT: Optional[PositiveFloat] = Field(default=0.1, ...)
46
+ REDIS_USE_CLUSTERS: bool = Field(default=False, ...)
47
+ REDIS_CLUSTERS: Optional[str] = Field(default=None, ...)
48
+ REDIS_CLUSTERS_PASSWORD: Optional[str] = Field(default=None, ...)
49
+ ```
50
+
51
+ **配置属性说明:**
52
+ - `REDIS_HOST`: Redis 服务器主机地址,默认为 `localhost`
53
+ - `REDIS_PORT`: Redis 服务器端口,默认为 `6379`,必须为正整数
54
+ - `REDIS_USERNAME`: Redis 认证用户名(如果需要),默认为 `None`
55
+ - `REDIS_PASSWORD`: Redis 认证密码(如果需要),默认为 `None`
56
+ - `REDIS_DB`: Redis 数据库编号(0-15),默认为 `0`
57
+ - `REDIS_USE_SSL`: 是否启用 SSL/TLS 加密连接,默认为 `False`
58
+ - `REDIS_USE_SENTINEL`: 是否启用 Redis Sentinel 模式,默认为 `False`
59
+ - `REDIS_SENTINELS`: Sentinel 节点列表,格式为逗号分隔的 `host:port`,例如 `"127.0.0.1:26379,127.0.0.1:26380"`
60
+ - `REDIS_SENTINEL_SERVICE_NAME`: Sentinel 服务名称,默认为 `None`
61
+ - `REDIS_SENTINEL_USERNAME`: Sentinel 认证用户名,默认为 `None`
62
+ - `REDIS_SENTINEL_PASSWORD`: Sentinel 认证密码,默认为 `None`
63
+ - `REDIS_SENTINEL_SOCKET_TIMEOUT`: Sentinel 连接超时时间(秒),默认为 `0.1`
64
+ - `REDIS_USE_CLUSTERS`: 是否启用 Redis Cluster 模式,默认为 `False`
65
+ - `REDIS_CLUSTERS`: Cluster 节点列表,格式为逗号分隔的 `host:port`,例如 `"127.0.0.1:7000,127.0.0.1:7001"`
66
+ - `REDIS_CLUSTERS_PASSWORD`: Cluster 认证密码,默认为 `None`
67
+
68
+ **使用示例:**
69
+ ```python
70
+ from tomskit.redis.config import RedisConfig
71
+
72
+ # 单机模式配置
73
+ config = RedisConfig(
74
+ REDIS_HOST='localhost',
75
+ REDIS_PORT=6379,
76
+ REDIS_PASSWORD='your_password',
77
+ REDIS_DB=0
78
+ )
79
+
80
+ # Sentinel 模式配置
81
+ sentinel_config = RedisConfig(
82
+ REDIS_USE_SENTINEL=True,
83
+ REDIS_SENTINELS='127.0.0.1:26379,127.0.0.1:26380',
84
+ REDIS_SENTINEL_SERVICE_NAME='mymaster',
85
+ REDIS_PASSWORD='your_password'
86
+ )
87
+
88
+ # Cluster 模式配置
89
+ cluster_config = RedisConfig(
90
+ REDIS_USE_CLUSTERS=True,
91
+ REDIS_CLUSTERS='127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002',
92
+ REDIS_CLUSTERS_PASSWORD='your_password'
93
+ )
94
+ ```
95
+
96
+ ### RedisClientWrapper
97
+
98
+ Redis 客户端包装器,提供类型安全的 Redis 客户端访问。支持异步操作和连接池管理。
99
+
100
+ ```python
101
+ class RedisClientWrapper(Generic[T]):
102
+ _client: Optional[T]
103
+
104
+ def __init__(self) -> None: ...
105
+
106
+ def __getattr__(self, item: str) -> Any: ...
107
+
108
+ def set_client(self, client: T) -> None: ...
109
+
110
+ @staticmethod
111
+ def initialize(config: dict[str, Any]) -> None: ...
112
+
113
+ @staticmethod
114
+ async def shutdown() -> None: ...
115
+ ```
116
+
117
+ **功能特性:**
118
+ - 提供类型安全的 Redis 客户端访问
119
+ - 自动代理所有 Redis 客户端方法
120
+ - 支持连接池管理(默认最大连接数为 128)
121
+ - 支持异步操作
122
+ - 提供优雅关闭方法
123
+
124
+ **方法说明:**
125
+ - `initialize(config)`: 静态方法,初始化 Redis 客户端。根据配置自动选择单机、Sentinel 或 Cluster 模式
126
+ - `shutdown()`: 静态异步方法,关闭 Redis 客户端连接
127
+ - `set_client(client)`: 设置 Redis 客户端实例
128
+ - `__getattr__(item)`: 代理所有 Redis 客户端方法,如 `get`, `set`, `hget`, `hset` 等
129
+
130
+ ### redis_client
131
+
132
+ 全局异步 Redis 客户端实例,类型为 `RedisClientWrapper[Redis]`。
133
+
134
+ ```python
135
+ redis_client: RedisClientWrapper[Redis] = RedisClientWrapper()
136
+ ```
137
+
138
+ **使用场景:**
139
+ 在需要异步 Redis 操作的地方使用 `redis_client` 实例。
140
+
141
+ ### redis_sync_client
142
+
143
+ 创建同步 Redis 客户端函数,返回同步的 Redis 客户端实例。
144
+
145
+ ```python
146
+ def redis_sync_client(config: dict[str, Any]) -> Redis | None: ...
147
+ ```
148
+
149
+ **参数说明:**
150
+ - `config`: 配置字典,包含 Redis 连接参数
151
+
152
+ **返回值:**
153
+ - 返回 `Redis` 客户端实例,如果配置错误则返回 `None`
154
+
155
+ **功能特性:**
156
+ - 支持单机、Sentinel 和 Cluster 模式
157
+ - 支持 SSL/TLS 加密连接
158
+ - 同步操作,适用于非异步场景
159
+
160
+ ## 完整使用示例
161
+
162
+ ### 初始化异步客户端
163
+
164
+ ```python
165
+ from tomskit.redis import RedisClientWrapper, redis_client, RedisConfig
166
+
167
+ # 创建配置
168
+ config = RedisConfig(
169
+ REDIS_HOST='localhost',
170
+ REDIS_PORT=6379,
171
+ REDIS_PASSWORD='your_password',
172
+ REDIS_DB=0
173
+ )
174
+
175
+ # 将配置转换为字典
176
+ config_dict = config.model_dump()
177
+
178
+ # 初始化客户端
179
+ RedisClientWrapper.initialize(config_dict)
180
+
181
+ # 现在可以使用 redis_client 进行操作
182
+ await redis_client.set('key', 'value')
183
+ value = await redis_client.get('key')
184
+ print(value) # 输出: value
185
+ ```
186
+
187
+ ### 基础操作
188
+
189
+ ```python
190
+ from tomskit.redis import redis_client
191
+
192
+ # 字符串操作
193
+ await redis_client.set('name', 'John')
194
+ name = await redis_client.get('name')
195
+ print(name) # 输出: John
196
+
197
+ # 设置过期时间
198
+ await redis_client.setex('token', 3600, 'abc123')
199
+
200
+ # 检查键是否存在
201
+ exists = await redis_client.exists('name')
202
+ print(exists) # 输出: 1
203
+
204
+ # 删除键
205
+ await redis_client.delete('name')
206
+
207
+ # 设置多个键值对
208
+ await redis_client.mset({'key1': 'value1', 'key2': 'value2'})
209
+
210
+ # 获取多个键的值
211
+ values = await redis_client.mget(['key1', 'key2'])
212
+ print(values) # 输出: ['value1', 'value2']
213
+ ```
214
+
215
+ ### Hash 操作
216
+
217
+ ```python
218
+ from tomskit.redis import redis_client
219
+
220
+ # 设置 Hash 字段
221
+ await redis_client.hset('user:1', mapping={
222
+ 'name': 'John',
223
+ 'age': '30',
224
+ 'email': 'john@example.com'
225
+ })
226
+
227
+ # 获取 Hash 字段
228
+ name = await redis_client.hget('user:1', 'name')
229
+ print(name) # 输出: John
230
+
231
+ # 获取所有 Hash 字段
232
+ user_data = await redis_client.hgetall('user:1')
233
+ print(user_data) # 输出: {'name': 'John', 'age': '30', 'email': 'john@example.com'}
234
+
235
+ # 删除 Hash 字段
236
+ await redis_client.hdel('user:1', 'email')
237
+
238
+ # 检查 Hash 字段是否存在
239
+ exists = await redis_client.hexists('user:1', 'name')
240
+ print(exists) # 输出: True
241
+ ```
242
+
243
+ ### List 操作
244
+
245
+ ```python
246
+ from tomskit.redis import redis_client
247
+
248
+ # 从左侧推入
249
+ await redis_client.lpush('tasks', 'task1', 'task2', 'task3')
250
+
251
+ # 从右侧推入
252
+ await redis_client.rpush('tasks', 'task4')
253
+
254
+ # 获取列表长度
255
+ length = await redis_client.llen('tasks')
256
+ print(length) # 输出: 4
257
+
258
+ # 获取列表元素
259
+ tasks = await redis_client.lrange('tasks', 0, -1)
260
+ print(tasks) # 输出: ['task3', 'task2', 'task1', 'task4']
261
+
262
+ # 从左侧弹出
263
+ task = await redis_client.lpop('tasks')
264
+ print(task) # 输出: task3
265
+ ```
266
+
267
+ ### Set 操作
268
+
269
+ ```python
270
+ from tomskit.redis import redis_client
271
+
272
+ # 添加元素
273
+ await redis_client.sadd('tags', 'python', 'redis', 'fastapi')
274
+
275
+ # 获取所有元素
276
+ tags = await redis_client.smembers('tags')
277
+ print(tags) # 输出: {'python', 'redis', 'fastapi'}
278
+
279
+ # 检查元素是否存在
280
+ is_member = await redis_client.sismember('tags', 'python')
281
+ print(is_member) # 输出: True
282
+
283
+ # 获取集合大小
284
+ size = await redis_client.scard('tags')
285
+ print(size) # 输出: 3
286
+
287
+ # 移除元素
288
+ await redis_client.srem('tags', 'redis')
289
+ ```
290
+
291
+ ### 有序集合(Sorted Set)操作
292
+
293
+ ```python
294
+ from tomskit.redis import redis_client
295
+
296
+ # 添加元素(带分数)
297
+ await redis_client.zadd('leaderboard', {'player1': 100, 'player2': 200, 'player3': 150})
298
+
299
+ # 获取排名(按分数从高到低)
300
+ top_players = await redis_client.zrevrange('leaderboard', 0, 2, withscores=True)
301
+ print(top_players) # 输出: [('player2', 200.0), ('player3', 150.0), ('player1', 100.0)]
302
+
303
+ # 获取元素分数
304
+ score = await redis_client.zscore('leaderboard', 'player1')
305
+ print(score) # 输出: 100.0
306
+
307
+ # 增加元素分数
308
+ new_score = await redis_client.zincrby('leaderboard', 50, 'player1')
309
+ print(new_score) # 输出: 150.0
310
+ ```
311
+
312
+ ### 在 FastAPI 中使用
313
+
314
+ ```python
315
+ from fastapi import FastAPI
316
+ from tomskit.redis import RedisClientWrapper, redis_client, RedisConfig
317
+ from contextlib import asynccontextmanager
318
+
319
+ app = FastAPI()
320
+
321
+ @asynccontextmanager
322
+ async def lifespan(app: FastAPI):
323
+ # 启动时初始化 Redis
324
+ config = RedisConfig()
325
+ RedisClientWrapper.initialize(config.model_dump())
326
+ yield
327
+ # 关闭时清理 Redis 连接
328
+ await RedisClientWrapper.shutdown()
329
+
330
+ app = FastAPI(lifespan=lifespan)
331
+
332
+ @app.get("/cache/{key}")
333
+ async def get_cache(key: str):
334
+ value = await redis_client.get(key)
335
+ if value is None:
336
+ return {"error": "Key not found"}
337
+ return {"key": key, "value": value}
338
+
339
+ @app.post("/cache/{key}")
340
+ async def set_cache(key: str, value: str):
341
+ await redis_client.set(key, value)
342
+ return {"key": key, "value": value, "status": "set"}
343
+ ```
344
+
345
+ ### 使用 Sentinel 模式
346
+
347
+ ```python
348
+ from tomskit.redis import RedisClientWrapper, redis_client, RedisConfig
349
+
350
+ # Sentinel 模式配置
351
+ config = RedisConfig(
352
+ REDIS_USE_SENTINEL=True,
353
+ REDIS_SENTINELS='127.0.0.1:26379,127.0.0.1:26380,127.0.0.1:26381',
354
+ REDIS_SENTINEL_SERVICE_NAME='mymaster',
355
+ REDIS_PASSWORD='your_password',
356
+ REDIS_DB=0
357
+ )
358
+
359
+ # 初始化客户端
360
+ RedisClientWrapper.initialize(config.model_dump())
361
+
362
+ # 使用方式与单机模式相同
363
+ await redis_client.set('key', 'value')
364
+ value = await redis_client.get('key')
365
+ ```
366
+
367
+ ### 使用 Cluster 模式
368
+
369
+ ```python
370
+ from tomskit.redis import RedisClientWrapper, redis_client, RedisConfig
371
+
372
+ # Cluster 模式配置
373
+ config = RedisConfig(
374
+ REDIS_USE_CLUSTERS=True,
375
+ REDIS_CLUSTERS='127.0.0.1:7000,127.0.0.1:7001,127.0.0.1:7002',
376
+ REDIS_CLUSTERS_PASSWORD='your_password'
377
+ )
378
+
379
+ # 初始化客户端
380
+ RedisClientWrapper.initialize(config.model_dump())
381
+
382
+ # 使用方式与单机模式相同
383
+ await redis_client.set('key', 'value')
384
+ value = await redis_client.get('key')
385
+ ```
386
+
387
+ ### 使用同步客户端
388
+
389
+ ```python
390
+ from tomskit.redis import redis_sync_client, RedisConfig
391
+
392
+ # 创建配置
393
+ config = RedisConfig(
394
+ REDIS_HOST='localhost',
395
+ REDIS_PORT=6379,
396
+ REDIS_PASSWORD='your_password'
397
+ )
398
+
399
+ # 转换为字典
400
+ config_dict = config.model_dump()
401
+
402
+ # 创建同步客户端
403
+ redis = redis_sync_client(config_dict)
404
+
405
+ if redis:
406
+ # 同步操作
407
+ redis.set('key', 'value')
408
+ value = redis.get('key')
409
+ print(value) # 输出: value
410
+
411
+ # 关闭连接
412
+ redis.close()
413
+ ```
414
+
415
+ ### 优雅关闭
416
+
417
+ ```python
418
+ from tomskit.redis import RedisClientWrapper
419
+
420
+ # 在应用关闭时调用
421
+ async def cleanup():
422
+ await RedisClientWrapper.shutdown()
423
+ ```
424
+
425
+ ## 环境变量配置
426
+
427
+ Redis 模块支持通过环境变量进行配置:
428
+
429
+ - `REDIS_HOST`: Redis 服务器主机地址
430
+ - `REDIS_PORT`: Redis 服务器端口
431
+ - `REDIS_USERNAME`: Redis 认证用户名
432
+ - `REDIS_PASSWORD`: Redis 认证密码
433
+ - `REDIS_DB`: Redis 数据库编号
434
+ - `REDIS_USE_SSL`: 是否启用 SSL/TLS
435
+ - `REDIS_USE_SENTINEL`: 是否启用 Sentinel 模式
436
+ - `REDIS_SENTINELS`: Sentinel 节点列表
437
+ - `REDIS_SENTINEL_SERVICE_NAME`: Sentinel 服务名称
438
+ - `REDIS_SENTINEL_USERNAME`: Sentinel 认证用户名
439
+ - `REDIS_SENTINEL_PASSWORD`: Sentinel 认证密码
440
+ - `REDIS_SENTINEL_SOCKET_TIMEOUT`: Sentinel 连接超时时间
441
+ - `REDIS_USE_CLUSTERS`: 是否启用 Cluster 模式
442
+ - `REDIS_CLUSTERS`: Cluster 节点列表
443
+ - `REDIS_CLUSTERS_PASSWORD`: Cluster 认证密码
444
+
445
+ **注意:** 在代码中使用 `REDIS_USE_CLUSTER`(单数)来检查配置,但配置类中定义的是 `REDIS_USE_CLUSTERS`(复数)。初始化时需要确保配置字典中的键名正确。
446
+
447
+ ## 注意事项
448
+
449
+ 1. **异步操作**:`redis_client` 的所有操作都是异步的,需要使用 `await` 关键字
450
+ 2. **连接池管理**:异步客户端默认最大连接数为 128,可根据实际需求调整
451
+ 3. **初始化顺序**:在使用 `redis_client` 之前必须先调用 `RedisClientWrapper.initialize()`
452
+ 4. **优雅关闭**:应用关闭时应该调用 `RedisClientWrapper.shutdown()` 来关闭连接
453
+ 5. **配置转换**:使用 `RedisConfig` 时,需要先转换为字典再传递给 `initialize()` 方法
454
+ 6. **Sentinel 和 Cluster**:使用 Sentinel 或 Cluster 模式时,需要确保相应的配置项都已正确设置
455
+ 7. **SSL 连接**:启用 SSL 时需要确保 Redis 服务器支持 SSL/TLS
456
+ 8. **同步客户端**:同步客户端主要用于非异步场景,如 Celery 任务等
457
+
458
+ ## 相关文档
459
+
460
+ - [Redis Guide](../docs/specs/redis_guide.md) - 详细的 Redis 使用指南
461
+ - [Redis 官方文档](https://redis.io/docs/) - Redis 官方文档
462
+ - [redis-py 文档](https://redis.readthedocs.io/) - redis-py 库文档
@@ -0,0 +1,6 @@
1
+
2
+ from tomskit.redis.redis_pool import RedisClientWrapper, redis_client
3
+ from tomskit.redis.config import RedisConfig
4
+ from tomskit.redis.redis_sync import redis_sync_client
5
+
6
+ __all__ = ['RedisClientWrapper', 'redis_client', 'RedisConfig', 'redis_sync_client']
@@ -0,0 +1,85 @@
1
+ from typing import Optional
2
+
3
+ from pydantic import Field, NonNegativeInt, PositiveFloat, PositiveInt
4
+ from pydantic_settings import BaseSettings
5
+
6
+
7
+ class RedisConfig(BaseSettings):
8
+ """
9
+ Configuration settings for Redis connection
10
+ """
11
+
12
+ REDIS_HOST: str = Field(
13
+ description="Hostname or IP address of the Redis server",
14
+ default="localhost",
15
+ )
16
+
17
+ REDIS_PORT: PositiveInt = Field(
18
+ description="Port number on which the Redis server is listening",
19
+ default=6379,
20
+ )
21
+
22
+ REDIS_USERNAME: Optional[str] = Field(
23
+ description="Username for Redis authentication (if required)",
24
+ default=None,
25
+ )
26
+
27
+ REDIS_PASSWORD: Optional[str] = Field(
28
+ description="Password for Redis authentication (if required)",
29
+ default=None,
30
+ )
31
+
32
+ REDIS_DB: NonNegativeInt = Field(
33
+ description="Redis database number to use (0-15)",
34
+ default=0,
35
+ )
36
+
37
+ REDIS_USE_SSL: bool = Field(
38
+ description="Enable SSL/TLS for the Redis connection",
39
+ default=False,
40
+ )
41
+
42
+ REDIS_USE_SENTINEL: Optional[bool] = Field(
43
+ description="Enable Redis Sentinel mode for high availability",
44
+ default=False,
45
+ )
46
+
47
+ REDIS_SENTINELS: Optional[str] = Field(
48
+ description="Comma-separated list of Redis Sentinel nodes (host:port)",
49
+ default=None,
50
+ )
51
+
52
+ REDIS_SENTINEL_SERVICE_NAME: Optional[str] = Field(
53
+ description="Name of the Redis Sentinel service to monitor",
54
+ default=None,
55
+ )
56
+
57
+ REDIS_SENTINEL_USERNAME: Optional[str] = Field(
58
+ description="Username for Redis Sentinel authentication (if required)",
59
+ default=None,
60
+ )
61
+
62
+ REDIS_SENTINEL_PASSWORD: Optional[str] = Field(
63
+ description="Password for Redis Sentinel authentication (if required)",
64
+ default=None,
65
+ )
66
+
67
+ REDIS_SENTINEL_SOCKET_TIMEOUT: Optional[PositiveFloat] = Field(
68
+ description="Socket timeout in seconds for Redis Sentinel connections",
69
+ default=0.1,
70
+ )
71
+
72
+ REDIS_USE_CLUSTERS: bool = Field(
73
+ description="Enable Redis Clusters mode for high availability",
74
+ default=False,
75
+ )
76
+
77
+ REDIS_CLUSTERS: Optional[str] = Field(
78
+ description="Comma-separated list of Redis Clusters nodes (host:port)",
79
+ default=None,
80
+ )
81
+
82
+ REDIS_CLUSTERS_PASSWORD: Optional[str] = Field(
83
+ description="Password for Redis Clusters authentication (if required)",
84
+ default=None,
85
+ )
@@ -0,0 +1,87 @@
1
+ """
2
+ Redis 扩展模块
3
+ """
4
+ from typing import Any, Union, Optional, TypeVar, Generic
5
+ from redis.asyncio import Connection, ConnectionPool, Redis, Sentinel, SSLConnection, RedisCluster
6
+ from redis.asyncio.cluster import ClusterNode
7
+
8
+ T = TypeVar("T", bound=Redis)
9
+
10
+ class RedisClientWrapper(Generic[T]):
11
+ _client: Optional[T] = None
12
+ def __init__(self) -> None:
13
+ self._client: Redis | None = None
14
+
15
+ def __getattr__(self, item) -> Any:
16
+ if self._client is None:
17
+ raise RuntimeError("Redis client is not initialized. Call initialize first.")
18
+ return getattr(self._client, item)
19
+
20
+ def set_client(self, client: T) -> None:
21
+ if self._client is None:
22
+ self._client = client
23
+
24
+ @staticmethod
25
+ def initialize(config) -> None:
26
+ global redis_client
27
+ connection_class: type[Union[Connection, SSLConnection]] = Connection
28
+ if config.get("REDIS_USE_SSL"):
29
+ connection_class = SSLConnection
30
+
31
+ redis_params = {
32
+ "username": config.get("REDIS_USERNAME"),
33
+ "password": config.get("REDIS_PASSWORD"),
34
+ "db": config.get("REDIS_DB"),
35
+ "encoding": "utf-8",
36
+ "encoding_errors": "strict",
37
+ "decode_responses": True,
38
+ "max_connections": config.get("REDIS_MAX_CONNECTIONS", 128),
39
+ }
40
+ if config.get("REDIS_USE_SENTINEL"):
41
+ sentinel_hosts = [
42
+ (node.split(":")[0], int(node.split(":")[1])) for node in config.get("REDIS_SENTINELS").split(",")
43
+ ]
44
+ sentinel = Sentinel(
45
+ sentinel_hosts,
46
+ sentinel_kwargs={
47
+ "socket_timeout": config.get("REDIS_SENTINEL_SOCKET_TIMEOUT", 0.1),
48
+ "username": config.get("REDIS_SENTINEL_USERNAME"),
49
+ "password": config.get("REDIS_SENTINEL_PASSWORD"),
50
+ },
51
+ )
52
+ master = sentinel.master_for(config.get("REDIS_SENTINEL_SERVICE_NAME"), **redis_params)
53
+ redis_client.set_client(master)
54
+ elif config.get("REDIS_USE_CLUSTER"):
55
+ nodes = [
56
+ ClusterNode(host=node.split(":")[0], port=int(node.split(":")[1])) for node in config.get("REDIS_CLUSTERS").split(",")
57
+ ]
58
+ redis_params.update(
59
+ {
60
+ "password" : config.get("REDIS_CLUSTERS_PASSWORD"),
61
+ }
62
+ )
63
+ cluster = RedisCluster(
64
+ startup_nodes=nodes,
65
+ **redis_params
66
+ )
67
+ redis_client.set_client(cluster) # type: ignore
68
+ else:
69
+ redis_params.update(
70
+ {
71
+ "host": config.get("REDIS_HOST"),
72
+ "port": config.get("REDIS_PORT"),
73
+ "max_connections": config.get("REDIS_MAX_CONNECTIONS", 128),
74
+ "connection_class": connection_class,
75
+ }
76
+ )
77
+ pool = ConnectionPool(**redis_params)
78
+ redis_client.set_client(Redis(connection_pool=pool))
79
+
80
+ @staticmethod
81
+ async def shutdown() -> None:
82
+ global redis_client
83
+ if redis_client._client is not None:
84
+ await redis_client._client.aclose()
85
+ redis_client._client = None
86
+
87
+ redis_client: RedisClientWrapper[Redis] = RedisClientWrapper()
@@ -0,0 +1,66 @@
1
+ """
2
+ Redis 扩展模块
3
+ """
4
+ from typing import Union
5
+ from redis import Redis, Connection, ConnectionPool, Sentinel, SSLConnection, RedisCluster
6
+ from redis.cluster import ClusterNode
7
+
8
+ def redis_sync_client(config) -> Redis | None:
9
+ """
10
+ Redis 同步客户端
11
+ """
12
+ redis: Redis | None = None
13
+
14
+ connection_class: type[Union[Connection, SSLConnection]] = Connection
15
+ if config.get("REDIS_USE_SSL"):
16
+ connection_class = SSLConnection
17
+
18
+ redis_params = {
19
+ "username": config.get("REDIS_USERNAME"),
20
+ "password": config.get("REDIS_PASSWORD"),
21
+ "db": config.get("REDIS_DB"),
22
+ "encoding": "utf-8",
23
+ "encoding_errors": "strict",
24
+ "decode_responses": True,
25
+ "max_connections": 1,
26
+ }
27
+ if config.get("REDIS_USE_SENTINEL"):
28
+ sentinel_hosts = [
29
+ (node.split(":")[0], int(node.split(":")[1])) for node in config.get("REDIS_SENTINELS").split(",")
30
+ ]
31
+ sentinel = Sentinel(
32
+ sentinel_hosts,
33
+ sentinel_kwargs={
34
+ "socket_timeout": config.get("REDIS_SENTINEL_SOCKET_TIMEOUT", 0.1),
35
+ "username": config.get("REDIS_SENTINEL_USERNAME"),
36
+ "password": config.get("REDIS_SENTINEL_PASSWORD"),
37
+ },
38
+ )
39
+ redis = sentinel.master_for(config.get("REDIS_SENTINEL_SERVICE_NAME"), **redis_params)
40
+ elif config.get("REDIS_USE_CLUSTER"):
41
+ nodes = [
42
+ ClusterNode(host=node.split(":")[0], port=int(node.split(":")[1])) for node in config.get("REDIS_CLUSTERS").split(",")
43
+ ]
44
+ redis_params.update(
45
+ {
46
+ "password" : config.get("REDIS_CLUSTERS_PASSWORD"),
47
+ }
48
+ )
49
+ redis = RedisCluster( # type: ignore
50
+ startup_nodes=nodes,
51
+ **redis_params
52
+ )
53
+ else:
54
+ redis_params.update(
55
+ {
56
+ "host": config.get("REDIS_HOST"),
57
+ "port": config.get("REDIS_PORT"),
58
+ "max_connections": 1,
59
+ "connection_class": connection_class,
60
+ }
61
+ )
62
+ pool = ConnectionPool(**redis_params)
63
+ redis = Redis(connection_pool=pool)
64
+
65
+ return redis
66
+