diff --git a/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java b/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java index ee2c9b9..d59147e 100644 --- a/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java +++ b/tenant-service/src/main/java/com/xuqm/tenant/service/SystemUpdateService.java @@ -8,7 +8,6 @@ import org.springframework.stereotype.Service; import java.io.BufferedReader; import java.io.InputStreamReader; import java.util.List; -import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @Service @@ -47,20 +46,58 @@ public class SystemUpdateService { } } - emit.accept(">>> 即将重启 tenant-service,连接将短暂中断..."); - emit.accept("RESTART_SELF"); + // tenant-service 自身的重建:不能直接用 docker-compose,因为一旦发出 stop 指令, + // 当前容器(含 docker-compose 进程)会立即被杀死,后续的 rm/create/start 步骤不会执行。 + // 解决方案:先用 docker run -d 启动一个独立助手容器,它不依附于 tenant-service, + // 能在 tenant-service 停止后继续完成重建。 + emit.accept(">>> 启动自更新助手容器..."); + String registry = System.getenv().getOrDefault("REGISTRY", ""); + String imageTag = System.getenv().getOrDefault("IMAGE_TAG", "latest"); + String selfImage = registry.isEmpty() ? "alpine" : (registry + "/tenant-service:" + imageTag); + boolean helperStarted = spawnSelfUpdater(composeFile, selfImage); - // 延迟 3 秒后重启自身,确保 SSE 事件已发送到客户端 - CompletableFuture.runAsync(() -> { - try { - Thread.sleep(3000); - exec(msg -> log.info("[self-restart] {}", msg), - "docker-compose", "-f", composeFile, "-p", "xuqm", - "up", "-d", "--no-deps", "--force-recreate", "tenant-service"); - } catch (Exception e) { - log.error("self-restart failed", e); - } - }); + if (helperStarted) { + emit.accept(">>> 助手容器已就绪,tenant-service 即将重建(连接将短暂中断)..."); + emit.accept("RESTART_SELF"); + } else { + emit.accept(">>> [警告] 助手容器启动失败,请手动执行:"); + emit.accept(">>> docker-compose -f " + composeFile + " -p xuqm up -d --no-deps --force-recreate tenant-service"); + emit.accept("DONE"); + } + } + + /** + * 启动一个独立的 detached 容器,在 tenant-service 被停止后重建它。 + * 助手容器与 tenant-service 无父子关系,不会随 tenant-service 终止。 + */ + private boolean spawnSelfUpdater(String composeFile, String image) { + try { + // 清理上次残留(若有) + new ProcessBuilder("docker", "rm", "-f", "xuqm-self-updater") + .redirectErrorStream(true).start().waitFor(); + + // 等待 8 秒确保 tenant-service 已完全停止,然后执行 force-recreate + String shellCmd = "sleep 8 && docker-compose -f " + composeFile + + " -p xuqm up -d --no-deps --force-recreate tenant-service"; + + Process p = new ProcessBuilder( + "docker", "run", "-d", "--rm", + "--name", "xuqm-self-updater", + "-v", "/var/run/docker.sock:/var/run/docker.sock", + "-v", deployRoot + ":" + deployRoot, + "--entrypoint", "sh", + image, + "-c", shellCmd + ).redirectErrorStream(true).start(); + + String out = new String(p.getInputStream().readAllBytes()).trim(); + int code = p.waitFor(); + log.info("self-updater spawn: code={} containerId={}", code, out); + return code == 0; + } catch (Exception e) { + log.error("failed to spawn self-updater", e); + return false; + } } private boolean isRunning(String service) {