技术选型与架构设计

后端框架:Spring Boot 2.7.x(简化配置,集成Web/定时任务)
数据库:MySQL 8.0(时序数据存储)+ Redis(缓存实时数据)
通信协议:MQTT(传感器数据传输)+ HTTP(管理端交互)
前端技术:Vue.js + ECharts(数据可视化)

数据库设计

-- 传感器设备表
CREATE TABLE `sensor_device` (
  `id` INT AUTO_INCREMENT PRIMARY KEY,
  `name` VARCHAR(50) NOT NULL COMMENT '设备名称',
  `location` POINT NOT NULL COMMENT 'GPS坐标',
  `status` TINYINT DEFAULT 1 COMMENT '1在线 0离线'
);

-- 传感器数据记录表(按月份分表)
CREATE TABLE `sensor_data_202307` (
  `id` BIGINT AUTO_INCREMENT PRIMARY KEY,
  `device_id` INT NOT NULL,
  `temperature` DECIMAL(5,2) COMMENT '温度℃',
  `humidity` DECIMAL(5,2) COMMENT '湿度%',
  `soil_moisture` DECIMAL(5,2) COMMENT '土壤湿度%',
  `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
  INDEX `idx_device_time` (`device_id`, `created_at`)
) PARTITION BY RANGE (UNIX_TIMESTAMP(created_at)) (
    PARTITION p202307 VALUES LESS THAN (UNIX_TIMESTAMP('2023-08-01'))
);

-- 预警规则表
CREATE TABLE `alert_rule` (
  `id` INT PRIMARY KEY,
  `device_type` VARCHAR(20) NOT NULL,
  `metric_type` ENUM('temperature','humidity','soil_moisture'),
  `operator` ENUM('>','<','=','≠'),
  `threshold` DECIMAL(10,2) NOT NULL,
  `severity` ENUM('critical','warning')
);

核心功能实现

MQTT数据接收处理

@Component
public class MqttSensorListener implements MqttCallback {
    @Autowired
    private SensorDataService dataService;

    @Override
    public void messageArrived(String topic, MqttMessage message) {
        String payload = new String(message.getPayload());
        SensorData data = JSON.parseObject(payload, SensorData.class);
        
        // 数据校验
        if(data.getTemperature() < -50 || data.getTemperature() > 100) {
            throw new InvalidDataException("温度值异常");
        }

        // 异步写入数据库
        CompletableFuture.runAsync(() -> {
            dataService.saveData(data);
            checkAlertRules(data); // 触发预警检查
        });
    }
}

动态预警规则检查

public void checkAlertRules(SensorData data) {
    List<AlertRule> rules = ruleDao.getRulesByDeviceType(data.getDeviceType());
    
    rules.forEach(rule -> {
        double actualValue = getValueByMetric(data, rule.getMetricType());
        if(checkCondition(actualValue, rule.getOperator(), rule.getThreshold())) {
            Alert alert = new Alert();
            alert.setDeviceId(data.getDeviceId());
            alert.setRuleId(rule.getId());
            alert.setActualValue(actualValue);
            alertDao.save(alert);
            
            // 推送通知
            pushNotification(alert);
        }
    });
}

private boolean checkCondition(double value, String operator, double threshold) {
    switch(operator) {
        case ">": return value > threshold;
        case "<": return value < threshold;
        case "=": return Math.abs(value - threshold) < 0.01;
        case "≠": return Math.abs(value - threshold) > 0.01;
        default: return false;
    }
}

性能优化方案

数据存储优化

  • 使用MySQL分区表按时间范围存储传感器数据
  • 超过3个月的数据自动归档到ClickHouse
  • 实时数据双写Redis(SortedSet结构存储最新100条)

预警通知降级策略

@Slf4j
@Service
public class NotificationService {
    @Resource
    private SMSProvider smsProvider;
    @Resource
    private EmailSender emailSender;
    
    @Retryable(maxAttempts=3, backoff=@Backoff(delay=1000))
    public void sendAlert(Severity severity, String message) {
        switch(severity) {
            case CRITICAL:
                smsProvider.send(message); // 短信优先
                break;
            case WARNING:
                emailSender.send(message); // 邮件通知
                break;
        }
    }
    
    @Recover
    public void recoverSend(Exception e, Severity severity, String message) {
        log.error("通知发送失败,降级到站内信", e);
        saveToMessageQueue(message); // 最终一致性处理
    }
}

数据可视化接口

RESTful API设计

@RestController
@RequestMapping("/api/sensor")
public class SensorDataController {
    
    @GetMapping("/{deviceId}/stats")
    public ResponseEntity<DataStats> getHourlyStats(
        @PathVariable int deviceId,
        @RequestParam @DateTimeFormat(iso=ISO.DATE_TIME) LocalDateTime start,
        @RequestParam @DateTimeFormat(iso=ISO.DATE_TIME) LocalDateTime end) {
        
        return ResponseEntity.ok()
            .cacheControl(CacheControl.maxAge(5, TimeUnit.MINUTES))
            .body(dataService.getStats(deviceId, start, end));
    }
    
    @GetMapping("/{deviceId}/history")
    public List<SensorData> getHistoryData(
        @PathVariable int deviceId,
        @RequestParam(defaultValue="1d") String duration) {
        
        TemporalAmount period = Duration.parse("PT" + duration.toUpperCase());
        LocalDateTime end = LocalDateTime.now();
        LocalDateTime start = end.minus(period);
        
        return dataService.queryRange(deviceId, start, end);
    }
}

系统安全措施

  1. 设备认证:每个传感器设备使用TLS双向认证,MQTT连接需验证客户端证书
  2. 数据加密:敏感字段使用AES-256加密存储,密钥由KMS管理
  3. 权限控制:基于Spring Security实现RBAC模型
  4. 审计日志:记录所有数据修改操作,使用Elasticsearch存储日志

部署方案

容器化部署

# 数据库服务
version: '3.8'
services:
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: ${DB_ROOT_PASS}
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/conf:/etc/mysql/conf.d
    ports:
      - "3306:3306"

  redis:
    image: redis:6-alpine
    command: redis-server --appendonly yes
    volumes:
      - ./redis/data:/data

性能监控

  • Prometheus采集JVM/MySQL指标
  • Grafana展示实时监控看板
  • 关键业务指标埋点:
    • 数据接收延迟(ms)
    • 预警触发准确率(%)
    • 通知送达成功率(%)

更多推荐