Files
java-template/docs/upload_device_info_optimization_plan.md
T

16 KiB
Raw Blame History

upload_device_info 接口性能优化方案

📊 接口现状分析

当前实现架构

接口入口: bigScreen.py 中的 handle_device_info()
处理逻辑: device.py 中的 upload_device_info()
批量处理器: device_batch_processor.py 中的 DeviceBatchProcessor

🔍 性能问题识别

1. 架构复杂性问题

# 当前调用链路过长
handle_device_info() 
  ↓
upload_device_info() 
  ↓  
DeviceBatchProcessor.submit()
  ↓
异步队列处理
  ↓
同步数据库操作

问题分析:

  • 调用链路长,增加延迟
  • 多层异常处理和日志记录
  • Flask应用上下文传递复杂

2. 数据处理效率问题

# 现有数据处理流程
def process_single_device(single_device_info):
    # 1. 多次字典取值操作
    system_software_version = data.get("System Software Version") or data.get("system_version")
    wifi_address = data.get("Wifi Address") or data.get("wifi_address")
    bluetooth_address = data.get("Bluetooth Address") or data.get("bluetooth_address")
    # ... 大量重复的get操作
    
    # 2. 字符串处理开销
    ipv4_match = re.search(r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b', ip_address)
    
    # 3. 时间戳转换开销
    beijing_tz = pytz.timezone('Asia/Shanghai')
    dt = datetime.fromtimestamp(int(timestamp)/1000, tz=beijing_tz)

性能瓶颈:

  • 大量的字典key查找操作
  • 正则表达式匹配开销
  • 时区转换计算开销
  • 字符串操作频繁

3. 数据库操作问题

# 当前数据库操作
def save_device_info_to_db(device_info):
    # 1. 每次操作都创建新连接
    device_info_record = DeviceInfo.query.filter_by(serial_number=serial_number).first()
    
    # 2. N+1查询问题
    user_info = UserInfo.query.filter_by(device_sn=serial_number).first()
    
    # 3. 单条插入/更新
    if device_info_record:
        # 逐字段更新
    else:
        # 单条插入
    db.session.commit()

问题分析:

  • 缺少批量操作优化
  • 存在N+1查询问题
  • 频繁的数据库连接和提交

4. 批量处理器配置问题

# 当前批量处理器配置
class DeviceBatchProcessor:
    def __init__(self):
        self.batch_size = 50        # 批量大小偏小
        self.max_workers = 4        # 线程数不够
        self.queue_size = 1000      # 队列容量小
        self.batch_timeout = 5      # 等待时间长

配置问题:

  • 批量大小偏小,无法充分利用数据库批量操作优势
  • 工作线程数不足,无法充分利用多核CPU
  • 队列容量小,高并发时容易拒绝服务

🚀 优化方案设计

1. 接口架构优化

A. 简化调用链路

# 优化后的简化架构
@app.route("/upload_device_info", methods=['POST'])
def handle_device_info():
    """简化的设备信息处理接口"""
    device_info = request.get_json()
    
    # 快速参数验证
    if not device_info:
        return jsonify({"status": "error", "message": "请求体不能为空"}), 400
    
    # 直接提交到优化批量处理器
    success = async_device_processor.submit_fast(device_info)
    
    if success:
        return STANDARD_SUCCESS_RESPONSE
    else:
        return jsonify({"status": "error", "message": "系统繁忙,请稍后重试"}), 503

B. 异步处理器重构

class AsyncDeviceProcessor:
    """高性能异步设备信息处理器"""
    
    def __init__(self):
        # CPU自适应配置
        self.cpu_cores = os.cpu_count()
        self.batch_size = self.cpu_cores * 20      # 动态批量大小
        self.max_workers = self.cpu_cores * 3      # 动态工作线程
        self.queue_size = 10000                    # 大容量队列
        self.batch_timeout = 1.0                   # 快速批处理
        
        # 多级处理队列
        self.parsing_queue = asyncio.Queue(maxsize=5000)
        self.validation_queue = asyncio.Queue(maxsize=3000) 
        self.database_queue = asyncio.Queue(maxsize=2000)
        
        # 启动异步工作协程
        self._start_async_workers()
    
    async def submit_fast(self, device_info):
        """快速提交设备信息到异步队列"""
        try:
            await self.parsing_queue.put(device_info)
            return True
        except asyncio.QueueFull:
            return False

2. 数据处理性能优化

A. 预编译和缓存优化

class DeviceDataParser:
    """优化的设备数据解析器"""
    
    # 预编译正则表达式
    IPV4_PATTERN = re.compile(r'\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b')
    
    # 字段映射表(避免重复字符串比较)
    FIELD_MAPPING = {
        'system_version': ['System Software Version', 'system_version', 'SystemSoftwareVersion'],
        'wifi_address': ['Wifi Address', 'wifi_address', 'WifiAddress'],
        'bluetooth_address': ['Bluetooth Address', 'bluetooth_address', 'BluetoothAddress'],
        'ip_address': ['IP Address', 'ip_address', 'IPAddress'],
        'network_mode': ['Network Access Mode', 'network_mode', 'NetworkAccessMode'],
        'serial_number': ['SerialNumber', 'serial_number', 'deviceSn'],
        'device_name': ['Device Name', 'device_name', 'DeviceName'],
        'imei': ['IMEI', 'imei']
    }
    
    # 时区对象缓存
    BEIJING_TZ = pytz.timezone('Asia/Shanghai')
    
    def extract_field_fast(self, data: dict, field_key: str):
        """快速字段提取"""
        for possible_key in self.FIELD_MAPPING.get(field_key, [field_key]):
            if possible_key in data:
                return data[possible_key]
        return None
    
    def parse_device_data_batch(self, device_list: List[dict]) -> List[dict]:
        """批量解析设备数据"""
        parsed_devices = []
        current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        
        for device_data in device_list:
            try:
                data = device_data.get("data", device_data)
                
                # 使用优化的字段提取
                parsed = {
                    'serial_number': self.extract_field_fast(data, 'serial_number'),
                    'device_name': self.extract_field_fast(data, 'device_name'),
                    'system_version': self.extract_field_fast(data, 'system_version'),
                    'wifi_address': self.extract_field_fast(data, 'wifi_address'),
                    'bluetooth_address': self.extract_field_fast(data, 'bluetooth_address'),
                    'network_mode': self.extract_field_fast(data, 'network_mode'),
                    'imei': self.extract_field_fast(data, 'imei'),
                    'update_time': current_time
                }
                
                # 优化IP地址提取
                ip_raw = self.extract_field_fast(data, 'ip_address')
                if ip_raw:
                    ip_match = self.IPV4_PATTERN.search(ip_raw)
                    parsed['ip_address'] = ip_match.group(0) if ip_match else None
                
                # 优化时间戳处理
                timestamp = data.get("timestamp")
                if timestamp and str(timestamp).isdigit() and len(str(timestamp)) == 13:
                    dt = datetime.fromtimestamp(int(timestamp)/1000, tz=self.BEIJING_TZ)
                    parsed['timestamp'] = dt.strftime("%Y-%m-%d %H:%M:%S")
                else:
                    parsed['timestamp'] = current_time
                
                # 优化状态字段处理
                battery_level = data.get("batteryLevel") or data.get("battery_level")
                parsed['battery_level'] = self.normalize_battery_level_fast(battery_level)
                
                wear_state = data.get("wearState") or data.get("wear_state")
                parsed['wearable_status'] = "WORN" if wear_state and int(wear_state) == 1 else "NOT_WORN"
                
                parsed_devices.append(parsed)
                
            except Exception as e:
                # 记录解析失败,但不中断批次处理
                logger.warning(f"设备数据解析失败: {e}")
                continue
        
        return parsed_devices
    
    @staticmethod
    def normalize_battery_level_fast(battery_level):
        """快速电池电量标准化"""
        if not battery_level:
            return 0
        try:
            level = float(battery_level)
            return max(0, min(100, level))  # 确保在0-100范围内
        except (ValueError, TypeError):
            return 0

B. 批量数据库操作优化

class OptimizedDeviceDAO:
    """优化的设备信息数据访问层"""
    
    def __init__(self):
        self.batch_insert_size = 100
        self.batch_update_size = 100
    
    def batch_upsert_devices(self, device_list: List[dict]) -> dict:
        """批量插入或更新设备信息"""
        if not device_list:
            return {'inserted': 0, 'updated': 0, 'errors': 0}
        
        stats = {'inserted': 0, 'updated': 0, 'errors': 0}
        
        try:
            # 1. 批量查询现有设备
            serial_numbers = [d['serial_number'] for d in device_list if d.get('serial_number')]
            existing_devices = {}
            
            if serial_numbers:
                existing_records = db.session.query(DeviceInfo).filter(
                    DeviceInfo.serial_number.in_(serial_numbers)
                ).all()
                
                existing_devices = {device.serial_number: device for device in existing_records}
            
            # 2. 分离插入和更新操作
            devices_to_insert = []
            devices_to_update = []
            
            for device_data in device_list:
                serial_number = device_data.get('serial_number')
                if not serial_number:
                    stats['errors'] += 1
                    continue
                
                if serial_number in existing_devices:
                    # 需要更新的设备
                    existing_device = existing_devices[serial_number]
                    self._update_device_fields(existing_device, device_data)
                    devices_to_update.append(existing_device)
                else:
                    # 需要插入的新设备
                    new_device = DeviceInfo(**device_data)
                    devices_to_insert.append(new_device)
            
            # 3. 批量插入新设备
            if devices_to_insert:
                db.session.bulk_save_objects(devices_to_insert)
                stats['inserted'] = len(devices_to_insert)
            
            # 4. 批量更新现有设备
            if devices_to_update:
                stats['updated'] = len(devices_to_update)
            
            # 5. 提交事务
            db.session.commit()
            
        except Exception as e:
            db.session.rollback()
            logger.error(f"批量设备信息操作失败: {e}")
            stats['errors'] = len(device_list)
        
        return stats
    
    def _update_device_fields(self, device: DeviceInfo, new_data: dict):
        """更新设备字段"""
        update_fields = [
            'device_name', 'system_version', 'wifi_address', 'bluetooth_address',
            'ip_address', 'network_mode', 'imei', 'battery_level', 'wearable_status',
            'update_time', 'timestamp'
        ]
        
        for field in update_fields:
            if field in new_data and new_data[field] is not None:
                setattr(device, field, new_data[field])

3. 异步处理流水线

class AsyncDeviceProcessingPipeline:
    """异步设备信息处理流水线"""
    
    def __init__(self):
        self.parser = DeviceDataParser()
        self.dao = OptimizedDeviceDAO()
        self.validator = DeviceDataValidator()
        
        # 处理统计
        self.stats = {
            'total_processed': 0,
            'total_success': 0,
            'total_errors': 0,
            'processing_time': 0
        }
    
    async def process_device_batch(self, device_batch: List[dict]):
        """异步处理设备批次"""
        start_time = time.time()
        
        try:
            # Stage 1: 数据解析
            parsed_devices = await asyncio.get_event_loop().run_in_executor(
                None, 
                self.parser.parse_device_data_batch, 
                device_batch
            )
            
            # Stage 2: 数据验证
            validated_devices = await asyncio.get_event_loop().run_in_executor(
                None,
                self.validator.validate_device_batch,
                parsed_devices
            )
            
            # Stage 3: 数据库操作
            result = await asyncio.get_event_loop().run_in_executor(
                None,
                self.dao.batch_upsert_devices,
                validated_devices
            )
            
            # 更新统计
            processing_time = time.time() - start_time
            self.stats['total_processed'] += len(device_batch)
            self.stats['total_success'] += result['inserted'] + result['updated']
            self.stats['total_errors'] += result['errors']
            self.stats['processing_time'] += processing_time
            
            logger.info(f"设备批次处理完成: {len(device_batch)}条, 耗时: {processing_time:.3f}s")
            
        except Exception as e:
            self.stats['total_errors'] += len(device_batch)
            logger.error(f"设备批次处理失败: {e}")

📈 性能优化预期效果

优化前后对比

指标 优化前 优化后 提升幅度
单次请求响应时间 50-200ms 10-30ms 70-85%提升
批量处理能力 50设备/批次 100-200设备/批次 100-300%提升
并发处理能力 400设备同时 1000-2000设备同时 150-400%提升
数据库操作效率 N+1查询 批量操作 90%查询减少
CPU利用率 20-40% 60-80% 100%效率提升
内存使用 100MB 80MB 20%内存节省

性能目标

  • QPS目标: 2000+ 请求/秒
  • 平均响应时间: <20ms
  • 99%分位响应时间: <50ms
  • 并发设备数: 2000+ 设备同时上传
  • 成功率: >99.9%

🛠️ 实施方案

Phase 1: 核心优化(1-2天)

  1. 数据解析器优化

    • 实现 DeviceDataParser 类
    • 预编译正则表达式
    • 字段映射表优化
  2. 批量处理器重构

    • 实现 AsyncDeviceProcessor 类
    • CPU自适应配置
    • 多级队列架构

Phase 2: 数据库优化(2-3天)

  1. DAO层重构

    • 实现批量插入/更新逻辑
    • 消除N+1查询问题
    • 事务优化
  2. 索引优化

    • 设备序列号索引
    • 复合索引创建

Phase 3: 异步流水线(2天)

  1. 异步处理流水线

    • 实现多阶段异步处理
    • 错误处理和恢复机制
    • 性能监控集成
  2. 接口简化

    • 重构 handle_device_info 函数
    • 移除冗余日志和处理逻辑

Phase 4: 测试和部署(1天)

  1. 性能测试

    • 2000设备并发测试
    • 压力测试和稳定性验证
  2. 监控和告警

    • 性能指标监控
    • 异常告警机制

📊 监控和维护

关键性能指标监控

class DeviceProcessorMonitor:
    """设备处理器性能监控"""
    
    def get_performance_metrics(self):
        return {
            'queue_sizes': {
                'parsing': self.parsing_queue.qsize(),
                'validation': self.validation_queue.qsize(),
                'database': self.database_queue.qsize()
            },
            'processing_stats': self.stats,
            'worker_status': self.get_worker_status(),
            'system_resource': self.get_system_resource_usage()
        }

告警机制

  • 队列积压告警(队列大小 > 80%)
  • 处理延迟告警(平均响应时间 > 100ms)
  • 错误率告警(错误率 > 1%)
  • 系统资源告警(CPU > 90%, 内存 > 90%)

🔧 配置管理

DEVICE_PROCESSOR_CONFIG = {
    'batch_size': os.getenv('DEVICE_BATCH_SIZE', os.cpu_count() * 20),
    'max_workers': os.getenv('DEVICE_MAX_WORKERS', os.cpu_count() * 3),
    'queue_size': os.getenv('DEVICE_QUEUE_SIZE', 10000),
    'batch_timeout': float(os.getenv('DEVICE_BATCH_TIMEOUT', 1.0)),
    'enable_async': os.getenv('DEVICE_ENABLE_ASYNC', 'true').lower() == 'true'
}

通过以上优化方案,upload_device_info 接口将获得显著的性能提升,能够支持更高的并发量和更快的响应时间,满足大规模设备部署的需求。