{"record":{"id":"b3c199a11557a2d9","repo":"YunaiV/ruoyi-vue-pro","slug":"http-hook","errorCode":null,"errorMessage":"HTTP Hook 服务启动失败","messagePattern":"HTTP Hook 服务启动失败","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"critical","filePath":"yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxProtocol.java","lineNumber":229,"sourceCode":"        if (httpConfig != null && Boolean.TRUE.equals(httpConfig.getSslEnabled())) {\n            Assert.notBlank(httpConfig.getSslCertPath(), \"EMQX HTTP SSL 证书路径(emqx.http.ssl-cert-path)不能为空\");\n            Assert.notBlank(httpConfig.getSslKeyPath(), \"EMQX HTTP SSL 私钥路径(emqx.http.ssl-key-path)不能为空\");\n            PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()\n                    .setKeyPath(httpConfig.getSslKeyPath())\n                    .setCertPath(httpConfig.getSslCertPath());\n            options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);\n        }\n        try {\n            httpServer = vertx.createHttpServer(options)\n                    .requestHandler(router)\n                    .listen()\n                    .toCompletionStage().toCompletableFuture()\n                    .get(10, TimeUnit.SECONDS);\n            log.info(\"[startHttpServer][IoT EMQX 协议 {} HTTP Hook 服务启动成功, port: {}, ssl: {}]\",\n                    getId(), properties.getPort(), httpConfig != null && Boolean.TRUE.equals(httpConfig.getSslEnabled()));\n        } catch (Exception e) {\n            log.error(\"[startHttpServer][IoT EMQX 协议 {} HTTP Hook 服务启动失败, port: {}]\", getId(), properties.getPort(), e);\n            throw new RuntimeException(\"HTTP Hook 服务启动失败\", e);\n        }\n    }\n\n    private void stopHttpServer() {\n        if (httpServer == null) {\n            return;\n        }\n        try {\n            httpServer.close().toCompletionStage().toCompletableFuture()\n                    .get(5, TimeUnit.SECONDS);\n            log.info(\"[stopHttpServer][IoT EMQX 协议 {} HTTP Hook 服务已停止]\", getId());\n        } catch (Exception e) {\n            log.error(\"[stopHttpServer][IoT EMQX 协议 {} HTTP Hook 服务停止失败]\", getId(), e);\n        } finally {\n            httpServer = null;\n        }\n    }\n","sourceCodeStart":211,"sourceCodeEnd":247,"githubUrl":"https://github.com/YunaiV/ruoyi-vue-pro/blob/0418084e222612af2fc1141f566af454f9236ab1/yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxProtocol.java#L211-L247","documentation":"Thrown when the Vert.x HTTP server hosting the EMQX HTTP Hook endpoints (/mqtt/auth, /mqtt/acl, /mqtt/event) fails to start. The code calls httpServer.listen() with a 10-second join (.get(10, TimeUnit.SECONDS)); any bind failure, SSL cert/key load error, or listen timeout is wrapped and rethrown as RuntimeException. Because IotEmqxProtocol.start() calls this first and propagates, it aborts the whole protocol instance startup.","triggerScenarios":"properties.getPort() is already bound by another process; SSL enabled (emqx.http.ssl-enabled=true) but ssl-cert-path / ssl-key-path is missing, unreadable, or invalid PEM; the OS denies the bind (privileged port <1024 without permission); listen() does not complete within 10 seconds.","commonSituations":"Port conflict with another EMQX protocol instance or service; running two gateway processes; SSL cert path typo or wrong working directory; cert/key file permissions; EMQX configured to call back on a port the gateway never opened.","solutions":["Check the port is free: ss -ltnp | grep <port> or lsof -i:<port>, then free it or change properties.port.","If SSL is enabled, verify emqx.http.ssl-cert-path and emqx.http.ssl-key-path exist and are readable by the gateway process, and form a valid key/cert pair (openssl x509 -noout -modulus matches key).","Ensure only one gateway instance binds the port (no duplicate protocol instances with the same port).","Raise the 10s listen timeout or check Vert.x event-loop health if bind is slow under load."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// before start(), confirm the hook port is free and SSL files (if enabled) exist\ntry (ServerSocket probe = new ServerSocket(properties.getPort())) {\n    // port is free\n} catch (IOException e) {\n    throw new IllegalStateException(\"HTTP Hook 端口被占用: \" + properties.getPort(), e);\n}\nif (Boolean.TRUE.equals(httpConfig.getSslEnabled())) {\n    java.nio.file.Files.readString(java.nio.file.Path.of(httpConfig.getSslCertPath()));\n    java.nio.file.Files.readString(java.nio.file.Path.of(httpConfig.getSslKeyPath()));\n}","typeGuard":null,"tryCatchPattern":"// in IotProtocolManager.start(), isolate each protocol so one bind failure doesn't abort all\nfor (ProtocolProperties config : protocolConfigs) {\n    try {\n        IotProtocol protocol = createProtocol(config);\n        if (protocol != null) { protocol.start(); protocols.add(protocol); }\n    } catch (Exception e) {\n        log.error(\"[start][协议实例 {} 启动失败，跳过]\", config.getId(), e);\n    }\n}","preventionTips":["Reserve the hook port exclusively for the gateway (document it, allocate from a range).","Validate SSL cert/key files in the constructor (Assert + Files.exists) so misconfiguration fails fast.","Run only one gateway instance per host:port; use a process supervisor that prevents duplicates.","Isolate per-protocol startup failures in the manager loop so a single bad port doesn't block other protocols."],"tags":["emqx","http-server","vertx","network","startup"],"backgroundTag":null,"analyzedSha":"0418084e222612af2fc1141f566af454f9236ab1","analyzedAt":"2026-08-14T00:56:18.412Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}