From 8a37c62e637b6a4a506771b9f16b3f03a0dcb580 Mon Sep 17 00:00:00 2001 From: Magnus Root Date: Fri, 24 Apr 2026 09:05:22 +0300 Subject: [PATCH] Speed matchchecker --- matchcheck.go | 109 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 20 deletions(-) diff --git a/matchcheck.go b/matchcheck.go index e815c91..a63d98f 100644 --- a/matchcheck.go +++ b/matchcheck.go @@ -9,6 +9,7 @@ import ( "regexp" "sort" "strings" + "sync" ) // ── Типы ───────────────────────────────────────────────────────────────────── @@ -173,7 +174,7 @@ func mcCheckClient(client, hostname string) mcClientCheck { func mcCheckContainer(client string, num int, angiePorts []int, hostname string) mcContainerCheck { result := mcContainerCheck{Number: num} - fullName := fmt.Sprintf("ptaf_%s_%03d", client, num) + fullName := mcBuildContainerName(client, hostname, num) label := fmt.Sprintf("контейнер %03d", num) fmt.Printf(" ┌─ Контейнер %03d\n", num) @@ -365,31 +366,99 @@ func mcIsValidJSON(filePath string) bool { return json.Unmarshal(data, &js) == nil } -func mcIsContainerRunning(name string) bool { - cmd := exec.Command("docker", "ps", "--format", "{{.Names}}") - output, err := cmd.Output() - if err != nil { - return false +// mcBuildContainerName строит имя Docker-контейнера по новому формату: +// {ClientTitle}_{AZN}_{prefix}{nodeN}_a{agentN} +// +// Пример: +// +// hostname: PTAF-TB-docker-AZ3-HW-node01, client: PSB_CFA, agent: 1 +// → PSB_CFA_AZ3_hn01_a001 +// +// hostname: PTAF-TB-docker-AZ1-node04, client: ITAR-TASS, agent: 1 +// → ITAR-TASS_AZ1_n04_a001 +func mcBuildContainerName(client, hostname string, agentNum int) string { + // Извлекаем AZN: ищем AZ + цифры в hostname + azRe := regexp.MustCompile(`AZ(\d+)`) + azMatch := azRe.FindStringSubmatch(hostname) + azPart := "AZ0" + if len(azMatch) > 1 { + azPart = "AZ" + azMatch[1] } - for _, line := range strings.Split(string(output), "\n") { - if strings.TrimSpace(line) == name { - return true + + // Определяем префикс ноды: HW → hn, иначе n + nodePrefix := "n" + if strings.Contains(hostname, "-HW-") { + nodePrefix = "hn" + } + + // Извлекаем номер ноды: цифры после "node" + nodeRe := regexp.MustCompile(`node(\d+)`) + nodeMatch := nodeRe.FindStringSubmatch(hostname) + nodePart := "00" + if len(nodeMatch) > 1 { + // Берём последние 2 цифры с ведущим нулём + num := nodeMatch[1] + if len(num) >= 2 { + nodePart = num[len(num)-2:] + } else { + nodePart = fmt.Sprintf("%02s", num) } } - return false + + return fmt.Sprintf("%s_%s_%s%s_a%03d", client, azPart, nodePrefix, nodePart, agentNum) +} + +// ── Кэш системных вызовов ──────────────────────────────────────────────────── +// docker ps и ss вызываются один раз на всё выполнение и кэшируются. +// Это критично при большом количестве контейнеров и портов (51+ портов на контейнер). + +var ( + mcRunningContainers map[string]bool // имя контейнера → запущен + mcListeningPorts map[int]bool // порт → слушается + mcCacheOnce sync.Once +) + +func mcInitCache() { + mcCacheOnce.Do(func() { + mcRunningContainers = make(map[string]bool) + mcListeningPorts = make(map[int]bool) + + // Один вызов docker ps — все запущенные контейнеры + if out, err := exec.Command("docker", "ps", "--format", "{{.Names}}").Output(); err == nil { + for _, line := range strings.Split(string(out), "\n") { + name := strings.TrimSpace(line) + if name != "" { + mcRunningContainers[name] = true + } + } + } + + // Один вызов ss — все слушающие порты + out, err := exec.Command("ss", "-tlnp").Output() + if err != nil { + out, err = exec.Command("netstat", "-tlnp").Output() + } + if err == nil { + re := regexp.MustCompile(`127\.0\.0\.1:(\d+)`) + for _, m := range re.FindAllStringSubmatch(string(out), -1) { + if len(m) > 1 { + var p int + fmt.Sscanf(m[1], "%d", &p) + mcListeningPorts[p] = true + } + } + } + }) +} + +func mcIsContainerRunning(name string) bool { + mcInitCache() + return mcRunningContainers[name] } func mcIsPortListening(port int) bool { - cmd := exec.Command("ss", "-tlnp") - output, err := cmd.Output() - if err != nil { - cmd = exec.Command("netstat", "-tlnp") - output, err = cmd.Output() - if err != nil { - return false - } - } - return strings.Contains(string(output), fmt.Sprintf("127.0.0.1:%d", port)) + mcInitCache() + return mcListeningPorts[port] } func mcContainsInt(slice []int, val int) bool {