From 8bdf02c4c0e3106b3fb8f0f18b5255727cdcdf6d Mon Sep 17 00:00:00 2001 From: CityFun <31820853+zhengkunwang223@users.noreply.github.com> Date: Wed, 10 Sep 2025 17:02:53 +0800 Subject: [PATCH] perf: Optimize process management performance (#10329) --- agent/app/api/v2/helper/helper.go | 10 +- agent/app/api/v2/process.go | 21 ++++ agent/app/service/process.go | 110 +++++++++++++++++- agent/router/ro_process.go | 1 + agent/utils/websocket/process_data.go | 58 ++------- frontend/src/api/interface/process.ts | 48 ++++++++ frontend/src/api/modules/process.ts | 4 + .../host/process/process/detail/index.vue | 36 +++--- .../src/views/host/process/process/index.vue | 2 +- 9 files changed, 223 insertions(+), 67 deletions(-) diff --git a/agent/app/api/v2/helper/helper.go b/agent/app/api/v2/helper/helper.go index 84babef6b..6a4f0e8c8 100644 --- a/agent/app/api/v2/helper/helper.go +++ b/agent/app/api/v2/helper/helper.go @@ -116,7 +116,11 @@ func CheckBind(req interface{}, c *gin.Context) error { return nil } -func ErrResponse(ctx *gin.Context, code int) { - ctx.JSON(code, nil) - ctx.Abort() +func GetParamInt32(paramName string, c *gin.Context) (int32, error) { + idParam, ok := c.Params.Get(paramName) + if !ok { + return 0, errors.New("error id in path") + } + intNum, _ := strconv.Atoi(idParam) + return int32(intNum), nil } diff --git a/agent/app/api/v2/process.go b/agent/app/api/v2/process.go index c92a18df4..a57de853a 100644 --- a/agent/app/api/v2/process.go +++ b/agent/app/api/v2/process.go @@ -36,3 +36,24 @@ func (b *BaseApi) StopProcess(c *gin.Context) { } helper.Success(c) } + +// @Tags Process +// @Summary Get Process Info By PID +// @Param pid path int true "PID" +// @Success 200 {object} websocket.PsProcessData +// @Security ApiKeyAuth +// @Security Timestamp +// @Router /process/{pid} [get] +func (b *BaseApi) GetProcessInfoByPID(c *gin.Context) { + pid, err := helper.GetParamInt32("pid", c) + if err != nil { + helper.BadRequest(c, err) + return + } + data, err := processService.GetProcessInfoByPID(pid) + if err != nil { + helper.BadRequest(c, err) + return + } + helper.SuccessWithData(c, data) +} diff --git a/agent/app/service/process.go b/agent/app/service/process.go index 86bc0c679..5c770b699 100644 --- a/agent/app/service/process.go +++ b/agent/app/service/process.go @@ -1,21 +1,26 @@ package service import ( + "fmt" "github.com/1Panel-dev/1Panel/agent/app/dto/request" + "github.com/1Panel-dev/1Panel/agent/utils/common" + "github.com/1Panel-dev/1Panel/agent/utils/websocket" "github.com/shirou/gopsutil/v4/process" + "time" ) type ProcessService struct{} type IProcessService interface { StopProcess(req request.ProcessReq) error + GetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error) } func NewIProcessService() IProcessService { return &ProcessService{} } -func (p *ProcessService) StopProcess(req request.ProcessReq) error { +func (ps *ProcessService) StopProcess(req request.ProcessReq) error { proc, err := process.NewProcess(req.PID) if err != nil { return err @@ -25,3 +30,106 @@ func (p *ProcessService) StopProcess(req request.ProcessReq) error { } return nil } + +func (ps *ProcessService) GetProcessInfoByPID(pid int32) (*websocket.PsProcessData, error) { + p, err := process.NewProcess(pid) + if err != nil { + return nil, fmt.Errorf("get process info by pid %v: %v", pid, err) + } + + exists, err := p.IsRunning() + if err != nil || !exists { + return nil, fmt.Errorf("process %v is not running", pid) + } + + data := &websocket.PsProcessData{ + PID: pid, + } + + if name, err := p.Name(); err == nil { + data.Name = name + } + + if ppid, err := p.Ppid(); err == nil { + data.PPID = ppid + } + + if username, err := p.Username(); err == nil { + data.Username = username + } + + if status, err := p.Status(); err == nil { + if len(status) > 0 { + data.Status = status[0] + } + } + + if createTime, err := p.CreateTime(); err == nil { + data.StartTime = time.Unix(createTime/1000, 0).Format("2006-01-02 15:04:05") + } + + if numThreads, err := p.NumThreads(); err == nil { + data.NumThreads = numThreads + } + + if connections, err := p.Connections(); err == nil { + data.NumConnections = len(connections) + + var connects []websocket.ProcessConnect + for _, conn := range connections { + pc := websocket.ProcessConnect{ + Status: conn.Status, + Laddr: conn.Laddr, + Raddr: conn.Raddr, + PID: pid, + Name: data.Name, + } + connects = append(connects, pc) + } + data.Connects = connects + } + + if cpuPercent, err := p.CPUPercent(); err == nil { + data.CpuValue = cpuPercent + data.CpuPercent = fmt.Sprintf("%.2f%%", cpuPercent) + } + + if ioCounters, err := p.IOCounters(); err == nil { + data.DiskRead = common.FormatBytes(ioCounters.ReadBytes) + data.DiskWrite = common.FormatBytes(ioCounters.WriteBytes) + } + + if cmdline, err := p.Cmdline(); err == nil { + data.CmdLine = cmdline + } + + if memInfo, err := p.MemoryInfo(); err == nil { + data.Rss = common.FormatBytes(memInfo.RSS) + data.VMS = common.FormatBytes(memInfo.VMS) + data.HWM = common.FormatBytes(memInfo.HWM) + data.Data = common.FormatBytes(memInfo.Data) + data.Stack = common.FormatBytes(memInfo.Stack) + data.Locked = common.FormatBytes(memInfo.Locked) + data.Swap = common.FormatBytes(memInfo.Swap) + data.RssValue = memInfo.RSS + } else { + data.Rss = "--" + data.Data = "--" + data.VMS = "--" + data.HWM = "--" + data.Stack = "--" + data.Locked = "--" + data.Swap = "--" + data.RssValue = 0 + } + + if envs, err := p.Environ(); err == nil { + data.Envs = envs + } + + if openFiles, err := p.OpenFiles(); err == nil { + data.OpenFiles = openFiles + } + + return data, nil +} diff --git a/agent/router/ro_process.go b/agent/router/ro_process.go index 01c2fd8b8..32e15b91d 100644 --- a/agent/router/ro_process.go +++ b/agent/router/ro_process.go @@ -14,5 +14,6 @@ func (f *ProcessRouter) InitRouter(Router *gin.RouterGroup) { { processRouter.GET("/ws", baseApi.ProcessWs) processRouter.POST("/stop", baseApi.StopProcess) + processRouter.GET("/:pid", baseApi.GetProcessInfoByPID) } } diff --git a/agent/utils/websocket/process_data.go b/agent/utils/websocket/process_data.go index 343b6a094..f90b73836 100644 --- a/agent/utils/websocket/process_data.go +++ b/agent/utils/websocket/process_data.go @@ -3,13 +3,13 @@ package websocket import ( "encoding/json" "fmt" + "github.com/1Panel-dev/1Panel/agent/utils/common" "sort" "strings" "sync" "time" "github.com/1Panel-dev/1Panel/agent/global" - "github.com/1Panel-dev/1Panel/agent/utils/common" "github.com/1Panel-dev/1Panel/agent/utils/files" "github.com/shirou/gopsutil/v4/host" "github.com/shirou/gopsutil/v4/net" @@ -74,10 +74,10 @@ type PsProcessData struct { Envs []string `json:"envs"` OpenFiles []process.OpenFilesStat `json:"openFiles"` - Connects []processConnect `json:"connects"` + Connects []ProcessConnect `json:"connects"` } -type processConnect struct { +type ProcessConnect struct { Type string `json:"type"` Status string `json:"status"` Laddr net.Addr `json:"localaddr"` @@ -86,7 +86,7 @@ type processConnect struct { Name string `json:"name"` } -type ProcessConnects []processConnect +type ProcessConnects []ProcessConnect type sshSession struct { Username string `json:"username"` @@ -196,53 +196,15 @@ func getProcessData(processConfig PsProcessConfig) (res []byte, err error) { procData.StartTime = t.Format("2006-1-2 15:04:05") } procData.NumThreads, _ = proc.NumThreads() - connections, procErr := proc.Connections() - if procErr == nil { - procData.NumConnections = len(connections) - for _, conn := range connections { - if conn.Laddr.IP != "" || conn.Raddr.IP != "" { - procData.Connects = append(procData.Connects, processConnect{ - Status: conn.Status, - Laddr: conn.Laddr, - Raddr: conn.Raddr, - }) - } - } - } procData.CpuValue, _ = proc.CPUPercent() procData.CpuPercent = fmt.Sprintf("%.2f", procData.CpuValue) + "%" - menInfo, procErr := proc.MemoryInfo() - if procErr == nil { - procData.Rss = common.FormatBytes(menInfo.RSS) - procData.RssValue = menInfo.RSS - procData.Data = common.FormatBytes(menInfo.Data) - procData.VMS = common.FormatBytes(menInfo.VMS) - procData.HWM = common.FormatBytes(menInfo.HWM) - procData.Stack = common.FormatBytes(menInfo.Stack) - procData.Locked = common.FormatBytes(menInfo.Locked) - procData.Swap = common.FormatBytes(menInfo.Swap) - } else { - procData.Rss = "--" - procData.Data = "--" - procData.VMS = "--" - procData.HWM = "--" - procData.Stack = "--" - procData.Locked = "--" - procData.Swap = "--" + if memInfo, err := proc.MemoryInfo(); err == nil { + procData.RssValue = memInfo.RSS + procData.Rss = common.FormatBytes(memInfo.RSS) + } else { procData.RssValue = 0 } - ioStat, procErr := proc.IOCounters() - if procErr == nil { - procData.DiskWrite = common.FormatBytes(ioStat.WriteBytes) - procData.DiskRead = common.FormatBytes(ioStat.ReadBytes) - } else { - procData.DiskWrite = "--" - procData.DiskRead = "--" - } - procData.CmdLine, _ = proc.Cmdline() - procData.OpenFiles, _ = proc.OpenFiles() - procData.Envs, _ = proc.Environ() resultMutex.Lock() result = append(result, procData) @@ -333,7 +295,7 @@ var netTypes = [...]string{"tcp", "udp"} func getNetConnections(config NetConfig) (res []byte, err error) { var ( - result []processConnect + result []ProcessConnect proc *process.Process ) for _, netType := range netTypes { @@ -352,7 +314,7 @@ func getNetConnections(config NetConfig) (res []byte, err error) { if config.Port > 0 && config.Port != conn.Laddr.Port && config.Port != conn.Raddr.Port { continue } - result = append(result, processConnect{ + result = append(result, ProcessConnect{ Type: netType, Status: conn.Status, Laddr: conn.Laddr, diff --git a/frontend/src/api/interface/process.ts b/frontend/src/api/interface/process.ts index 95e010fb7..869a2fe19 100644 --- a/frontend/src/api/interface/process.ts +++ b/frontend/src/api/interface/process.ts @@ -2,4 +2,52 @@ export namespace Process { export interface StopReq { PID: number; } + + export interface PsProcessData { + PID: number; + name: string; + PPID: number; + username: string; + status: string; + startTime: string; + numThreads: number; + numConnections: number; + cpuPercent: string; + + diskRead: string; + diskWrite: string; + cmdLine: string; + + rss: string; + vms: string; + hwm: string; + data: string; + stack: string; + locked: string; + swap: string; + + cpuValue: number; + rssValue: number; + + envs: string[]; + + openFiles: OpenFilesStat[]; + connects: ProcessConnect[]; + } + + export interface ProcessConnect { + type: string; + status: string; + localaddr: string; + remoteaddr: string; + PID: number; + name: string; + } + + export type ProcessConnects = ProcessConnect[]; + + export interface OpenFilesStat { + path: string; + fd: number; + } } diff --git a/frontend/src/api/modules/process.ts b/frontend/src/api/modules/process.ts index af9d737c9..28545abec 100644 --- a/frontend/src/api/modules/process.ts +++ b/frontend/src/api/modules/process.ts @@ -4,3 +4,7 @@ import { Process } from '../interface/process'; export const stopProcess = (req: Process.StopReq) => { return http.post(`/process/stop`, req); }; + +export const getProcessByID = (pid: number) => { + return http.get(`/process/${pid}`); +}; diff --git a/frontend/src/views/host/process/process/detail/index.vue b/frontend/src/views/host/process/process/detail/index.vue index d80621ebc..dd0184907 100644 --- a/frontend/src/views/host/process/process/detail/index.vue +++ b/frontend/src/views/host/process/process/detail/index.vue @@ -1,7 +1,7 @@