YunaiV/ruoyi-vue-pro · critical · RuntimeException

HTTP Hook 服务启动失败

Error message

HTTP Hook 服务启动失败

What it means

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.

Source

Thrown at yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/emqx/IotEmqxProtocol.java:229

        if (httpConfig != null && Boolean.TRUE.equals(httpConfig.getSslEnabled())) {
            Assert.notBlank(httpConfig.getSslCertPath(), "EMQX HTTP SSL 证书路径(emqx.http.ssl-cert-path)不能为空");
            Assert.notBlank(httpConfig.getSslKeyPath(), "EMQX HTTP SSL 私钥路径(emqx.http.ssl-key-path)不能为空");
            PemKeyCertOptions pemKeyCertOptions = new PemKeyCertOptions()
                    .setKeyPath(httpConfig.getSslKeyPath())
                    .setCertPath(httpConfig.getSslCertPath());
            options.setSsl(true).setKeyCertOptions(pemKeyCertOptions);
        }
        try {
            httpServer = vertx.createHttpServer(options)
                    .requestHandler(router)
                    .listen()
                    .toCompletionStage().toCompletableFuture()
                    .get(10, TimeUnit.SECONDS);
            log.info("[startHttpServer][IoT EMQX 协议 {} HTTP Hook 服务启动成功, port: {}, ssl: {}]",
                    getId(), properties.getPort(), httpConfig != null && Boolean.TRUE.equals(httpConfig.getSslEnabled()));
        } catch (Exception e) {
            log.error("[startHttpServer][IoT EMQX 协议 {} HTTP Hook 服务启动失败, port: {}]", getId(), properties.getPort(), e);
            throw new RuntimeException("HTTP Hook 服务启动失败", e);
        }
    }

    private void stopHttpServer() {
        if (httpServer == null) {
            return;
        }
        try {
            httpServer.close().toCompletionStage().toCompletableFuture()
                    .get(5, TimeUnit.SECONDS);
            log.info("[stopHttpServer][IoT EMQX 协议 {} HTTP Hook 服务已停止]", getId());
        } catch (Exception e) {
            log.error("[stopHttpServer][IoT EMQX 协议 {} HTTP Hook 服务停止失败]", getId(), e);
        } finally {
            httpServer = null;
        }
    }

View on GitHub (pinned to 0418084e22)

Solutions

  1. Check the port is free: ss -ltnp | grep <port> or lsof -i:<port>, then free it or change properties.port.
  2. 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).
  3. Ensure only one gateway instance binds the port (no duplicate protocol instances with the same port).
  4. Raise the 10s listen timeout or check Vert.x event-loop health if bind is slow under load.
Defensive patterns

Strategy: validation

Validate before calling

// before start(), confirm the hook port is free and SSL files (if enabled) exist
try (ServerSocket probe = new ServerSocket(properties.getPort())) {
    // port is free
} catch (IOException e) {
    throw new IllegalStateException("HTTP Hook 端口被占用: " + properties.getPort(), e);
}
if (Boolean.TRUE.equals(httpConfig.getSslEnabled())) {
    java.nio.file.Files.readString(java.nio.file.Path.of(httpConfig.getSslCertPath()));
    java.nio.file.Files.readString(java.nio.file.Path.of(httpConfig.getSslKeyPath()));
}

Try / catch

// in IotProtocolManager.start(), isolate each protocol so one bind failure doesn't abort all
for (ProtocolProperties config : protocolConfigs) {
    try {
        IotProtocol protocol = createProtocol(config);
        if (protocol != null) { protocol.start(); protocols.add(protocol); }
    } catch (Exception e) {
        log.error("[start][协议实例 {} 启动失败,跳过]", config.getId(), e);
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of YunaiV/ruoyi-vue-pro@0418084e22 (2026-08-14). Data as JSON: /api/errors/b3c199a11557a2d9. Report an issue: GitHub.