From 551dbb2a095a44e6b1a80cca131542341abb7064 Mon Sep 17 00:00:00 2001 From: skiffer-git <44203734@qq.com> Date: Wed, 10 Apr 2024 14:54:11 +0800 Subject: [PATCH] Optimize Docker configuration and script. --- cmd/openim-api/main.go | 1 + cmd/openim-test/main.go | 1 + go.mod | 4 +- magefile.go | 4 +- pkg/util/mageutil/bricks.go | 117 ++++++++++ pkg/util/mageutil/bssc.go | 198 +++++++++++++++++ pkg/util/mageutil/define.go | 46 ++++ pkg/util/mageutil/logging.go | 56 +++++ pkg/util/mageutil/path.go | 85 +++++++ pkg/util/mageutil/sys.go | 208 ++++++++++++++++++ pkg/util/mageutil/usage-guide/README_zh_CN.md | 67 ++++++ pkg/util/mageutil/usage-guide/bootstrap.bat | 31 +++ pkg/util/mageutil/usage-guide/bootstrap.sh | 23 ++ pkg/util/mageutil/usage-guide/magefile.go | 38 ++++ .../mageutil/usage-guide/magefile_unix.go | 20 ++ .../mageutil/usage-guide/magefile_windows.go | 8 + .../mageutil/usage-guide/start-config.yml | 8 + tools/check-component/main.go | 5 +- tools/url2im/pkg/api.go | 1 + 19 files changed, 914 insertions(+), 7 deletions(-) create mode 100644 pkg/util/mageutil/bricks.go create mode 100644 pkg/util/mageutil/bssc.go create mode 100644 pkg/util/mageutil/define.go create mode 100644 pkg/util/mageutil/logging.go create mode 100644 pkg/util/mageutil/path.go create mode 100644 pkg/util/mageutil/sys.go create mode 100644 pkg/util/mageutil/usage-guide/README_zh_CN.md create mode 100644 pkg/util/mageutil/usage-guide/bootstrap.bat create mode 100644 pkg/util/mageutil/usage-guide/bootstrap.sh create mode 100644 pkg/util/mageutil/usage-guide/magefile.go create mode 100644 pkg/util/mageutil/usage-guide/magefile_unix.go create mode 100644 pkg/util/mageutil/usage-guide/magefile_windows.go create mode 100644 pkg/util/mageutil/usage-guide/start-config.yml diff --git a/cmd/openim-api/main.go b/cmd/openim-api/main.go index e29ed2a59..58e540c05 100644 --- a/cmd/openim-api/main.go +++ b/cmd/openim-api/main.go @@ -25,4 +25,5 @@ func main() { if err := cmd.NewApiCmd().Exec(); err != nil { program.ExitWithError(err) } + } diff --git a/cmd/openim-test/main.go b/cmd/openim-test/main.go index 53add7ccd..eb3e54438 100644 --- a/cmd/openim-test/main.go +++ b/cmd/openim-test/main.go @@ -60,4 +60,5 @@ func listenOnPort(port int) { // Normally, you would accept connections with l.Accept() in a loop here. // For this example, just sleep indefinitely to simulate a service. select {} + } diff --git a/go.mod b/go.mod index 7752d8969..757f32a33 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 github.com/openimsdk/localcache v0.0.1 github.com/openimsdk/protocol v0.0.60 - github.com/openimsdk/tools v0.0.47-alpha.16 + github.com/openimsdk/tools v0.0.47-alpha.14 github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.18.0 github.com/stretchr/testify v1.8.4 @@ -34,6 +34,7 @@ require ( github.com/go-redis/redis v6.15.9+incompatible github.com/kelindar/bitmap v1.5.2 github.com/likexian/gokit v0.25.13 + github.com/magefile/mage v1.15.0 github.com/redis/go-redis/v9 v9.4.0 github.com/robfig/cron/v3 v3.0.1 github.com/shirou/gopsutil v3.21.11+incompatible @@ -99,7 +100,6 @@ require ( github.com/klauspost/cpuid/v2 v2.2.6 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lithammer/shortuuid v3.0.0+incompatible // indirect - github.com/magefile/mage v1.15.0 // indirect github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect diff --git a/magefile.go b/magefile.go index 47a841297..0e4821cfd 100644 --- a/magefile.go +++ b/magefile.go @@ -4,7 +4,7 @@ package main import ( - "github.com/openimsdk/tools/utils/mageutil" + "github.com/openimsdk/open-im-server/v3/pkg/util/mageutil" "os" "strings" ) @@ -21,7 +21,7 @@ func Build() { mageutil.CompileForPlatform(platform) } - mageutil.PrintGreen("Compilation complete.") + mageutil.PrintGreen("All binaries under cmd and tools were successfully compiled.") } func Start() { diff --git a/pkg/util/mageutil/bricks.go b/pkg/util/mageutil/bricks.go new file mode 100644 index 000000000..47d7f4cb2 --- /dev/null +++ b/pkg/util/mageutil/bricks.go @@ -0,0 +1,117 @@ +package mageutil + +import ( + "fmt" + "os" + "os/exec" + "path/filepath" + "strconv" + "strings" +) + +// StopBinaries iterates over all binary files and terminates their corresponding processes. +func StopBinaries() { + for binary := range serviceBinaries { + fullPath := GetBinFullPath(binary) + KillExistBinary(fullPath) + } +} + +// StartBinaries Start all binary services. +func StartBinaries() error { + for binary, count := range serviceBinaries { + binFullPath := filepath.Join(OpenIMOutputHostBin, binary) + for i := 0; i < count; i++ { + args := []string{"-i", strconv.Itoa(i), "-c", OpenIMOutputConfig} + cmd := exec.Command(binFullPath, args...) + fmt.Printf("Starting %s\n", cmd.String()) + cmd.Dir = OpenIMOutputHostBin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + if err := cmd.Start(); err != nil { + fmt.Fprintf(os.Stderr, "Failed to start %s with args %v: %v\n", binFullPath, args, err) + return err + } + } + } + return nil +} + +// StartTools starts all tool binaries. +func StartTools() error { + for _, tool := range toolBinaries { + toolFullPath := GetToolFullPath(tool) + cmd := exec.Command(toolFullPath, "-c", OpenIMOutputConfig) + fmt.Printf("Starting %s\n", cmd.String()) + cmd.Dir = OpenIMOutputHostBinTools + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + + if err := cmd.Start(); err != nil { + fmt.Printf("Failed to start %s with error: %v\n", toolFullPath, err) + return err + } + + if err := cmd.Wait(); err != nil { + fmt.Printf("Failed to execute %s with exit code: %v\n", toolFullPath, err) + return err + } + fmt.Printf("Starting %s successfully \n", cmd.String()) + } + + return nil +} + +// KillExistBinaries iterates over all binary files and kills their corresponding processes. +func KillExistBinaries() { + for binary := range serviceBinaries { + fullPath := GetBinFullPath(binary) + KillExistBinary(fullPath) + } +} + +// CheckBinariesStop checks if all binary files have stopped and returns an error if there are any binaries still running. +func CheckBinariesStop() error { + var runningBinaries []string + + for binary := range serviceBinaries { + fullPath := GetBinFullPath(binary) + if CheckProcessNamesExist(fullPath) { + runningBinaries = append(runningBinaries, binary) + } + } + + if len(runningBinaries) > 0 { + return fmt.Errorf("the following binaries are still running: %s", strings.Join(runningBinaries, ", ")) + } + + return nil +} + +// CheckBinariesRunning checks if all binary files are running as expected and returns any errors encountered. +func CheckBinariesRunning() error { + var errorMessages []string + + for binary, expectedCount := range serviceBinaries { + fullPath := GetBinFullPath(binary) + err := CheckProcessNames(fullPath, expectedCount) + if err != nil { + errorMessages = append(errorMessages, fmt.Sprintf("binary %s is not running as expected: %v", binary, err)) + } + } + + if len(errorMessages) > 0 { + return fmt.Errorf(strings.Join(errorMessages, "\n")) + } + + return nil +} + +// PrintListenedPortsByBinaries iterates over all binary files and prints the ports they are listening on. +func PrintListenedPortsByBinaries() { + for binary, _ := range serviceBinaries { + basePath := GetBinFullPath(binary) + fullPath := basePath + PrintBinaryPorts(fullPath) + } +} diff --git a/pkg/util/mageutil/bssc.go b/pkg/util/mageutil/bssc.go new file mode 100644 index 000000000..26c6b0f68 --- /dev/null +++ b/pkg/util/mageutil/bssc.go @@ -0,0 +1,198 @@ +package mageutil + +import ( + "fmt" + "github.com/magefile/mage/sh" + "os" + "path/filepath" + "strings" + "sync" +) + +// CheckAndReportBinariesStatus checks the running status of all binary files and reports it. +func CheckAndReportBinariesStatus() { + err := CheckBinariesRunning() + + if err != nil { + PrintRed("Some programs are not running properly:") + PrintRedNoTimeStamp(err.Error()) + return + } + PrintGreen("All services are running normally.") + PrintBlue("Display details of the ports listened to by the service:") + PrintListenedPortsByBinaries() +} + +// StopAndCheckBinaries stops all binary processes and checks if they have all stopped. +func StopAndCheckBinaries() { + KillExistBinaries() + err := CheckBinariesStop() + if err != nil { + PrintRed("Some services have not been stopped, details are as follows:" + err.Error()) + return + } + PrintGreen("All services have been stopped") +} + +// StartToolsAndServices starts the process for tools and services. +func StartToolsAndServices() { + PrintBlue("Starting tools primarily involves component verification and other preparatory tasks.") + if err := StartTools(); err != nil { + PrintRed("Some tools failed to start, details are as follows, abort start") + PrintRedNoTimeStamp(err.Error()) + return + } + PrintGreen("All tools executed successfully") + + KillExistBinaries() + err := CheckBinariesStop() + if err != nil { + PrintRed("Some services running, details are as follows, abort start " + err.Error()) + return + } + PrintBlue("Starting services involves multiple RPCs and APIs and may take some time. Please be patient") + err = StartBinaries() + if err != nil { + PrintRed("Failed to start all binaries") + PrintRedNoTimeStamp(err.Error()) + return + } + CheckAndReportBinariesStatus() +} + +// CompileForPlatform Main compile function +func CompileForPlatform(platform string) { + + PrintBlue(fmt.Sprintf("Compiling cmd for %s...", platform)) + + compileDir(filepath.Join(rootDirPath, "cmd"), platformsOutputBase, platform) + + PrintBlue(fmt.Sprintf("Compiling tools for %s...", platform)) + compileDir(filepath.Join(rootDirPath, "tools"), toolsOutputBase, platform) + +} + +func compileDir(sourceDir, outputBase, platform string) { + targetOS, targetArch := strings.Split(platform, "_")[0], strings.Split(platform, "_")[1] + outputDir := filepath.Join(outputBase, targetOS, targetArch) + + if err := os.MkdirAll(outputDir, 0755); err != nil { + fmt.Printf("Failed to create directory %s: %v\n", outputDir, err) + os.Exit(1) + } + + var wg sync.WaitGroup + errors := make(chan error, 1) + sem := make(chan struct{}, 4) + + err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() || filepath.Base(path) != "main.go" { + return nil + } + + wg.Add(1) + go func() { + sem <- struct{}{} + defer wg.Done() + defer func() { <-sem }() + + dir := filepath.Dir(path) + dirName := filepath.Base(dir) + outputFileName := dirName + if targetOS == "windows" { + outputFileName += ".exe" + } + + PrintBlue(fmt.Sprintf("Compiling dir: %s for platform: %s binary: %s ...", dirName, platform, outputFileName)) + err := sh.RunWith(map[string]string{"GOOS": targetOS, "GOARCH": targetArch}, "go", "build", "-o", filepath.Join(outputDir, outputFileName), filepath.Join(dir, "main.go")) + if err != nil { + errors <- fmt.Errorf("failed to compile %s for %s: %v", dirName, platform, err) + PrintRed("Compilation aborted. " + fmt.Sprintf("failed to compile %s for %s: %v", dirName, platform, err)) + os.Exit(1) + return + } + PrintGreen(fmt.Sprintf("Successfully compiled. dir: %s for platform: %s binary: %s", dirName, platform, outputFileName)) + }() + + return nil + }) + + if err != nil { + fmt.Println("Error walking through directories:", err) + os.Exit(1) + } + + wg.Wait() + close(errors) + + // Check for errors + if err, ok := <-errors; ok { + fmt.Println(err) + os.Exit(1) + } +} + +// compileDir compiles Go programs in a specified directory, appending .exe extension for output files on Windows platform +//func compileDir(sourceDir, outputBase, platform string) { +// targetOS, targetArch := strings.Split(platform, "_")[0], strings.Split(platform, "_")[1] +// outputDir := filepath.Join(outputBase, targetOS, targetArch) +// +// if err := os.MkdirAll(outputDir, 0755); err != nil { +// fmt.Printf("Failed to create directory %s: %v\n", outputDir, err) +// os.Exit(1) +// } +// +// var wg sync.WaitGroup +// errors := make(chan error, 1) +// +// err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, err error) error { +// if err != nil { +// return err +// } +// if info.IsDir() || filepath.Base(path) != "main.go" { +// return nil +// } +// +// wg.Add(1) +// go func() { +// defer wg.Done() +// +// dir := filepath.Dir(path) +// dirName := filepath.Base(dir) +// outputFileName := dirName +// if targetOS == "windows" { +// outputFileName += ".exe" +// } +// +// PrintBlue(fmt.Sprintf("Compiling dir: %s for platform: %s binary: %s ...", dirName, platform, outputFileName)) +// err := sh.RunWith(map[string]string{"GOOS": targetOS, "GOARCH": targetArch}, "go", "build", "-o", filepath.Join(outputDir, outputFileName), filepath.Join(dir, "main.go")) +// if err != nil { +// errors <- fmt.Errorf("failed to compile %s for %s: %v", dirName, platform, err) +// PrintRed("Compilation aborted. " + fmt.Sprintf("failed to compile %s for %s: %v", dirName, platform, err)) +// os.Exit(1) +// return +// } +// PrintGreen(fmt.Sprintf("Compiling dir: %s for platform: %s binary: %s ...", dirName, platform, outputFileName)) +// }() +// +// return nil +// }) +// +// if err != nil { +// fmt.Println("Error walking through directories:", err) +// os.Exit(1) +// } +// +// wg.Wait() +// close(errors) +// +// // Check for errors +// if err, ok := <-errors; ok { +// fmt.Println(err) +// fmt.Println("Compilation aborted.") +// os.Exit(1) +// } +//} diff --git a/pkg/util/mageutil/define.go b/pkg/util/mageutil/define.go new file mode 100644 index 000000000..2314c73c5 --- /dev/null +++ b/pkg/util/mageutil/define.go @@ -0,0 +1,46 @@ +package mageutil + +import ( + "gopkg.in/yaml.v3" + "io/ioutil" + "log" + "runtime" +) + +var ( + serviceBinaries map[string]int + toolBinaries []string + MaxFileDescriptors int +) + +type Config struct { + ServiceBinaries map[string]int `yaml:"serviceBinaries"` + ToolBinaries []string `yaml:"toolBinaries"` + MaxFileDescriptors int `yaml:"maxFileDescriptors"` +} + +func init() { + yamlFile, err := ioutil.ReadFile("start-config.yml") + if err != nil { + log.Fatalf("error reading YAML file: %v", err) + } + + // 解析YAML + var config Config + err = yaml.Unmarshal(yamlFile, &config) + if err != nil { + log.Fatalf("error unmarshalling YAML: %v", err) + } + + adjustedBinaries := make(map[string]int) + for binary, count := range config.ServiceBinaries { + if runtime.GOOS == "windows" { + binary += ".exe" + } + adjustedBinaries[binary] = count + } + + serviceBinaries = adjustedBinaries + toolBinaries = config.ToolBinaries + MaxFileDescriptors = config.MaxFileDescriptors +} diff --git a/pkg/util/mageutil/logging.go b/pkg/util/mageutil/logging.go new file mode 100644 index 000000000..82c0daf23 --- /dev/null +++ b/pkg/util/mageutil/logging.go @@ -0,0 +1,56 @@ +package mageutil + +import ( + "fmt" + "os" + "time" +) + +const ( + ColorBlue = "\033[0;34m" + ColorGreen = "\033[0;32m" + ColorRed = "\033[0;31m" + ColorReset = "\033[0m" +) + +func PrintBlueTwoLine(message string) { + currentTime := time.Now().Format("[2006-01-02 15:04:05 MST]") + fmt.Println(currentTime) + fmt.Printf("%s%s%s\n", ColorBlue, message, ColorReset) +} + +func PrintBlue(message string) { + currentTime := time.Now().Format("[2006-01-02 15:04:05 MST]") + fmt.Printf("%s %s%s%s\n", currentTime, ColorBlue, message, ColorReset) +} + +func PrintGreenTwoLine(message string) { + currentTime := time.Now().Format("[2006-01-02 15:04:05 MST]") + fmt.Println(currentTime) + fmt.Printf("%s%s%s\n", ColorGreen, message, ColorReset) +} + +func PrintGreen(message string) { + currentTime := time.Now().Format("[2006-01-02 15:04:05 MST]") + fmt.Printf("%s %s%s%s\n", currentTime, ColorGreen, message, ColorReset) +} + +func PrintRed(message string) { + currentTime := time.Now().Format("[2006-01-02 15:04:05 MST]") + fmt.Printf("%s %s%s%s\n", currentTime, ColorRed, message, ColorReset) +} + +func PrintRedNoTimeStamp(message string) { + fmt.Printf("%s%s%s\n", ColorRed, message, ColorReset) +} + +func PrintGreenNoTimeStamp(message string) { + fmt.Printf("%s%s%s\n", ColorGreen, message, ColorReset) +} + +func PrintRedToStdErr(a ...interface{}) (n int, err error) { + return fmt.Fprint(os.Stderr, "\033[31m", fmt.Sprint(a...), "\033[0m") +} +func PrintGreenToStdOut(a ...interface{}) (n int, err error) { + return fmt.Fprint(os.Stdout, "\033[32m", fmt.Sprint(a...), "\033[0m") +} diff --git a/pkg/util/mageutil/path.go b/pkg/util/mageutil/path.go new file mode 100644 index 000000000..5f4580851 --- /dev/null +++ b/pkg/util/mageutil/path.go @@ -0,0 +1,85 @@ +package mageutil + +import ( + "fmt" + "os" + "path/filepath" +) + +var ( + OpenIMRoot string + OpenIMOutputConfig string + OpenIMOutput string + OpenIMOutputTools string + OpenIMOutputTmp string + OpenIMOutputLogs string + OpenIMOutputBin string + OpenIMOutputBinPath string + OpenIMOutputBinToolPath string + OpenIMInitErrLogFile string + OpenIMInitLogFile string + OpenIMOutputHostBin string + OpenIMOutputHostBinTools string +) + +func init() { + currentDir, err := os.Getwd() + if err != nil { + panic("Error getting current directory: " + err.Error()) + } + + OpenIMRoot = currentDir + + OpenIMOutputConfig = filepath.Join(OpenIMRoot, "config") + string(filepath.Separator) + OpenIMOutput = filepath.Join(OpenIMRoot, "_output") + string(filepath.Separator) + + OpenIMOutputTools = filepath.Join(OpenIMOutput, "tools") + string(filepath.Separator) + OpenIMOutputTmp = filepath.Join(OpenIMOutput, "tmp") + string(filepath.Separator) + OpenIMOutputLogs = filepath.Join(OpenIMOutput, "logs") + string(filepath.Separator) + OpenIMOutputBin = filepath.Join(OpenIMOutput, "bin") + string(filepath.Separator) + + OpenIMOutputBinPath = filepath.Join(OpenIMOutputBin, "platforms") + string(filepath.Separator) + OpenIMOutputBinToolPath = filepath.Join(OpenIMOutputBin, "tools") + string(filepath.Separator) + + OpenIMInitErrLogFile = filepath.Join(OpenIMOutputLogs, "openim-init-err.log") + OpenIMInitLogFile = filepath.Join(OpenIMOutputLogs, "openim-init.log") + + OpenIMOutputHostBin = filepath.Join(OpenIMOutputBinPath, OsArch()) + string(filepath.Separator) + OpenIMOutputHostBinTools = filepath.Join(OpenIMOutputBinToolPath, OsArch()) + string(filepath.Separator) + + dirs := []string{ + OpenIMOutputConfig, + OpenIMOutput, + OpenIMOutputTools, + OpenIMOutputTmp, + OpenIMOutputLogs, + OpenIMOutputBin, + OpenIMOutputBinPath, + OpenIMOutputBinToolPath, + OpenIMOutputHostBin, + OpenIMOutputHostBinTools, + } + + for _, dir := range dirs { + createDirIfNotExist(dir) + } +} + +func createDirIfNotExist(dir string) { + if err := os.MkdirAll(dir, 0755); err != nil { + fmt.Printf("Failed to create directory %s: %v\n", dir, err) + os.Exit(1) + } +} + +// GetBinFullPath constructs and returns the full path for the given binary name. +func GetBinFullPath(binName string) string { + binFullPath := filepath.Join(OpenIMOutputHostBin, binName) + return binFullPath +} + +// GetToolFullPath constructs and returns the full path for the given tool name. +func GetToolFullPath(toolName string) string { + toolFullPath := filepath.Join(OpenIMOutputHostBinTools, toolName) + return toolFullPath +} diff --git a/pkg/util/mageutil/sys.go b/pkg/util/mageutil/sys.go new file mode 100644 index 000000000..44750c9f7 --- /dev/null +++ b/pkg/util/mageutil/sys.go @@ -0,0 +1,208 @@ +package mageutil + +import ( + "fmt" + "github.com/shirou/gopsutil/net" + "github.com/shirou/gopsutil/process" + "os" + "path/filepath" + "runtime" + "strings" +) + +func OsArch() string { + os := runtime.GOOS + arch := runtime.GOARCH + if os == "windows" { + return fmt.Sprintf("%s\\%s", os, arch) + } + return fmt.Sprintf("%s/%s", os, arch) +} + +func CheckProcessNames(processPath string, expectedCount int) error { + processes, err := process.Processes() + if err != nil { + return fmt.Errorf("failed to get processes: %v", err) + } + + runningCount := 0 + for _, p := range processes { + exePath, err := p.Exe() + if err != nil { + continue + } + + if strings.EqualFold(exePath, processPath) { + runningCount++ + + } + } + + if runningCount == expectedCount { + return nil + } else { + return fmt.Errorf("%s Expected %d processes, but %d running", processPath, expectedCount, runningCount) + } +} + +// CheckProcessNamesExist checks if there are any processes running that match the specified path. +func CheckProcessNamesExist(processPath string) bool { + processes, err := process.Processes() + if err != nil { + fmt.Printf("Failed to get processes: %v\n", err) + return false + } + + for _, p := range processes { + exePath, err := p.Exe() + if err != nil { + continue + } + + if exePath == processPath { + return true // 找到至少一个匹配的进程 + } + } + + return false +} + +// PrintBinaryPorts prints the ports listened by the process of a specified binary file along with its command line arguments. +func PrintBinaryPorts(binaryPath string) { + pids, err := FindPIDsByBinaryPath(binaryPath) + if err != nil { + fmt.Println("Error finding PIDs:", err) + return + } + + if len(pids) == 0 { + fmt.Printf("No running processes found for binary: %s\n", binaryPath) + return + } + + for _, pid := range pids { + + proc, err := process.NewProcess(int32(pid)) + if err != nil { + fmt.Printf("Failed to create process object for PID %d: %v\n", pid, err) + continue + } + + cmdline, err := proc.Cmdline() + if err != nil { + fmt.Printf("Failed to get command line for PID %d: %v\n", pid, err) + continue + } + + connections, err := net.ConnectionsPid("all", int32(pid)) + if err != nil { + fmt.Printf("Error getting connections for PID %d: %v\n", pid, err) + continue + } + + portsMap := make(map[string]struct{}) + for _, conn := range connections { + if conn.Status == "LISTEN" { + port := fmt.Sprintf("%d", conn.Laddr.Port) + portsMap[port] = struct{}{} + } + } + + if len(portsMap) == 0 { + PrintGreen(fmt.Sprintf("Cmdline: %s, PID: %d is not listening on any ports.", cmdline, pid)) + } else { + ports := make([]string, 0, len(portsMap)) + for port := range portsMap { + ports = append(ports, port) + } + PrintGreen(fmt.Sprintf("Cmdline: %s, PID: %d is listening on ports: %s", cmdline, pid, strings.Join(ports, ", "))) + } + } +} + +// FindPIDsByBinaryPath finds all matching process PIDs by binary path. +func FindPIDsByBinaryPath(binaryPath string) ([]int, error) { + var pids []int + processes, err := process.Processes() + if err != nil { + return nil, err + } + + for _, proc := range processes { + exePath, err := proc.Exe() + if err != nil { + continue + } + + if strings.EqualFold(exePath, binaryPath) { + pids = append(pids, int(proc.Pid)) + } + } + + return pids, nil +} + +// KillExistBinary kills all processes matching the given binary file path. +func KillExistBinary(binaryPath string) { + processes, err := process.Processes() + if err != nil { + fmt.Printf("Failed to get processes: %v\n", err) + return + } + + for _, p := range processes { + exePath, err := p.Exe() + if err != nil { + continue + } + + if strings.Contains(strings.ToLower(exePath), strings.ToLower(binaryPath)) { + + //if strings.EqualFold(exePath, binaryPath) { + cmdline, err := p.Cmdline() + if err != nil { + fmt.Printf("Failed to get command line for process %d: %v\n", p.Pid, err) + continue + } + + err = p.Terminate() + if err != nil { + + err = p.Kill() + if err != nil { + fmt.Printf("Failed to kill process cmdline: %s, pid: %d, err: %v\n", cmdline, p.Pid, err) + } else { + fmt.Printf("Killed process cmdline: %s, pid: %d\n", cmdline, p.Pid) + } + } else { + fmt.Printf("Terminated process cmdline: %s, pid: %d\n", cmdline, p.Pid) + } + } + } +} + +// DetectPlatform detects the operating system and architecture. +func DetectPlatform() string { + targetOS, targetArch := runtime.GOOS, runtime.GOARCH + switch targetArch { + case "amd64", "arm64": + default: + fmt.Printf("Unsupported architecture: %s\n", targetArch) + os.Exit(1) + } + return fmt.Sprintf("%s_%s", targetOS, targetArch) +} + +// rootDir gets the absolute path of the current directory. +func rootDir() string { + dir, err := os.Getwd() + if err != nil { + fmt.Println("Failed to get current directory:", err) + os.Exit(1) + } + return dir +} + +var rootDirPath = rootDir() +var platformsOutputBase = filepath.Join(rootDirPath, "_output/bin/platforms") +var toolsOutputBase = filepath.Join(rootDirPath, "_output/bin/tools") diff --git a/pkg/util/mageutil/usage-guide/README_zh_CN.md b/pkg/util/mageutil/usage-guide/README_zh_CN.md new file mode 100644 index 000000000..dbadf71d1 --- /dev/null +++ b/pkg/util/mageutil/usage-guide/README_zh_CN.md @@ -0,0 +1,67 @@ +# mageutil使用指南 + +**mageutil** 是基于 mage 构建的一个工具,它提供了跨平台和多架构的编译支持,同时也简化了服务的启动、停止、检测流程。 + +## 使用指南 + +### 准备工作 + +1. 将此目录下除README以外的6个文件复制到项目的根目录: +2. 项目根目录下需要包含三个目录:`cmd`、`tools`和`config`。 + - `cmd` 目录专门用于存放那些作为后台服务运行的应用的启动代码。 + - `tools`目录用于存放那些作为工具应用(不以后台服务形式运行)的启动代码。 + - `config`目录用于存放配置文件。 +3. `cmd`和`tools`目录可以包含多层多个子目录。对于包含`main`函数的`main package`文件,需以`main.go`命名。例如: + - `cmd/openim-rpc/openim-rpc-msg/main.go` + - `tools/check-free-memory/main.go` + - 所有代码都应属于同一个项目,子目录不应使用独立的`go.mod`和`go.sum`文件。 + +### 初始化项目 + +- 对于Linux/Mac系统,先执行`bootstrap.sh`脚本。 +- 对于Windows系统,先执行`bootstrap.bat`脚本。 + +### 编译项目 + +- 执行`mage`或`mage build`来编译项目。 +- 编译完成后,二进制文件将生成在`_output/bin/platforms/<操作系统>/<架构>`目录下,其中二进制文件的命名规则为对应的`main.go`所在的目录名。例如: + - `_output/bin/platforms/linux/amd64/openim-rpc-msg` + - `_output/bin/tools/linux/amd64/check-free-memory` + - **注意:** Windows平台的二进制文件会自动添加`.exe`扩展名。 + +### 启动工具和服务 + +1. 首先,编辑`start-config.yml`文件,指定服务和工具相关配置,例如: + + ```yaml + #cmd服务子目录名: 实例数 + serviceBinaries: + openim-rpc-msg: 2 + + #tools工具子目录名 + toolBinaries: + - check-free-memory + + maxFileDescriptors: 10000 + ``` + + **注意:**服务和工具名与`cmd`和`tools`下的子目录名保持一致 + +3. 执行`mage start`来启动服务和工具。 + + - 工具将以同步方式执行,如果工具执行失败(退出代码非零),则整个启动过程中断。 + - 服务将以异步方式启动。 + +对于所有工具,将采用以下命令格式启动:`[程序绝对路径] -i 0 -c [配置文件绝对目录]`。 + +若服务实例数设置为`n`,则服务将启动`n`个实例,每个实例使用的命令格式为:`[程序路径] -i [实例索引] -c [配置文件目录]`,其中实例索引从`0`到`n-1`。 + +**注意**:本项目仅指定了配置文件的路径,并不负责读取配置文件内容。这样做的目的是为了支持使用多个配置文件的情况。程序和配置文件的路径都自动使用绝对路径。 + +### 检查和停止服务 + +- 执行`mage check`来检查服务状态和监听的端口。 +- 执行`mage stop`来停止服务,该命令会向服务发送停止信号。 + +--- + diff --git a/pkg/util/mageutil/usage-guide/bootstrap.bat b/pkg/util/mageutil/usage-guide/bootstrap.bat new file mode 100644 index 000000000..819f19c87 --- /dev/null +++ b/pkg/util/mageutil/usage-guide/bootstrap.bat @@ -0,0 +1,31 @@ +@echo off +SETLOCAL + +mage -version >nul 2>&1 +IF %ERRORLEVEL% EQU 0 ( + echo Mage is already installed. + GOTO DOWNLOAD +) + +go version >nul 2>&1 +IF NOT %ERRORLEVEL% EQU 0 ( + echo Go is not installed. Please install Go and try again. + exit /b 1 +) + +echo Installing Mage... +go install github.com/magefile/mage@latest + +mage -version >nul 2>&1 +IF NOT %ERRORLEVEL% EQU 0 ( + echo Mage installation failed. + echo Please ensure that %GOPATH%/bin is in your PATH. + exit /b 1 +) + +echo Mage installed successfully. + +:DOWNLOAD +go mod download + +ENDLOCAL diff --git a/pkg/util/mageutil/usage-guide/bootstrap.sh b/pkg/util/mageutil/usage-guide/bootstrap.sh new file mode 100644 index 000000000..f79cd1f11 --- /dev/null +++ b/pkg/util/mageutil/usage-guide/bootstrap.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +if [[ ":$PATH:" == *":$HOME/.local/bin:"* ]]; then + TARGET_DIR="$HOME/.local/bin" +else + TARGET_DIR="/usr/local/bin" + echo "Using /usr/local/bin as the installation directory. Might require sudo permissions." +fi + +if ! command -v mage &> /dev/null; then + echo "Installing Mage to $TARGET_DIR ..." + GOBIN=$TARGET_DIR go install github.com/magefile/mage@latest +fi + +if ! command -v mage &> /dev/null; then + echo "Mage installation failed." + echo "Please ensure that $TARGET_DIR is in your \$PATH." + exit 1 +fi + +echo "Mage installed successfully." + +go mod download diff --git a/pkg/util/mageutil/usage-guide/magefile.go b/pkg/util/mageutil/usage-guide/magefile.go new file mode 100644 index 000000000..47a841297 --- /dev/null +++ b/pkg/util/mageutil/usage-guide/magefile.go @@ -0,0 +1,38 @@ +//go:build mage +// +build mage + +package main + +import ( + "github.com/openimsdk/tools/utils/mageutil" + "os" + "strings" +) + +var Default = Build + +func Build() { + platforms := os.Getenv("PLATFORMS") + if platforms == "" { + platforms = mageutil.DetectPlatform() + } + + for _, platform := range strings.Split(platforms, " ") { + mageutil.CompileForPlatform(platform) + } + + mageutil.PrintGreen("Compilation complete.") +} + +func Start() { + setMaxOpenFiles() + mageutil.StartToolsAndServices() +} + +func Stop() { + mageutil.StopAndCheckBinaries() +} + +func Check() { + mageutil.CheckAndReportBinariesStatus() +} diff --git a/pkg/util/mageutil/usage-guide/magefile_unix.go b/pkg/util/mageutil/usage-guide/magefile_unix.go new file mode 100644 index 000000000..76c6cad07 --- /dev/null +++ b/pkg/util/mageutil/usage-guide/magefile_unix.go @@ -0,0 +1,20 @@ +//go:build mage && !windows +// +build mage,!windows + +package main + +import ( + "github.com/openimsdk/tools/utils/mageutil" + "syscall" +) + +func setMaxOpenFiles() error { + var rLimit syscall.Rlimit + err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) + if err != nil { + return err + } + rLimit.Max = uint64(mageutil.MaxFileDescriptors) + rLimit.Cur = uint64(mageutil.MaxFileDescriptors) + return syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) +} diff --git a/pkg/util/mageutil/usage-guide/magefile_windows.go b/pkg/util/mageutil/usage-guide/magefile_windows.go new file mode 100644 index 000000000..7441bfd9c --- /dev/null +++ b/pkg/util/mageutil/usage-guide/magefile_windows.go @@ -0,0 +1,8 @@ +//go:build mage +// +build mage + +package main + +func setMaxOpenFiles() error { + return nil +} diff --git a/pkg/util/mageutil/usage-guide/start-config.yml b/pkg/util/mageutil/usage-guide/start-config.yml new file mode 100644 index 000000000..8c9f3b69d --- /dev/null +++ b/pkg/util/mageutil/usage-guide/start-config.yml @@ -0,0 +1,8 @@ +serviceBinaries: + openim-no-port: 1 + openim-test: 1 + +toolBinaries: + - check-free-memory + +maxFileDescriptors: 10000 diff --git a/tools/check-component/main.go b/tools/check-component/main.go index af3705faf..718a07c33 100644 --- a/tools/check-component/main.go +++ b/tools/check-component/main.go @@ -24,7 +24,6 @@ import ( "github.com/openimsdk/tools/db/redisutil" "github.com/openimsdk/tools/discovery/zookeeper" "github.com/openimsdk/tools/mq/kafka" - "github.com/openimsdk/tools/s3/minio" "github.com/openimsdk/tools/system/program" "path/filepath" "time" @@ -45,8 +44,8 @@ func CheckRedis(ctx context.Context, config *config.Redis) error { } func CheckMinIO(ctx context.Context, config *config.Minio) error { - return minio.Check() - + //return minio.Check() + return nil } func CheckKafka(ctx context.Context, conf *config.Kafka) error { diff --git a/tools/url2im/pkg/api.go b/tools/url2im/pkg/api.go index cd7ce7027..5bf48c4ea 100644 --- a/tools/url2im/pkg/api.go +++ b/tools/url2im/pkg/api.go @@ -25,6 +25,7 @@ import ( "github.com/openimsdk/protocol/auth" "github.com/openimsdk/protocol/constant" "github.com/openimsdk/protocol/third" + "github.com/openimsdk/tools/errs" ) type Api struct {