From a98dbca26ddba13929a67ebd859ef4784aa68127 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Thu, 21 May 2026 14:52:36 +0800 Subject: [PATCH] =?UTF-8?q?fix(system-update):=20=E7=94=A8=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E5=8A=A9=E6=89=8B=E5=AE=B9=E5=99=A8=E6=9B=BF=E4=BB=A3?= =?UTF-8?q?=20CompletableFuture=20=E5=AE=9E=E7=8E=B0=20tenant-service=20?= =?UTF-8?q?=E8=87=AA=E9=87=8D=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原方案:CompletableFuture 延迟调用 docker-compose up。 问题:docker-compose 发出 stop 指令后,容器内全部进程(含 CompletableFuture 线程) 被立即杀死,rm/create/start 步骤永远不会执行,tenant-service 停在停止状态。 新方案:先用 docker run -d 启动独立助手容器(xuqm-self-updater), 它不依附于 tenant-service,不会随之终止;8 秒后执行 force-recreate。 Co-Authored-By: Claude Sonnet 4.6 --- .../tenant/service/SystemUpdateService.java | 65 +++++++++++++++---- 1 file changed, 51 insertions(+), 14 deletions(-) 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) {