Files
2025-12-06 11:26:05 +08:00

311 lines
8.6 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package main
import (
"cmii-uav-watchdog-common/wdd_log"
"flag"
"os"
"os/exec"
"os/signal"
"strings"
"sync"
"syscall"
"time"
)
// 是否是Debug模式
var DebugMode = false
var (
businessProgramType = flag.String("business-program-type", "", "Type of business program (java or python)")
businessProgramPath = flag.String("business-program-path", "", "Path to the business program file")
stopRequested bool
currentCmd *exec.Cmd
mu sync.Mutex
)
// java 启动参数
var (
podName = "unknown"
imageName = "unknown"
imageVersion = "unknown"
workpath = "/cmii"
custJavaOpts = "-Xms200m -Xmx1500m -Djava.awt.headless=true -Dlog4j2.formatMsgNoLookups=true "
envJvmTimezone = "Asia/Shanghai"
k8sNamespace = "default"
applicationName = "app"
nacosRegistry = "helm-nacos:8848"
nacosDiscoveryIp = "127.0.0.1"
nacosDiscoveryPort = "8080"
nacosUsername = "nacos"
nacosPassword = "nacos"
bizConfigGroup = "wdd-biz"
sysConfigGroup = "wdd-sys"
)
// 初始化
func init() {
// 获取环境变量 特殊设置才能开启DEBUG
debugMode := os.Getenv("CMII_DEBUG_MODE")
if debugMode == "WDD_DEBUG" {
DebugMode = true
}
wdd_log.Info("DebugMode 是否开启: %v", DebugMode)
}
// 初始化配置并打印配置信息
func initConfig() {
// 获取环境变量
imageName = getEnvOrDefault("IMAGE_NAME", imageName)
if imageName != "unknown" {
parts := strings.Split(imageName, ":")
if len(parts) > 1 {
imageVersion = parts[len(parts)-1]
}
}
workpath = getEnvOrDefault("WORKPATH", workpath)
custJavaOpts = getEnvOrDefault("CUST_JAVA_OPTS", custJavaOpts)
envJvmTimezone = getEnvOrDefault("ENV_JVM_TIMEZONE", envJvmTimezone)
k8sNamespace = getEnvOrDefault("K8S_NAMESPACE", k8sNamespace)
applicationName = getEnvOrDefault("APPLICATION_NAME", applicationName)
nacosRegistry = getEnvOrDefault("NACOS_REGISTRY", nacosRegistry)
nacosDiscoveryIp = getEnvOrDefault("NACOS_DISCOVERY_IP", nacosDiscoveryIp)
nacosDiscoveryPort = getEnvOrDefault("NACOS_DISCOVERY_PORT", nacosDiscoveryPort)
nacosUsername = getEnvOrDefault("NACOS_USERNAME", nacosUsername)
nacosPassword = getEnvOrDefault("NACOS_PASSWORD", nacosPassword)
// 设置默认配置组
defaultConfigGroup := "default"
if imageVersion != "unknown" {
parts := strings.Split(imageVersion, "-")
if len(parts) > 0 {
defaultConfigGroup = parts[0]
}
}
bizConfigGroup = getEnvOrDefault("BIZ_CONFIG_GROUP", bizConfigGroup)
if bizConfigGroup == "" {
wdd_log.Info("[CONTAINER] As BIZ_CONFIG_GROUP is null, it set default value [%s]", defaultConfigGroup)
bizConfigGroup = defaultConfigGroup
os.Setenv("BIZ_CONFIG_GROUP", bizConfigGroup)
}
sysConfigGroup = getEnvOrDefault("SYS_CONFIG_GROUP", sysConfigGroup)
if sysConfigGroup == "" {
wdd_log.Info("[CONTAINER] As SYS_CONFIG_GROUP is null, it set default value [%s]", defaultConfigGroup)
sysConfigGroup = defaultConfigGroup
os.Setenv("SYS_CONFIG_GROUP", sysConfigGroup)
}
// 打印配置信息
wdd_log.Info("[CONTAINER] %s image is running ...", imageName)
wdd_log.Info("[CONTAINER] IMAGE_VERSION is %s", imageVersion)
wdd_log.Info("[CONTAINER] WORKPATH is %s", workpath)
wdd_log.Info("[CONTAINER] CUST_JAVA_OPTS is %s", custJavaOpts)
wdd_log.Info("[CONTAINER] JVM_TIMEZONE is %s", envJvmTimezone)
wdd_log.Info("[CONTAINER] K8S_NAMESPACE is %s", k8sNamespace)
wdd_log.Info("[CONTAINER] APPLICATION_NAME is %s", applicationName)
wdd_log.Info("[CONTAINER] NACOS_REGISTRY is %s", nacosRegistry)
wdd_log.Info("[CONTAINER] NACOS_DISCOVERY_IP is %s", nacosDiscoveryIp)
wdd_log.Info("[CONTAINER] NACOS_DISCOVERY_PORT is %s", nacosDiscoveryPort)
wdd_log.Info("[CONTAINER] NACOS_USERNAME is %s", nacosUsername)
wdd_log.Info("[CONTAINER] NACOS_PASSWORD is %s", nacosPassword)
wdd_log.Info("[CONTAINER] BIZ_CONFIG_GROUP is %s", bizConfigGroup)
wdd_log.Info("[CONTAINER] SYS_CONFIG_GROUP is %s", sysConfigGroup)
wdd_log.Info("[CONTAINER] starting...")
}
// 获取环境变量,如果为空则返回默认值
func getEnvOrDefault(key, defaultValue string) string {
value := os.Getenv(key)
if value == "" {
return defaultValue
}
return value
}
func startBusinessProcess(programType, programPath string) *exec.Cmd {
var cmd *exec.Cmd
switch programType {
case "java":
// 初始化配置
initConfig()
// 构建命令参数列表
args := []string{}
// 添加Java选项
if custJavaOpts != "" {
// 分割CUST_JAVA_OPTS中的多个参数
for _, opt := range splitArgs(custJavaOpts) {
if opt != "" {
args = append(args, opt)
}
}
}
// 添加主JAR文件
args = append(args, "-jar", programPath)
// 添加其他参数
args = append(args, []string{
"--user.timezone=" + envJvmTimezone,
"-Dfile.encoding=UTF-8",
"--spring.main.allow-bean-definition-overriding=true",
"--spring.application.name=" + applicationName,
"--spring.cloud.nacos.username=" + nacosUsername,
"--spring.cloud.nacos.password=" + nacosPassword,
"--spring.cloud.nacos.config.server-addr=" + nacosRegistry,
"--spring.cloud.nacos.config.extension-configs[0].data-id=" + applicationName + ".yml",
"--spring.cloud.nacos.config.extension-configs[0].group=" + bizConfigGroup,
"--spring.cloud.nacos.config.extension-configs[0].refresh=true",
"--spring.cloud.nacos.config.shared-configs[0].data-id=cmii-backend-system.yml",
"--spring.cloud.nacos.config.shared-configs[0].group=" + sysConfigGroup,
"--spring.cloud.nacos.config.shared-configs[0].refresh=true",
"--spring.cloud.nacos.discovery.server-addr=" + nacosRegistry,
"--spring.cloud.nacos.discovery.ip=" + nacosDiscoveryIp,
"--spring.cloud.nacos.discovery.port=" + nacosDiscoveryPort,
}...)
wdd_log.Info("[CONTAINER] java args: %v", args)
cmd = exec.Command("java", args...)
case "python":
cmd = exec.Command("python", programPath)
default:
wdd_log.Error("不支持的业务程序类型: %s", programType)
}
return cmd
}
// 分割命令行参数
func splitArgs(s string) []string {
var args []string
var inQuote bool
var current string
var quoteChar rune
for _, r := range s {
switch {
case (r == '"' || r == '\'') && !inQuote:
inQuote = true
quoteChar = r
case r == quoteChar && inQuote:
inQuote = false
quoteChar = 0
case r == ' ' && !inQuote:
if current != "" {
args = append(args, current)
current = ""
}
default:
current += string(r)
}
}
if current != "" {
args = append(args, current)
}
return args
}
func main() {
// 解析命令行参数
flag.Parse()
if *businessProgramType == "" || *businessProgramPath == "" {
wdd_log.Error("缺少必要的参数: -business-program-type 和 -business-program-path 必须指定")
}
// 信号处理
signalChan := make(chan os.Signal, 1)
signal.Notify(signalChan, os.Interrupt, syscall.SIGTERM)
// StartHeartbeatDetection(signalChan)
go func() {
for sig := range signalChan {
wdd_log.Info("接收到信号: %v", sig)
mu.Lock()
stopRequested = true
if currentCmd != nil && currentCmd.Process != nil {
// 发送 SIGTERM 给业务进程
if err := currentCmd.Process.Signal(syscall.SIGTERM); err != nil {
wdd_log.Error("向进程发送SIGTERM信号失败: %v", err)
}
// 等待 10 秒后强制杀死进程
time.AfterFunc(10*time.Second, func() {
mu.Lock()
defer mu.Unlock()
if currentCmd != nil && currentCmd.Process != nil {
wdd_log.Warn("优雅关闭超时发送SIGKILL信号")
currentCmd.Process.Kill()
}
})
}
mu.Unlock()
}
}()
// 授权检测
go func() {
for {
StartHeartbeatDetection(signalChan)
}
}()
// 主循环
for {
mu.Lock()
if stopRequested {
mu.Unlock()
wdd_log.Info("收到停止请求,正在关闭")
os.Exit(0)
}
mu.Unlock()
cmd := startBusinessProcess(*businessProgramType, *businessProgramPath)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// 启动业务进程
if err := cmd.Start(); err != nil {
wdd_log.Error("启动业务进程失败: %v", err)
time.Sleep(5 * time.Second)
continue
}
// 业务进程启动成功
if *businessProgramType == "java" {
wdd_log.Info("[CONTAINER] 程序启动成功!")
}
mu.Lock()
currentCmd = cmd
mu.Unlock()
// 等待业务进程退出
err := cmd.Wait()
mu.Lock()
currentCmd = nil
mu.Unlock()
if err != nil {
wdd_log.Error("业务进程异常退出: %v", err)
} else {
wdd_log.Info("业务进程正常退出")
}
mu.Lock()
if stopRequested {
mu.Unlock()
wdd_log.Info("收到停止请求,正在关闭")
os.Exit(0)
}
mu.Unlock()
// 等待 5 秒后重启
wdd_log.Info("5秒后重启业务进程...")
time.Sleep(5 * time.Second)
}
}