Spring Boot与WebSocket构建物联网实时通信系统

一、系统架构深度解析

1.1 物联网通信架构图

MQTT/HTTP WebSocket 物联网设备 Spring Boot服务 Web前端 MySQL数据库 管理员控制台 移动应用 消息队列 数据分析服务

1.2 核心组件职责矩阵

组件 职责 关键技术
设备接入层 协议转换、数据校验 Netty/MQTT
消息分发层 实时消息路由 STOMP/WebSocket
业务处理层 设备状态管理 Spring Data JPA
数据持久层 设备数据存储 MySQL/TimeSeries DB
前端展示层 实时数据可视化 SockJS/Chart.js

二、生产级WebSocket实现

2.1 增强版WebSocket配置

java 复制代码
@Configuration
@EnableWebSocketMessageBroker
@EnableScheduling
public class EnhancedWebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Value("${websocket.allowed-origins:*}")
    private String[] allowedOrigins;

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic", "/queue");
        config.setApplicationDestinationPrefixes("/app");
        config.setUserDestinationPrefix("/user");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/iot-ws")
            .setAllowedOrigins(allowedOrigins)
            .addInterceptors(new AuthHandshakeInterceptor())
            .withSockJS()
            .setStreamBytesLimit(512 * 1024)
            .setHttpMessageCacheSize(1000);
    }

    @Override
    public void configureWebSocketTransport(WebSocketTransportRegistration registry) {
        registry.setMessageSizeLimit(128 * 1024);
        registry.setSendTimeLimit(60 * 1000);
        registry.setSendBufferSizeLimit(1024 * 1024);
    }

    @Bean
    public WebSocketHandler getWebSocketHandler() {
        return new CustomWebSocketHandler();
    }
}

2.2 设备状态管理服务

java 复制代码
@Service
public class DeviceStateService {
    private final Map<String, DeviceState> deviceStates = new ConcurrentHashMap<>();
    private final SimpMessagingTemplate messagingTemplate;

    @Scheduled(fixedRate = 30000)
    public void checkDeviceHeartbeat() {
        deviceStates.entrySet().removeIf(entry -> {
            boolean isDead = System.currentTimeMillis() - entry.getValue().getLastActive() > 60000;
            if (isDead) {
                messagingTemplate.convertAndSend("/topic/device/offline", entry.getKey());
            }
            return isDead;
        });
    }

    public void updateState(String deviceId, DeviceData data) {
        DeviceState state = deviceStates.computeIfAbsent(deviceId, id -> new DeviceState());
        state.update(data);
        messagingTemplate.convertAndSend("/topic/device/" + deviceId + "/state", state);
    }

    @Data
    public static class DeviceState {
        private String status;
        private long lastActive;
        private Map<String, Object> metrics = new HashMap<>();

        public void update(DeviceData data) {
            this.status = data.getStatus();
            this.lastActive = System.currentTimeMillis();
            this.metrics.putAll(data.getMetrics());
        }
    }
}

三、物联网协议集成方案

3.1 MQTT协议适配器

java 复制代码
@Configuration
public class MqttConfig {

    @Bean
    public MqttPahoClientFactory mqttClientFactory() {
        DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
        MqttConnectOptions options = new MqttConnectOptions();
        options.setServerURIs(new String[] {"tcp://mqtt-broker:1883"});
        options.setUserName("iot-service");
        options.setPassword("password".toCharArray());
        options.setAutomaticReconnect(true);
        factory.setConnectionOptions(options);
        return factory;
    }

    @Bean
    public MessageProducer inboundChannelAdapter(
        MqttPahoClientFactory factory,
        DeviceDataService dataService) {

        MqttPahoMessageDrivenChannelAdapter adapter =
            new MqttPahoMessageDrivenChannelAdapter("server-1", factory, "devices/#");
        adapter.setCompletionTimeout(5000);
        adapter.setConverter(new DefaultPahoMessageConverter());
        adapter.setOutputChannelName("mqttInputChannel");
        adapter.setRecoveryInterval(10000);
        return adapter;
    }

    @ServiceActivator(inputChannel = "mqttInputChannel")
    public void handleMessage(Message<?> message) {
        String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString();
        DeviceData data = parseData(message.getPayload());
        dataService.processIncomingData(topic, data);
    }
}

3.2 协议转换中间件

java 复制代码
@Component
public class ProtocolAdapter {

    private final Map<ProtocolType, MessageConverter> converters = new EnumMap<>(ProtocolType.class);

    @PostConstruct
    public void init() {
        converters.put(ProtocolType.MQTT, new MqttMessageConverter());
        converters.put(ProtocolType.HTTP, new HttpMessageConverter());
        converters.put(ProtocolType.CoAP, new CoapMessageConverter());
    }

    public DeviceData convert(ProtocolType type, Object rawMessage) {
        MessageConverter converter = converters.get(type);
        if (converter == null) {
            throw new UnsupportedProtocolException(type);
        }
        return converter.convert(rawMessage);
    }

    public interface MessageConverter {
        DeviceData convert(Object message);
    }
}

四、安全与可靠性保障

4.1 WebSocket认证拦截器

java 复制代码
public class AuthHandshakeInterceptor implements HandshakeInterceptor {

    @Override
    public boolean beforeHandshake(ServerHttpRequest request,
                                 ServerHttpResponse response,
                                 WebSocketHandler wsHandler,
                                 Map<String, Object> attributes) {

        String token = request.getHeaders().getFirst("Authorization");
        if (!validateToken(token)) {
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            return false;
        }

        String deviceId = extractDeviceId(token);
        attributes.put("deviceId", deviceId);
        return true;
    }

    @Override
    public void afterHandshake(ServerHttpRequest request,
                              ServerHttpResponse response,
                              WebSocketHandler wsHandler,
                              Exception exception) {
        // 握手后处理逻辑
    }
}

4.2 消息可靠性保障

java 复制代码
@Controller
public class ReliableMessageController {

    @MessageMapping("/device/command")
    @SendToUser("/queue/command-ack")
    public CommandAck sendCommand(DeviceCommand command,
                                 Principal principal,
                                 SimpMessageHeaderAccessor headerAccessor) {

        String sessionId = headerAccessor.getSessionId();
        String deviceId = principal.getName();

        try {
            // 发送命令到设备
            boolean success = deviceService.sendCommand(deviceId, command);
            return new CommandAck(command.getId(), success);
        } catch (Exception e) {
            return new CommandAck(command.getId(), false, e.getMessage());
        }
    }

    @MessageMapping("/device/telemetry")
    public void receiveTelemetry(DeviceTelemetry telemetry,
                               @Header("simpSessionId") String sessionId) {

        // 持久化遥测数据
        telemetryService.saveTelemetry(telemetry);

        // 发送确认回执
        messagingTemplate.convertAndSendToUser(
            sessionId,
            "/queue/telemetry-ack",
            new TelemetryAck(telemetry.getTimestamp()));
    }
}

五、性能优化策略

5.1 消息批处理配置

java 复制代码
@Configuration
public class BatchingConfig {

    @Bean
    @ServiceActivator(inputChannel = "deviceDataChannel")
    public MessageHandler batchHandler() {
        AggregatingMessageHandler handler = new AggregatingMessageHandler(
            new MessageGroupProcessor() {
                @Override
                public Object processMessageGroup(MessageGroup group) {
                    return group.getMessages().stream()
                        .map(Message::getPayload)
                        .collect(Collectors.toList());
                }
            });

        handler.setOutputChannel(processedDataChannel());
        handler.setSendPartialResultOnExpiry(true);
        handler.setGroupTimeoutExpression(new ValueExpression<>(5000L));
        handler.setBatchSize(100);
        return handler;
    }

    @Bean
    public MessageChannel processedDataChannel() {
        return new DirectChannel();
    }
}

5.2 集群扩展方案

java 复制代码
@Configuration
@EnableRedisRepositories
public class ClusterConfig {

    @Bean
    public RedisMessageListenerContainer redisContainer(
        RedisConnectionFactory factory,
        SessionDisconnectListener listener) {

        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(factory);
        container.addMessageListener(listener, new PatternTopic("__keyevent@*__:expired"));
        return container;
    }

    @Bean
    public RedisOperationsSessionRepository sessionRepository(
        RedisConnectionFactory factory) {

        RedisOperationsSessionRepository repository =
            new RedisOperationsSessionRepository(factory);
        repository.setDefaultMaxInactiveInterval(1800);
        return repository;
    }
}

六、监控与运维方案

6.1 监控指标配置

java 复制代码
@Configuration
public class MetricsConfig {

    @Bean
    public MeterRegistryCustomizer<PrometheusMeterRegistry> metricsCustomizer() {
        return registry -> {
            Gauge.builder("websocket.sessions.active",
                    () -> sessionRepository.getActiveSessionsCount())
                .register(registry);

            Counter.builder("device.messages.received")
                .tag("protocol", "websocket")
                .register(registry);
        };
    }

    @Bean
    public WebSocketEventLogger webSocketEventLogger() {
        return new WebSocketEventLogger();
    }
}

6.2 日志审计实现

java 复制代码
@Aspect
@Component
@Slf4j
public class WebSocketLogAspect {

    @AfterReturning(pointcut = "@annotation(org.springframework.messaging.handler.annotation.MessageMapping)",
                   returning = "result")
    public void logMessageMapping(JoinPoint jp, Object result) {
        Object[] args = jp.getArgs();
        if (args.length > 0 && args[0] instanceof BaseMessage) {
            BaseMessage message = (BaseMessage) args[0];
            log.info("Processed message: {} with result: {}",
                    message.getClass().getSimpleName(),
                    result);
        }
    }

    @AfterThrowing(pointcut = "@annotation(org.springframework.messaging.handler.annotation.MessageMapping)",
                  throwing = "ex")
    public void logMessageError(JoinPoint jp, Exception ex) {
        Object[] args = jp.getArgs();
        if (args.length > 0 && args[0] instanceof BaseMessage) {
            BaseMessage message = (BaseMessage) args[0];
            log.error("Error processing message: {}",
                     message.getClass().getSimpleName(),
                     ex);
        }
    }
}

通过以上方案,可以构建出高性能、高可靠的物联网实时通信系统。建议在实际部署时:

  1. 根据设备规模调整线程池和连接参数
  2. 实施完善的监控告警机制
  3. 进行充分的压力测试
  4. 制定详细的容灾和降级方案
  5. 建立设备认证和授权体系