初始化
This commit is contained in:
@@ -0,0 +1,434 @@
|
||||
# MAC关联OLT流程改进建议
|
||||
|
||||
## 文档信息
|
||||
- **分析时间**: 2026年4月2日
|
||||
- **分析对象**: `MAC关联OLT流程.md`
|
||||
- **分析重点**: 与系统设计匹配度、40个OLT设备扩展性
|
||||
- **目标读者**: 后端开发同事
|
||||
|
||||
## 一、当前流程分析
|
||||
|
||||
### 1.1 文档描述流程
|
||||
1. 远程连接OLT设备 → 执行`dis onu slot 1`命令
|
||||
2. 解析返回的ONU设备信息
|
||||
3. 提取MAC地址、距离、端口、状态信息
|
||||
4. 将信息展示给前端并关联到OLT
|
||||
|
||||
### 1.2 系统实现现状
|
||||
**匹配度评分**: 70%
|
||||
|
||||
| 功能点 | 文档描述 | 系统实现 | 状态 |
|
||||
|--------|----------|----------|------|
|
||||
| SSH连接 | 远程连接OLT | `SSHService.connect()` | ✅ 已实现 |
|
||||
| 命令执行 | `dis onu slot 1` | `execute_command()` | ✅ 已实现 |
|
||||
| 分页处理 | "---- More ----" | 空格键继续 | ✅ 已实现 |
|
||||
| MAC地址提取 | 1484-778f-cb20格式 | `parse_onu_status()` | ✅ 已实现 |
|
||||
| 状态解析 | Up/Offline | 解析状态字段 | ✅ 已实现 |
|
||||
| 距离提取 | 12384米 | ❌ 未实现 | ❌ 缺失 |
|
||||
| 端口解析 | Onu1/0/2:1 | ❌ 未完整解析 | ⚠️ 需改进 |
|
||||
| 数据关联 | 关联到OLT | `olt_id`外键 | ✅ 已实现 |
|
||||
|
||||
### 1.3 主要问题
|
||||
1. **数据提取不完整**: 当前只解析状态,未提取距离和详细端口信息
|
||||
2. **数据库字段缺失**: 缺少`distance`字段存储距离信息
|
||||
3. **端口解析不足**: 需要从"Onu1/0/2:1"解析为`slot_number`和`port_number`
|
||||
|
||||
## 二、40个OLT设备扩展性挑战
|
||||
|
||||
### 2.1 当前方案问题(串行执行)
|
||||
| 问题 | 影响程度 | 说明 |
|
||||
|------|----------|------|
|
||||
| 执行效率低 | 高 | 40个OLT串行检查,预计20-40分钟 |
|
||||
| SSH连接开销 | 中 | 每个OLT独立连接,建立/断开开销大 |
|
||||
| 网络延迟累积 | 中 | 网络不佳的OLT会阻塞整个流程 |
|
||||
| 资源占用 | 中 | 可能同时维护多个SSH连接 |
|
||||
| 错误传播 | 高 | 一个OLT失败可能影响后续检查 |
|
||||
|
||||
### 2.2 性能预估
|
||||
| 方案 | 40个OLT检查时间 | 资源占用 | 实现复杂度 | 推荐度 |
|
||||
|------|----------------|----------|------------|--------|
|
||||
| 当前串行 | ~20-40分钟 | 低 | 低 | ⭐ |
|
||||
| 并行10并发 | ~2-4分钟 | 中 | 中 | ⭐⭐⭐⭐⭐ |
|
||||
| 并行20并发 | ~1-2分钟 | 高 | 高 | ⭐⭐⭐ |
|
||||
|
||||
## 三、改进方案建议
|
||||
|
||||
### 3.1 方案一:并行处理 + 连接池(推荐)
|
||||
|
||||
#### 3.1.1 架构设计
|
||||
```
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ Celery主任务 │───▶│ OLT检查任务分发 │───▶│ 并行执行检查 │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
|
||||
│ SSH连接池管理 │◄───│ 连接复用机制 │◄───│ 并发控制(10) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
```
|
||||
|
||||
#### 3.1.2 核心组件
|
||||
1. **任务分发器**: 将40个OLT检查拆分为独立Celery任务
|
||||
2. **连接池管理器**: 复用SSH连接,减少建立/断开开销
|
||||
3. **并发控制器**: 限制最大并发数(建议10个)
|
||||
4. **结果聚合器**: 收集各OLT检查结果,统一存储
|
||||
|
||||
#### 3.1.3 代码实现建议
|
||||
|
||||
**A. SSH连接池实现**
|
||||
```python
|
||||
# app/services/ssh_pool.py
|
||||
import paramiko
|
||||
from typing import Dict, Optional
|
||||
from threading import Lock
|
||||
|
||||
class SSHConnectionPool:
|
||||
"""SSH连接池,支持连接复用"""
|
||||
|
||||
def __init__(self, max_connections: int = 10):
|
||||
self.pool: Dict[str, paramiko.SSHClient] = {}
|
||||
self.lock = Lock()
|
||||
self.max_connections = max_connections
|
||||
|
||||
def get_connection(self, host: str, username: str, password: str) -> paramiko.SSHClient:
|
||||
"""获取或创建SSH连接"""
|
||||
key = f"{host}:{username}"
|
||||
|
||||
with self.lock:
|
||||
if key in self.pool:
|
||||
client = self.pool[key]
|
||||
# 检查连接是否仍然有效
|
||||
try:
|
||||
transport = client.get_transport()
|
||||
if transport and transport.is_active():
|
||||
return client
|
||||
except:
|
||||
pass
|
||||
|
||||
# 创建新连接
|
||||
if len(self.pool) >= self.max_connections:
|
||||
# 清理最久未使用的连接
|
||||
self._cleanup_oldest()
|
||||
|
||||
client = paramiko.SSHClient()
|
||||
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
client.connect(hostname=host, username=username, password=password, timeout=30)
|
||||
|
||||
self.pool[key] = client
|
||||
return client
|
||||
|
||||
def _cleanup_oldest(self):
|
||||
"""清理最久未使用的连接"""
|
||||
# 简化实现:清理第一个连接
|
||||
if self.pool:
|
||||
key = next(iter(self.pool))
|
||||
try:
|
||||
self.pool[key].close()
|
||||
except:
|
||||
pass
|
||||
del self.pool[key]
|
||||
```
|
||||
|
||||
**B. 增强的解析器**
|
||||
```python
|
||||
# app/services/onu_parser.py
|
||||
import re
|
||||
from typing import Dict, List, Optional
|
||||
from dataclasses import dataclass
|
||||
|
||||
@dataclass
|
||||
class ONUInfo:
|
||||
"""ONU设备完整信息"""
|
||||
mac_address: str
|
||||
status: str # online/offline
|
||||
distance_m: Optional[int] = None # 距离(米)
|
||||
slot_number: Optional[int] = None # 插槽号
|
||||
port_number: Optional[int] = None # 端口号
|
||||
loid: Optional[str] = None # LOID
|
||||
model: Optional[str] = None # 设备型号
|
||||
|
||||
class ONUParser:
|
||||
"""增强的ONU信息解析器"""
|
||||
|
||||
@staticmethod
|
||||
def parse_output(output: str) -> List[ONUInfo]:
|
||||
"""解析OLT命令输出"""
|
||||
devices = []
|
||||
lines = output.split('\n')
|
||||
|
||||
current_slot = None
|
||||
|
||||
for line in lines:
|
||||
# 检测新的插槽区域
|
||||
slot_match = re.search(r'Olt(\d+)/(\d+)/(\d+)', line)
|
||||
if slot_match:
|
||||
current_slot = int(slot_match.group(1))
|
||||
continue
|
||||
|
||||
# 跳过表头行
|
||||
if 'MAC' in line and 'LOID' in line:
|
||||
continue
|
||||
|
||||
# 解析设备行
|
||||
device = ONUParser._parse_device_line(line, current_slot)
|
||||
if device:
|
||||
devices.append(device)
|
||||
|
||||
return devices
|
||||
|
||||
@staticmethod
|
||||
def _parse_device_line(line: str, slot: Optional[int]) -> Optional[ONUInfo]:
|
||||
"""解析单行设备信息"""
|
||||
# 匹配MAC地址
|
||||
mac_match = re.search(r'([0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4})', line, re.IGNORECASE)
|
||||
if not mac_match:
|
||||
return None
|
||||
|
||||
mac = mac_match.group(1).lower()
|
||||
|
||||
# 提取状态
|
||||
status = 'offline'
|
||||
if 'Up' in line:
|
||||
status = 'online'
|
||||
|
||||
# 提取距离
|
||||
distance = None
|
||||
dist_match = re.search(r'(\d+)\s*Dist\(M\)', line)
|
||||
if dist_match:
|
||||
distance = int(dist_match.group(1))
|
||||
|
||||
# 提取端口信息
|
||||
slot_num, port_num = None, None
|
||||
port_match = re.search(r'Onu(\d+)/(\d+)/(\d+):(\d+)', line)
|
||||
if port_match:
|
||||
slot_num = int(port_match.group(1))
|
||||
port_num = int(port_match.group(4))
|
||||
|
||||
# 提取LOID
|
||||
loid = None
|
||||
loid_match = re.search(r'\s+(\d+)\s+', line)
|
||||
if loid_match:
|
||||
loid = loid_match.group(1)
|
||||
|
||||
return ONUInfo(
|
||||
mac_address=mac,
|
||||
status=status,
|
||||
distance_m=distance,
|
||||
slot_number=slot_num or slot,
|
||||
port_number=port_num,
|
||||
loid=loid
|
||||
)
|
||||
```
|
||||
|
||||
**C. 并行检查任务**
|
||||
```python
|
||||
# app/tasks/parallel_check_tasks.py
|
||||
from celery import group
|
||||
from app.tasks.check_tasks import check_single_olt
|
||||
from app.models.device import OLTDevice
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
def check_all_olts_parallel(db: Session, max_concurrent: int = 10):
|
||||
"""并行检查所有OLT设备"""
|
||||
# 获取所有OLT设备
|
||||
olts = db.query(OLTDevice).all()
|
||||
|
||||
if not olts:
|
||||
return {"message": "没有可检查的OLT设备"}
|
||||
|
||||
# 创建任务组
|
||||
tasks = []
|
||||
for olt in olts:
|
||||
tasks.append(check_single_olt.s(olt.id))
|
||||
|
||||
# 分批执行,控制并发数
|
||||
results = []
|
||||
for i in range(0, len(tasks), max_concurrent):
|
||||
batch = tasks[i:i + max_concurrent]
|
||||
job = group(batch)
|
||||
result = job.apply_async()
|
||||
results.extend(result.get()) # 等待批次完成
|
||||
|
||||
return {
|
||||
"total_olts": len(olts),
|
||||
"completed": len([r for r in results if r.get("success")]),
|
||||
"failed": len([r for r in results if not r.get("success")]),
|
||||
"results": results
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 方案二:数据库优化
|
||||
|
||||
#### 3.2.1 新增字段建议
|
||||
```sql
|
||||
-- 在onu_devices表中新增字段
|
||||
ALTER TABLE onu_devices ADD COLUMN IF NOT EXISTS distance_m INTEGER;
|
||||
ALTER TABLE onu_devices ADD COLUMN IF NOT EXISTS loid VARCHAR(50);
|
||||
ALTER TABLE onu_devices ADD COLUMN IF NOT EXISTS model VARCHAR(100);
|
||||
ALTER TABLE onu_devices ADD COLUMN IF NOT EXISTS last_seen TIMESTAMP;
|
||||
|
||||
-- 在olt_devices表中新增字段
|
||||
ALTER TABLE olt_devices ADD COLUMN IF NOT EXISTS max_concurrent INTEGER DEFAULT 5;
|
||||
ALTER TABLE olt_devices ADD COLUMN IF NOT EXISTS check_timeout INTEGER DEFAULT 60;
|
||||
```
|
||||
|
||||
#### 3.2.2 数据模型更新
|
||||
```python
|
||||
# app/models/device.py - 更新ONUDevice模型
|
||||
class ONUDevice(Base):
|
||||
__tablename__ = "onu_devices"
|
||||
|
||||
# 现有字段...
|
||||
distance_m = Column(Integer) # 新增:距离(米)
|
||||
loid = Column(String(50)) # 新增:LOID
|
||||
model = Column(String(100)) # 新增:设备型号
|
||||
last_seen = Column(TIMESTAMP) # 新增:最后在线时间
|
||||
|
||||
# 索引优化
|
||||
__table_args__ = (
|
||||
Index('idx_mac_olt', 'mac_address', 'olt_id'),
|
||||
Index('idx_status_checked', 'status', 'last_seen'),
|
||||
)
|
||||
```
|
||||
|
||||
### 3.3 方案三:监控与告警增强
|
||||
|
||||
#### 3.3.1 检查任务监控
|
||||
```python
|
||||
# app/services/monitor_service.py
|
||||
class CheckMonitor:
|
||||
"""检查任务监控服务"""
|
||||
|
||||
@staticmethod
|
||||
def get_check_stats(db: Session):
|
||||
"""获取检查统计信息"""
|
||||
stats = {
|
||||
"total_olts": db.query(OLTDevice).count(),
|
||||
"total_onus": db.query(ONUDevice).count(),
|
||||
"online_onus": db.query(ONUDevice)
|
||||
.join(DeviceStatusHistory)
|
||||
.filter(DeviceStatusHistory.status == 'online')
|
||||
.distinct().count(),
|
||||
"recent_checks": db.query(DeviceStatusHistory)
|
||||
.order_by(DeviceStatusHistory.checked_at.desc())
|
||||
.limit(10).all()
|
||||
}
|
||||
return stats
|
||||
|
||||
@staticmethod
|
||||
def check_olt_health(olt: OLTDevice) -> Dict:
|
||||
"""检查OLT健康状态"""
|
||||
# 测试连接
|
||||
ssh = SSHService(olt.ip_address, olt.username, olt.password)
|
||||
try:
|
||||
start_time = time.time()
|
||||
ssh.connect()
|
||||
connect_time = time.time() - start_time
|
||||
|
||||
# 执行简单命令测试
|
||||
output = ssh.execute_command("display version")
|
||||
|
||||
return {
|
||||
"olt_id": olt.id,
|
||||
"status": "healthy",
|
||||
"connect_time": connect_time,
|
||||
"response_time": len(output) / 1024, # KB/s
|
||||
"timestamp": datetime.utcnow()
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
"olt_id": olt.id,
|
||||
"status": "unhealthy",
|
||||
"error": str(e),
|
||||
"timestamp": datetime.utcnow()
|
||||
}
|
||||
finally:
|
||||
ssh.close()
|
||||
```
|
||||
|
||||
## 四、实施计划建议
|
||||
|
||||
### 4.1 第一阶段:基础改进(1-2天)
|
||||
1. 增强ONU信息解析器,提取完整字段
|
||||
2. 更新数据库模型,新增必要字段
|
||||
3. 修改现有检查逻辑,存储完整信息
|
||||
|
||||
### 4.2 第二阶段:并行化改造(3-5天)
|
||||
1. 实现SSH连接池
|
||||
2. 改造Celery任务为并行执行
|
||||
3. 添加并发控制和错误处理
|
||||
4. 测试10个OLT并发场景
|
||||
|
||||
### 4.3 第三阶段:优化与监控(2-3天)
|
||||
1. 添加检查任务监控
|
||||
2. 实现健康检查机制
|
||||
3. 优化数据库查询性能
|
||||
4. 添加告警通知功能
|
||||
|
||||
### 4.4 第四阶段:压力测试(1-2天)
|
||||
1. 模拟40个OLT并发检查
|
||||
2. 测试网络异常情况
|
||||
3. 验证系统稳定性
|
||||
4. 性能调优
|
||||
|
||||
## 五、风险与应对
|
||||
|
||||
### 5.1 技术风险
|
||||
| 风险 | 可能性 | 影响 | 应对措施 |
|
||||
|------|--------|------|----------|
|
||||
| OLT连接超时 | 中 | 高 | 实现超时重试机制 |
|
||||
| 并发连接过多 | 低 | 中 | 限制最大并发数 |
|
||||
| 内存泄漏 | 低 | 高 | 添加连接池清理机制 |
|
||||
| 数据库锁竞争 | 中 | 中 | 优化事务处理,使用批量操作 |
|
||||
|
||||
### 5.2 业务风险
|
||||
| 风险 | 可能性 | 影响 | 应对措施 |
|
||||
|------|--------|------|----------|
|
||||
| 检查结果不一致 | 低 | 中 | 添加数据校验机制 |
|
||||
| 检查时间过长 | 中 | 高 | 实现增量检查,优化查询 |
|
||||
| 用户等待时间 | 中 | 中 | 提供进度查询接口 |
|
||||
|
||||
## 六、预期收益
|
||||
|
||||
### 6.1 性能提升
|
||||
- **检查时间**: 从20-40分钟缩短到2-4分钟(10倍提升)
|
||||
- **资源利用率**: SSH连接复用减少50%连接开销
|
||||
- **系统稳定性**: 错误隔离避免单点故障影响全局
|
||||
|
||||
### 6.2 功能增强
|
||||
- **数据完整性**: 完整记录距离、端口、LOID等信息
|
||||
- **监控能力**: 实时监控检查任务状态和OLT健康度
|
||||
- **扩展性**: 支持未来扩展到更多OLT设备
|
||||
|
||||
### 6.3 运维改善
|
||||
- **故障诊断**: 详细的检查日志和错误信息
|
||||
- **性能分析**: 检查任务性能统计和趋势分析
|
||||
- **容量规划**: 基于实际负载的资源规划依据
|
||||
|
||||
## 七、后续建议
|
||||
|
||||
### 7.1 短期建议(1个月内)
|
||||
1. 实现基础并行检查功能
|
||||
2. 完成数据库字段扩展
|
||||
3. 添加基础监控告警
|
||||
|
||||
### 7.2 中期建议(1-3个月)
|
||||
1. 实现智能调度(基于OLT负载和网络状况)
|
||||
2. 添加检查结果分析和报表
|
||||
3. 集成自动化运维工具
|
||||
|
||||
### 7.3 长期建议(3-6个月)
|
||||
1. 机器学习预测设备故障
|
||||
2. 自动化故障恢复机制
|
||||
3. 多数据中心部署支持
|
||||
|
||||
---
|
||||
|
||||
**文档维护**:
|
||||
- 本文档应随系统改进同步更新
|
||||
- 重大架构变更需更新本文档
|
||||
- 实际实施中的调整应记录在案
|
||||
|
||||
**联系方式**:
|
||||
- 如有疑问或建议,请通过项目沟通渠道反馈
|
||||
- 实施过程中遇到问题及时记录并分享解决方案
|
||||
Reference in New Issue
Block a user