credential-pool-sdk
一个基于 Redis 的高并发共享凭证池 SDK。
它的目标不是绑定某个具体业务,而是提供一套可复用的“凭证池基础设施”能力,适合在多进程、多节点环境下统一管理以下对象:
- Cookie
- JWT
- Token
- Nonce
- Session
- 其他需要共享、复用、并发控制的认证凭证
设计目标
本项目聚焦“池能力”,不耦合业务代码,不内置具体站点逻辑。
核心目标:
- 通过
pip install安装使用 - 只依赖 Redis 作为共享状态中心
- 提供统一的凭证池 API
- 支持高并发随机获取
- 支持独占获取与归还
- 支持失败计数、租约回收、统计查询
适用场景
适合以下场景:
- 多个 worker 共享一批 Cookie / JWT
- 某些凭证允许被重复并发使用
- 某些凭证同一时间只能被一个 worker 独占使用
- 多台机器共同消费同一池凭证
- 业务项目希望把“凭证共享与并发控制”抽成独立 SDK
架构图
下面这张图描述了 credential-pool-sdk 在业务项目中的典型位置:
flowchart LR
A[业务 Producer<br/>生成 Cookie / JWT / Token] --> B[credential-pool-sdk]
C[业务 Consumer 1<br/>Worker / Collector] --> B
D[业务 Consumer 2<br/>Worker / Service] --> B
E[业务 Consumer N<br/>多节点实例] --> B
B --> F[(Redis)]
subgraph SDK[credential-pool-sdk]
B1[RedisCredentialPool]
B2[CredentialItem]
B3[Lease / Failure / Stats]
end
B --- B1
B --- B2
B --- B3
架构说明
- Producer 负责生成凭证,并调用 SDK 写入池。
- Consumer 负责从池中获取凭证,并在使用后回写结果。
- SDK 只负责“共享池能力”,不负责业务调度。
- Redis 作为多节点之间的共享状态中心。
时序图
下面是“独占获取一个当前未被使用的凭证,并在使用后归还”的典型时序:
sequenceDiagram
participant Worker as 业务 Worker
participant SDK as credential-pool-sdk
participant Redis as Redis
participant Target as 目标站点
Worker->>SDK: acquire_available(owner, lease_ttl)
SDK->>Redis: Lua 原子脚本<br/>从 available 随机取一个 key<br/>写入 lease_owner / lease_expiry
Redis-->>SDK: 返回 credential key
SDK->>Redis: HGET items[key]
Redis-->>SDK: 返回 CredentialItem
SDK-->>Worker: 返回独占凭证
Worker->>Target: 使用凭证发起请求
Target-->>Worker: 返回结果
alt 成功
Worker->>SDK: record_success(key)
SDK->>Redis: HDEL failures[key]
Worker->>SDK: release(key, owner)
SDK->>Redis: Lua 原子脚本<br/>删除 lease<br/>放回 available
else 失败
Worker->>SDK: record_failure(key)
SDK->>Redis: HINCRBY failures[key]
Worker->>SDK: release(key, owner)
SDK->>Redis: Lua 原子脚本<br/>删除 lease<br/>放回 available
end
两种获取模式的区别
模式一:共享获取
item = await pool.random_get()
特点:
- 同一个凭证可以被多个调用方同时拿到
- 不会改变凭证当前可用状态
- 适合可共享复用的凭证
模式二:独占获取
leased = await pool.acquire_available(owner="worker-1", lease_ttl=60)
特点:
- 同一个凭证同一时间只能被一个调用方持有
- 获取后必须通过
release()归还 - 若调用方崩溃,可通过
reclaim_expired_leases()回收
核心能力
1. 统一对象模型
池中的元素不是裸字符串,而是统一对象,例如:
{
"key": "jwt:1",
"value": "token-1",
"kind": "jwt",
"status": "active",
"created_at": 1760000000,
"updated_at": 1760000000,
"meta": {}
}
这样后续扩展字段时,不需要改动使用方式。
2. 两种获取模式
本 SDK 提供两种不同的“取凭证”方式。
random_get()
随机获取一个凭证。
特点:
- 同一个凭证可以被多个调用方同时取到
- 适合“可共享复用”的场景
- 例如:某些 JWT、某些无状态 Cookie
acquire_available(owner, lease_ttl)
随机获取一个当前未被占用的凭证,并立即标记为“已被使用”。
特点:
- 同一时间,一个凭证只能被一个调用方独占获取
- 适合“同一时刻只能单独使用”的场景
- 获取后必须通过
release()归还 - 若调用方异常退出,也可以通过过期租约回收
3. 归还凭证
通过:
await pool.release(key, owner="worker-1")
将独占中的凭证重新放回可用池。
4. 基础 CRUD
支持:
- 添加单个凭证
- 批量添加凭证
- 查询单个凭证
- 列表查询
- 更新凭证
- 删除凭证
5. 失败计数
支持:
record_success(key)record_failure(key)get_failure_count(key)
适合业务层自行实现失败阈值策略。
6. 租约回收
支持:
await pool.reclaim_expired_leases()
用于回收超时未归还的独占凭证。
7. 统计能力
支持:
- 总凭证数
- 可用凭证数
- 已租约凭证数
- 失败计数条目数
安装
pip install credential-pool-sdk
如果是本地开发:
pip install -e .
快速开始
创建连接
from credential_pool_sdk import RedisCredentialPool
pool = RedisCredentialPool.from_url(
"redis://127.0.0.1:6379/0",
pool_name="vimeo_jwt",
)
凭证对象示例
from credential_pool_sdk import CredentialItem
item = CredentialItem(
key="jwt:1",
value="token-1",
kind="jwt",
meta={"source": "producer-a"},
)
添加凭证
from credential_pool_sdk import CredentialItem
await pool.add(
CredentialItem(
key="jwt:1",
value="token-1",
kind="jwt",
)
)
随机获取一个可共享凭证
item = await pool.random_get()
if item is not None:
print(item.key, item.value)
独占获取一个当前未使用凭证
leased = await pool.acquire_available(owner="worker-1", lease_ttl=60)
if leased is not None:
print("acquired:", leased.key)
归还独占凭证
await pool.release(leased.key, owner="worker-1")
失败计数
await pool.record_failure("jwt:1")
failures = await pool.get_failure_count("jwt:1")
print(failures)
回收超时未归还租约
reclaimed = await pool.reclaim_expired_leases()
print("reclaimed:", reclaimed)
完整示例
import asyncio
from credential_pool_sdk import CredentialItem, RedisCredentialPool
async def main() -> None:
pool = RedisCredentialPool.from_url(
"redis://127.0.0.1:6379/0",
pool_name="vimeo_jwt",
)
await pool.add(
CredentialItem(
key="jwt:1",
value="token-1",
kind="jwt",
)
)
shared_item = await pool.random_get()
print("shared:", shared_item)
leased_item = await pool.acquire_available(owner="worker-1", lease_ttl=60)
print("leased:", leased_item)
if leased_item is not None:
await pool.release(leased_item.key, owner="worker-1")
stats = await pool.stats()
print(stats)
await pool.close()
asyncio.run(main())
API 概览
池对象
RedisCredentialPool
主要方法:
add(item)add_many(items)get(key)list(limit=100)update(key, patch)remove(key)random_get()acquire_available(owner, lease_ttl=60, exclude_keys=None)release(key, owner=None)reclaim_expired_leases(now_ts=None)record_success(key)record_failure(key, amount=1)get_failure_count(key)count()count_available()count_leased()stats()close()
数据对象
CredentialItem
字段:
keyvaluekindstatuscreated_atupdated_atmeta
统计对象
PoolStats
字段:
totalavailableleasedfailures
Redis 数据模型
对于池名 {pool_name},当前版本使用以下 Redis Key:
credential_pool:{pool_name}:itemsRedis Hash,存储key -> jsoncredential_pool:{pool_name}:activeRedis Set,存储所有激活凭证 keycredential_pool:{pool_name}:availableRedis Set,存储当前可独占获取的凭证 keycredential_pool:{pool_name}:failuresRedis Hash,存储key -> failure_countcredential_pool:{pool_name}:lease_ownerRedis Hash,存储key -> ownercredential_pool:{pool_name}:lease_expiryRedis Sorted Set,存储key -> expiry_ts
数据结构关系
flowchart TD
A[items<br/>Hash: key -> json] --> B[active<br/>Set: 所有激活 key]
B --> C[available<br/>Set: 当前可独占获取 key]
A --> D[failures<br/>Hash: key -> failure_count]
A --> E[lease_owner<br/>Hash: key -> owner]
A --> F[lease_expiry<br/>ZSet: key -> expiry_ts]
说明
items是主数据存储。active表示凭证仍在池内。available表示该凭证当前未被独占持有。lease_owner与lease_expiry一起构成租约系统。failures用于业务层实现失败阈值控制。
推荐使用模式
适合使用 random_get()
- JWT 可被多个请求共享使用
- 无状态 Cookie
- 对单个凭证并发使用没有强限制的场景
适合使用 acquire_available()
- 单个凭证同一时间只能被一个 worker 使用
- 代理认证信息
- 有明显并发冲突风险的 Cookie / Token
- 需要“使用后归还”的场景
最佳实践
key设计成全局唯一且可读,例如jwt:1、cookie:user_001kind用于标识凭证类型,例如jwt、cookie、nonce- 独占获取后务必在
finally中执行release() - 对长时间运行任务,建议定期执行
reclaim_expired_leases() - 失败阈值删除策略建议由业务层自行控制,不要在 SDK 核心层写死
License
待补充。
Release files for credential-pool-sdk 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| credential_pool_sdk-1.0.0.tar.gz | 11.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| credential_pool_sdk-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 20.3 kB
Release files / credential_pool_sdk-1.0.0.tar.gz
| Download URL | credential_pool_sdk-1.0.0.tar.gz |
|---|---|
| Size | 11.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
ffb605d6be3b7461cccee134e69e56a48185ada8522eafb671df564f879079f1
|
|
BLAKE2b-256 checksum How to use checksums |
589bb89a8ca9c085abdcc4c8bfecc35fde49da5372783a726c0f4775b65081a1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.0
|
Release files / credential_pool_sdk-1.0.0-py3-none-any.whl
| Download URL | credential_pool_sdk-1.0.0-py3-none-any.whl |
|---|---|
| Size | 8.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
54f70c38d1e272ffa5d795b100a394d270a02f7c15e92798eb0c9cec26553ffd
|
|
BLAKE2b-256 checksum How to use checksums |
b1909d6bbed00e43fc5195a8aeded8f443cec3c5495a8c09f2892dda11750ecc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.12.0
|