Prechádzať zdrojové kódy

config(api): 更新应用配置变量

- 移除 model_health_host 配置项
- 移除 auth_host 配置项
- 移除 product_name 配置项
- 更新 workflow_host 默认值为 http://10.146.0.2:8080
ChenYu 7 mesiacov pred
rodič
commit
6d48e072a0

+ 1 - 0
build.sh

@@ -128,6 +128,7 @@ docker run -d \
     -p 9902:8090 \
     --volume /etc/localtime:/etc/localtime \
     -v /var/run/docker.sock:/var/run/docker.sock \
+    --group-add \$(stat -c '%g' /var/run/docker.sock) \
     -v /logs/${MODULE}:/app/logs \
     -v /data/www/file/:/data/www/file/ \
     -e ACTIVE_ENV=${ENV} \

+ 87 - 51
src/main/java/com/aitori/service/DockerLogService.java

@@ -5,15 +5,17 @@ import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.StandardProtocolFamily;
+import java.net.UnixDomainSocketAddress;
+import java.nio.ByteBuffer;
+import java.nio.channels.SocketChannel;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.List;
-import java.util.concurrent.TimeUnit;
 
 @Slf4j
 @Service
@@ -23,7 +25,7 @@ public class DockerLogService {
     private final DockerProperties dockerProperties;
 
     /**
-     * 通过 Docker Engine API 查询容器日志并按关键词过滤。
+     * 通过 Docker Engine API (Unix Socket) 查询容器日志并按关键词过滤。
      * 需要宿主机将 /var/run/docker.sock 挂载到容器内。
      */
     public List<String> queryLogs(String containerName, String keyword, Integer tailLines) {
@@ -35,74 +37,108 @@ public class DockerLogService {
         }
 
         String socketPath = dockerProperties.getSocketPath();
-        if (!Files.exists(Path.of(socketPath))) {
+        Path sockFile = Path.of(socketPath);
+        if (!Files.exists(sockFile)) {
             throw new IllegalStateException(
                     "Docker socket 不存在: " + socketPath + ",请确认已挂载 -v /var/run/docker.sock:/var/run/docker.sock");
         }
 
-        String url = String.format(
-                "http://localhost/v1.44/containers/%s/logs?stdout=true&stderr=true&tail=%d&timestamps=true",
+        String requestPath = String.format(
+                "/v1.44/containers/%s/logs?stdout=true&stderr=true&tail=%d&timestamps=true",
                 containerName, tailLines);
 
-        ProcessBuilder pb = new ProcessBuilder(
-                "curl", "--silent", "--no-buffer",
-                "--unix-socket", socketPath,
-                url
-        );
-        pb.redirectErrorStream(true);
-
-        try {
-            Process process = pb.start();
-            List<String> matchedLines = new ArrayList<>();
-
-            try (InputStream is = process.getInputStream();
-                 BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
-
-                String line;
-                while ((line = reader.readLine()) != null) {
-                    String cleaned = stripDockerStreamHeader(line);
-                    if (keyword == null || keyword.isBlank() || cleaned.contains(keyword)) {
-                        matchedLines.add(cleaned);
-                    }
-                }
+        byte[] rawResponse = callDockerApi(sockFile, requestPath);
+        String body = extractHttpBody(rawResponse);
+
+        List<String> matchedLines = new ArrayList<>();
+        for (String line : body.split("\n")) {
+            String cleaned = stripNonPrintable(line).trim();
+            if (cleaned.isEmpty()) {
+                continue;
+            }
+            if (keyword == null || keyword.isBlank() || cleaned.contains(keyword)) {
+                matchedLines.add(cleaned);
             }
+        }
+
+        log.info("查询Docker日志完成, container={}, keyword={}, matched={}", containerName, keyword, matchedLines.size());
+        return matchedLines;
+    }
 
-            boolean finished = process.waitFor(30, TimeUnit.SECONDS);
-            if (!finished) {
-                process.destroyForcibly();
-                log.warn("查询Docker日志超时, container={}", containerName);
+    /**
+     * 通过 Java Unix Domain Socket 直接调用 Docker Engine API,
+     * 不依赖 curl 或 docker CLI。
+     */
+    private byte[] callDockerApi(Path socketPath, String requestPath) {
+        UnixDomainSocketAddress address = UnixDomainSocketAddress.of(socketPath);
+
+        try (SocketChannel channel = SocketChannel.open(StandardProtocolFamily.UNIX)) {
+            channel.connect(address);
+
+            String httpRequest = "GET " + requestPath + " HTTP/1.0\r\nHost: localhost\r\n\r\n";
+            channel.write(ByteBuffer.wrap(httpRequest.getBytes(StandardCharsets.UTF_8)));
+
+            ByteArrayOutputStream baos = new ByteArrayOutputStream();
+            ByteBuffer buf = ByteBuffer.allocate(8192);
+            while (channel.read(buf) > 0) {
+                buf.flip();
+                byte[] chunk = new byte[buf.remaining()];
+                buf.get(chunk);
+                baos.write(chunk);
+                buf.clear();
             }
 
-            int exitCode = finished ? process.exitValue() : -1;
-            if (exitCode != 0 && matchedLines.isEmpty()) {
-                log.error("curl 退出码={}, container={}", exitCode, containerName);
-                throw new RuntimeException("查询Docker日志失败,请检查容器名称是否正确: " + containerName);
+            byte[] response = baos.toByteArray();
+            if (response.length == 0) {
+                throw new RuntimeException("Docker API 返回空响应,请检查 docker.sock 权限");
             }
+            return response;
 
-            log.info("查询Docker日志完成, container={}, keyword={}, matched={}", containerName, keyword, matchedLines.size());
-            return matchedLines;
+        } catch (IOException e) {
+            throw new RuntimeException("连接 Docker socket 失败: " + e.getMessage(), e);
+        }
+    }
 
-        } catch (InterruptedException e) {
-            Thread.currentThread().interrupt();
-            throw new RuntimeException("查询Docker日志被中断", e);
-        } catch (Exception e) {
-            log.error("查询Docker日志异常, container={}, keyword={}", containerName, keyword, e);
-            throw new RuntimeException("查询Docker日志异常: " + e.getMessage(), e);
+    /**
+     * 从原始 HTTP 响应中提取 body(跳过 HTTP 头部)。
+     * 同时检查 HTTP 状态码,非 200 时抛出异常并附带错误信息。
+     */
+    private String extractHttpBody(byte[] rawResponse) {
+        String full = new String(rawResponse, StandardCharsets.UTF_8);
+
+        int headerEnd = full.indexOf("\r\n\r\n");
+        if (headerEnd < 0) {
+            throw new RuntimeException("Docker API 响应格式异常: " + full.substring(0, Math.min(200, full.length())));
+        }
+
+        String headers = full.substring(0, headerEnd);
+        String body = full.substring(headerEnd + 4);
+
+        String statusLine = headers.lines().findFirst().orElse("");
+        if (!statusLine.contains("200")) {
+            String errorMsg = body.trim().isEmpty() ? statusLine : body.trim();
+            log.error("Docker API 返回错误, status={}, body={}", statusLine, errorMsg);
+            throw new RuntimeException("Docker API 错误: " + errorMsg);
         }
+
+        return body;
     }
 
     /**
      * Docker 非 TTY 模式日志的每一帧带有 8 字节二进制头,
-     * 这里尝试剥离行首的不可打印字符。
+     * 剥离所有不可打印字符(保留 tab)。
      */
-    private String stripDockerStreamHeader(String line) {
+    private String stripNonPrintable(String line) {
         if (line == null || line.isEmpty()) {
             return line;
         }
-        int start = 0;
-        while (start < line.length() && (line.charAt(start) < 0x20 && line.charAt(start) != '\t')) {
-            start++;
+        StringBuilder sb = new StringBuilder(line.length());
+        for (int i = 0; i < line.length(); i++) {
+            char c = line.charAt(i);
+            if (c >= 0x20 || c == '\t') {
+                sb.append(c);
+            }
         }
-        return start > 0 ? line.substring(start) : line;
+        return sb.toString();
     }
 }