377 lines
13 KiB
Markdown
377 lines
13 KiB
Markdown
# GVS-DSDK — GVS Distributed SDK
|
||
|
||
> **薄封装层**:直接包装 `gvsds/apps/msync` 原生层,作为子服务器(如 `ATestCServer`)和主服务器(`gvsds`)之间共享的通信桥梁。
|
||
>
|
||
> **设计原则**:所有方法名 1:1 对应原生层 PascalCase;零业务逻辑;纯转发。
|
||
>
|
||
> **双端版本严格一致**:主从两端必须使用同一 SDK 版本(当前 `1.0.0`),通过 `pyproject.toml` 锁定。
|
||
|
||
---
|
||
|
||
## 1. 简介
|
||
|
||
`gvsdsdk` 把 `gvsds/apps/msync` 的**所有公共类/函数**原样转发导出,使得:
|
||
|
||
- **主服务器**(`gvsds`):可继续使用自己的 `gvsds.apps.msync` 模块
|
||
- **子服务器**(`ATestCServer`):可从 `gvsdsdk` 导入**完全相同的 API**,但**不依赖 gvsds 模型层**
|
||
- **第三方服务**:可以独立引入 `gvsdsdk` 即可使用全部 MSYNC 能力
|
||
|
||
被转发的原生模块:
|
||
|
||
| SDK 模块 | 包装的原生模块 | 提供的类/函数 |
|
||
|---|---|---|
|
||
| `gvsdsdk.auth` | `gvsds.apps.msync.auth` | `SignPayload`, `GenerateChallenge`, `BuildAuthPayload`, `AuthenticateService`, `ServiceTokenEncoder`, … |
|
||
| `gvsdsdk.registry` | `gvsds.apps.msync.registry` | `ServiceRegistry` |
|
||
| `gvsdsdk.ogm` | `gvsds.apps.msync.ogm` | `OGMManager` |
|
||
| `gvsdsdk.transaction` | `gvsds.apps.msync.transaction` | `TransactionManager` |
|
||
| `gvsdsdk.local_tx` | `gvsds.apps.msync.local_tx` | `LocalTransactionManager`, `StepHandler`, `local_tx` |
|
||
| `gvsdsdk.client` | `gvsds.apps.msync.client` | `ServiceClient` |
|
||
|
||
每个类在 SDK 与原生层是**完全同一个对象**(`is` 比较为 `True`),无任何运行时包装开销。
|
||
|
||
## 2. 安装
|
||
|
||
子服务器(如 ATestCServer)项目根目录的 `manage.py` 已自动添加 `gvsdsdk` 的父目录到 `sys.path`:
|
||
|
||
```python
|
||
# manage.py:5
|
||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
```
|
||
|
||
无需额外配置。
|
||
|
||
**独立使用**(非 gvsds 生态项目):
|
||
|
||
```bash
|
||
$env:PYTHONPATH = "D:\SMANAGE\gvsdsdk"
|
||
# 或 Linux/macOS
|
||
export PYTHONPATH=/path/to/SMANAGE/gvsdsdk
|
||
```
|
||
|
||
依赖:
|
||
- **核心**:`gvsds.apps.msync`(位于 `gvsds/` 项目内)
|
||
- **Python**:仅使用标准库
|
||
|
||
## 3. 目录结构
|
||
|
||
```
|
||
gvsdsdk/
|
||
├── __init__.py # 公共导出(re-export)
|
||
├── auth.py # → gvsds.apps.msync.auth
|
||
├── registry.py # → gvsds.apps.msync.registry
|
||
├── ogm.py # → gvsds.apps.msync.ogm
|
||
├── transaction.py # → gvsds.apps.msync.transaction
|
||
├── local_tx.py # → gvsds.apps.msync.local_tx
|
||
└── client.py # → gvsds.apps.msync.client
|
||
```
|
||
|
||
每个文件只有 5-25 行代码,纯粹是 `from gvsds.apps.msync.X import Y` 的重新导出。
|
||
|
||
## 4. 核心 API(PascalCase)
|
||
|
||
### 4.1 认证 (gvsdsdk.auth)
|
||
|
||
| 函数 / 类 | 描述 |
|
||
|---|---|
|
||
| `SignPayload(payload, secret)` | HMAC-SHA256 签名 |
|
||
| `VerifySignature(payload, signature, secret)` | 验签 |
|
||
| `GenerateChallenge()` | 生成挑战字符串 |
|
||
| `BuildAuthPayload(service, challenge, ts=None)` | 构造认证载荷 |
|
||
| `SignAuthPayload(payload, secret)` | 一站式签名 |
|
||
| `VerifyAuthPayload(payload, signature, secret)` | 一站式验签 |
|
||
| `ServiceTokenEncoder` | JWT 风格令牌编解码 |
|
||
| `IssueServiceToken(service_node, ...)` | 签发令牌 |
|
||
| `AuthenticateService(service_name, signed_payload, signature)` | 完整认证 |
|
||
| `ValidateAccessToken(access_token)` | 校验访问令牌 |
|
||
| `RefreshServiceToken(refresh_token)` | 刷新令牌 |
|
||
|
||
### 4.2 注册中心 (gvsdsdk.registry)
|
||
|
||
```python
|
||
from gvsdsdk import ServiceRegistry
|
||
|
||
node = ServiceRegistry.Register(
|
||
Name='my-service',
|
||
Domain='http://127.0.0.1:8080',
|
||
Secret='shared-secret',
|
||
Scope=['ogm:read', 'ogm:write'],
|
||
ServiceType=0, # 0=Worker, 1=Master
|
||
Metadata={'region': 'cn-east'},
|
||
)
|
||
|
||
ServiceRegistry.Heartbeat('my-service')
|
||
ServiceRegistry.Discover('my-service')
|
||
ServiceRegistry.DiscoverAll(ActiveOnly=True)
|
||
ServiceRegistry.UpdateScope('my-service', ['ogm:read'])
|
||
ServiceRegistry.RotateSecret('my-service', 'new-secret')
|
||
ServiceRegistry.Deregister('my-service')
|
||
```
|
||
|
||
### 4.3 OGM (gvsdsdk.ogm)
|
||
|
||
```python
|
||
from gvsdsdk import OGMManager
|
||
|
||
entry = OGMManager.Register(
|
||
ObjectType='user',
|
||
LocalID='123',
|
||
ServiceName='my-service',
|
||
GlobalID=None, # 可选,自动生成
|
||
Metadata={'username': 'tom'},
|
||
)
|
||
|
||
info = OGMManager.Resolve(entry['GlobalID'])
|
||
local = OGMManager.ResolveLocal('user', '123', 'my-service')
|
||
by_type = OGMManager.QueryByType('user')
|
||
by_svc = OGMManager.QueryByService('my-service')
|
||
OGMManager.Update(entry['GlobalID'], metadata={'vip': True})
|
||
OGMManager.Deregister(entry['GlobalID'])
|
||
```
|
||
|
||
### 4.4 分布式事务 (gvsdsdk.transaction)
|
||
|
||
```python
|
||
from gvsdsdk import TransactionManager
|
||
|
||
dtx = TransactionManager.Create(
|
||
Name='create-order',
|
||
StepsData=[
|
||
{'ServiceName': 'svc-a', 'StepName': 'create_user', 'Payload': {...}},
|
||
{'ServiceName': 'svc-b', 'StepName': 'create_wallet', 'Payload': {...}},
|
||
],
|
||
Description='创建订单事务',
|
||
TransactionType='saga',
|
||
InitiatorServiceName='my-service',
|
||
TimeoutSeconds=300,
|
||
)
|
||
|
||
TransactionManager.Execute(dtx.TransactionCode, access_token=token)
|
||
TransactionManager.Compensate(dtx.TransactionCode)
|
||
TransactionManager.Cancel(dtx.TransactionCode)
|
||
TransactionManager.GetDetail(dtx.TransactionCode)
|
||
TransactionManager.RetryStep(dtx.TransactionCode, step_index=0)
|
||
TransactionManager.CheckTimeouts()
|
||
TransactionManager.QueryByStatus(status=2) # Committed
|
||
```
|
||
|
||
### 4.5 本地事务 (gvsdsdk.local_tx)
|
||
|
||
```python
|
||
from gvsdsdk import local_tx
|
||
|
||
def create_user(payload):
|
||
user_id = do_something(payload)
|
||
return {'UserID': user_id}
|
||
|
||
def delete_user(payload, result):
|
||
undo_something(result['UserID'])
|
||
|
||
local_tx.register('create_user', action=create_user, compensate=delete_user)
|
||
```
|
||
|
||
### 4.6 服务客户端 (gvsdsdk.client)
|
||
|
||
```python
|
||
from gvsdsdk import ServiceClient
|
||
|
||
client = ServiceClient(
|
||
ServiceName='my-service',
|
||
ServiceSecret='shared-secret',
|
||
MasterURL='https://api.gvsds.com',
|
||
)
|
||
|
||
client.Authenticate()
|
||
info = client.DiscoverService('gerp')
|
||
ogm = client.ResolveOGM('user:abc123')
|
||
resp = client.Request('GET', 'https://gerp.gvsds.com/api/x', scope=['ogm:read'])
|
||
```
|
||
|
||
## 5. Socket.IO 事件协议(PascalCase)
|
||
|
||
> **重要**:所有事件的请求/响应字段全部使用 **PascalCase**。
|
||
|
||
| 事件 | 请求字段 | 响应字段 |
|
||
|---|---|---|
|
||
| `msync:register` | `ServiceName`, `Domain`, `Secret`, `Scope`, `ServiceType`, `Metadata` | `ServiceNode` 序列化 |
|
||
| `msync:auth` | `ServiceName`, `Payload`, `Signature` | `AccessToken`, `RefreshToken`, `Scope`, `ExpiresAt` |
|
||
| `msync:auth:refresh` | `RefreshToken` | 同上 |
|
||
| `msync:heartbeat` | — | — |
|
||
| `msync:deregister` | `ServiceName` | — |
|
||
| `msync:discover` | `ActiveOnly` | `services[]` |
|
||
| `msync:discover:one` | `ServiceName` | `service` |
|
||
| `msync:ogm:register` | `ObjectType`, `LocalID`, `ServiceName`, `GlobalID`, `Metadata` | `ogm` |
|
||
| `msync:ogm:batch` | `Items[]`, `ServiceName` | `ogms[]` |
|
||
| `msync:ogm:resolve` | `GlobalID` | `ogm` |
|
||
| `msync:ogm:query` | `ObjectType`/`ServiceName`/`LocalID` | `results` |
|
||
| `msync:ogm:update` | `GlobalID`, `Metadata` | `ogm` |
|
||
| `msync:ogm:deregister` | `GlobalID` | — |
|
||
| `msync:tx:create` | `Name`, `Steps[]`, `Description`, `TransactionType`, `InitiatorServiceName`, `ReferenceOGMUUID`, `Payload`, `TimeoutSeconds` | `dtx` |
|
||
| `msync:tx:execute` | `TransactionCode`, `AccessToken` | `dtx` |
|
||
| `msync:tx:compensate` | 同上 | `dtx` |
|
||
| `msync:tx:cancel` | `TransactionCode` | — |
|
||
| `msync:tx:detail` | `TransactionCode` | `dtx` |
|
||
| `msync:tx:retry` | `TransactionCode`, `StepIndex`, `AccessToken` | `step` |
|
||
| `msync:tx:timeout_check` | — | `TimedOut[]` |
|
||
| `msync:tx:query` | `Status` | `dtxs[]` |
|
||
|
||
服务端推送:
|
||
|
||
- `msync:service:online` — `{Service, Domain}`
|
||
- `msync:service:offline` — `{Service}`
|
||
|
||
## 6. 快速上手
|
||
|
||
### 6.1 子服务器(ATestCServer)使用 SDK
|
||
|
||
[ATestCServer/msync_client/sio_client.py](file:///d:/SMANAGE/ATestCServer/msync_client/sio_client.py) 提供了 `MSyncSocketClient`,**已全部 PascalCase**:
|
||
|
||
```python
|
||
from msync_client.sio_client import MSyncSocketClient
|
||
|
||
msync = MSyncSocketClient(
|
||
master_url='https://www.gvsds.com',
|
||
service_name='atest-cserver',
|
||
service_domain='http://127.0.0.1:25080',
|
||
service_secret='shared-secret',
|
||
scope=['ogm:read', 'ogm:write', 'registry:read'],
|
||
)
|
||
msync.Connect()
|
||
|
||
# OGM
|
||
ogm = msync.OGMRegister('user', '123', 'atest-cserver')
|
||
resolved = msync.OGMResolve(ogm['GlobalID'])
|
||
|
||
# 服务发现
|
||
for svc in msync.DiscoverAll():
|
||
print(svc['ServiceName'], '->', svc['Domain'])
|
||
|
||
# 状态
|
||
print('已认证:', msync.IsAuthenticated)
|
||
print('AccessToken:', msync.AccessToken[:20], '...')
|
||
|
||
msync.Disconnect()
|
||
```
|
||
|
||
### 6.2 直接使用 SDK 类
|
||
|
||
```python
|
||
from gvsdsdk import ServiceRegistry, OGMManager, TransactionManager
|
||
|
||
# 注册中心
|
||
ServiceRegistry.Register(
|
||
Name='my-service',
|
||
Domain='http://localhost:8080',
|
||
Secret='secret',
|
||
)
|
||
|
||
# OGM
|
||
entry = OGMManager.Register('user', '1', 'my-service')
|
||
|
||
# 事务
|
||
dtx = TransactionManager.Create(
|
||
Name='test-tx',
|
||
StepsData=[{'ServiceName': 'svc-a', 'StepName': 'step1'}],
|
||
)
|
||
```
|
||
|
||
## 7. 架构示意
|
||
|
||
```
|
||
┌──────────────────┐ ┌──────────────────┐
|
||
│ ATestCServer │ │ gvsds (master) │
|
||
│ │ │ │
|
||
│ MSyncSocketClient│ ←──┐ │ Socket.IO │
|
||
│ (PascalCase) │ │ │ Server │
|
||
│ │ │ │ (PascalCase) │
|
||
│ ┌────────────┐ │ │ │ │
|
||
│ │ gvsdsdk │ │ │ │ ┌────────────┐ │
|
||
│ │ .auth │◄─┼─────┼───┼──│ msync │ │
|
||
│ │ .ogm │ │ │ │ │ .auth │ │
|
||
│ │ .registry │ │ │ │ │ .ogm │ │
|
||
│ │ .tx │ │ │ │ │ .registry │ │
|
||
│ │ .client │ │ │ │ │ .tx │ │
|
||
│ └─────┬──────┘ │ │ │ └─────┬──────┘ │
|
||
│ │ │ │ │ │ │
|
||
│ 薄封装 │ │ │ 原生层 │
|
||
│ ▼ │ │ │ ▼ │
|
||
│ ┌────────────┐ │ │ │ ┌────────────┐ │
|
||
│ │ gvsds │ │ │ │ │ gvsds │ │
|
||
│ │ .apps │◄─┼─────┘ │ │ .apps │ │
|
||
│ │ .msync │ │ │ │ .msync │ │
|
||
│ └────────────┘ │ │ └────────────┘ │
|
||
└──────────────────┘ └──────────────────┘
|
||
```
|
||
|
||
`gvsdsdk` 与 `gvsds.apps.msync` 中的类是**完全同一个对象**——`from gvsdsdk import ServiceRegistry` 与 `from gvsds.apps.msync.registry import ServiceRegistry` 拿到的是同一个类。
|
||
|
||
## 8. 与项目原生层的集成
|
||
|
||
### 8.1 ATestCServer
|
||
|
||
[ATestCServer/manage.py:5](file:///d:/SMANAGE/ATestCServer/manage.py#L5) 在启动时设置 `sys.path`:
|
||
|
||
```python
|
||
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||
# → D:\SMANAGE 被加入 sys.path
|
||
# → import gvsdsdk 即可工作
|
||
```
|
||
|
||
子服务器**不直接 import** `gvsds.apps.msync`(避免 Django settings 依赖),仅通过 SDK 引用。
|
||
|
||
### 8.2 gvsds 主服务器
|
||
|
||
[gvsds/settings.py](file:///d:/SMANAGE/gvsds/settings.py) 启动时同样将 `gvsdsdk` 加入 `sys.path`:
|
||
|
||
```python
|
||
# 在 wsgi.py / asgi.py 中
|
||
sys.path.append(BASE_DIR)
|
||
```
|
||
|
||
`gvsds.apps.msync` 内部模块(如 [sio.py](file:///d:/SMANAGE/gvsds/apps/msync/sio.py))会**直接 import** 原生层 `gvsds.apps.msync.auth/registry/ogm/transaction`,**协议 100% 兼容**。
|
||
|
||
## 9. 测试与验证
|
||
|
||
```python
|
||
import django; django.setup()
|
||
import gvsdsdk
|
||
|
||
# 验证 SDK 类与原生层是同一对象
|
||
from gvsdsdk import ServiceRegistry, OGMManager, TransactionManager
|
||
from gvsds.apps.msync.registry import ServiceRegistry as NativeSR
|
||
from gvsds.apps.msync.ogm import OGMManager as NativeOGM
|
||
from gvsds.apps.msync.transaction import TransactionManager as NativeTM
|
||
|
||
assert ServiceRegistry is NativeSR
|
||
assert OGMManager is NativeOGM
|
||
assert TransactionManager is NativeTM
|
||
print('SDK 薄封装校验通过')
|
||
```
|
||
|
||
## 10. 版本与兼容性
|
||
|
||
| 项目 | 版本 |
|
||
|---|---|
|
||
| **gvsdsdk** | 1.0.0 |
|
||
| **协议版本** | v1.0 (PascalCase 严格模式) |
|
||
| **Python** | >= 3.8 |
|
||
| **兼容 gvsds** | >= 1.0 |
|
||
| **兼容 ATestCServer** | >= 1.0 |
|
||
|
||
## 11. 错误码速查
|
||
|
||
| 错误码 | 含义 | 建议处理 |
|
||
|---|---|---|
|
||
| 400 | 参数错误 | 检查请求体字段名(PascalCase) |
|
||
| 401 | 认证失败 | 检查 `ServiceSecret` |
|
||
| 404 | 资源不存在 | 重新注册服务 |
|
||
| 409 | 冲突 | OGM 已注册、服务重名等 |
|
||
| 500 | 服务器内部错误 | 查看服务器日志 |
|
||
|
||
## 12. 维护
|
||
|
||
- **主服务器**:`gvsds/apps/msync/`(业务实现)
|
||
- **SDK 仓库**:`gvsdsdk/`(薄封装)
|
||
- **子服务器示例**:`ATestCServer/msync_client/`
|
||
|
||
修改原生层时,**SDK 无需改动**——所有类/函数会通过 `from … import …` 自动反映最新行为。
|