1
0
mirror of https://github.com/1Panel-dev/1Panel.git synced 2025-03-14 01:34:47 +08:00

fix: 解决守护进程停止之后状态读取异常的问题 (#2186)

Refs https://github.com/1Panel-dev/1Panel/issues/2167
This commit is contained in:
zhengkunwang 2023-09-05 16:12:10 +08:00 committed by GitHub
parent 38b462eece
commit ed3d587046
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 63 additions and 94 deletions

View File

@ -189,21 +189,6 @@ func (b *BaseApi) GetProcess(c *gin.Context) {
helper.SuccessWithData(c, configs) helper.SuccessWithData(c, configs)
} }
// @Tags Host tool
// @Summary Load Supervisor process status
// @Description 获取 Supervisor 进程状态
// @Success 200 {array} response.ProcessStatus
// @Security ApiKeyAuth
// @Router /host/tool/supervisor/process/load [post]
func (b *BaseApi) LoadProcessStatus(c *gin.Context) {
datas, err := hostToolService.LoadProcessStatus()
if err != nil {
helper.ErrorWithDetail(c, constant.CodeErrInternalServer, constant.ErrTypeInternalServer, err)
return
}
helper.SuccessWithData(c, datas)
}
// @Tags Host tool // @Tags Host tool
// @Summary Get Supervisor process config // @Summary Get Supervisor process config
// @Description 操作 Supervisor 进程文件 // @Description 操作 Supervisor 进程文件

View File

@ -3,14 +3,6 @@ package service
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"os/exec"
"os/user"
"path"
"strconv"
"strings"
"sync"
"time"
"github.com/1Panel-dev/1Panel/backend/app/dto/request" "github.com/1Panel-dev/1Panel/backend/app/dto/request"
"github.com/1Panel-dev/1Panel/backend/app/dto/response" "github.com/1Panel-dev/1Panel/backend/app/dto/response"
"github.com/1Panel-dev/1Panel/backend/buserr" "github.com/1Panel-dev/1Panel/backend/buserr"
@ -22,6 +14,11 @@ import (
"github.com/1Panel-dev/1Panel/backend/utils/systemctl" "github.com/1Panel-dev/1Panel/backend/utils/systemctl"
"github.com/pkg/errors" "github.com/pkg/errors"
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"os/exec"
"os/user"
"path"
"strconv"
"strings"
) )
type HostToolService struct{} type HostToolService struct{}
@ -34,7 +31,6 @@ type IHostToolService interface {
GetToolLog(req request.HostToolLogReq) (string, error) GetToolLog(req request.HostToolLogReq) (string, error)
OperateSupervisorProcess(req request.SupervisorProcessConfig) error OperateSupervisorProcess(req request.SupervisorProcessConfig) error
GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error) GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error)
LoadProcessStatus() ([]response.ProcessStatus, error)
OperateSupervisorProcessFile(req request.SupervisorProcessFileReq) (string, error) OperateSupervisorProcessFile(req request.SupervisorProcessFileReq) (string, error)
} }
@ -377,56 +373,6 @@ func (h *HostToolService) OperateSupervisorProcess(req request.SupervisorProcess
return nil return nil
} }
func (h *HostToolService) LoadProcessStatus() ([]response.ProcessStatus, error) {
var res []response.ProcessStatus
statusLines, _ := cmd.Exec("supervisorctl status")
if len(statusLines) == 0 {
return res, nil
}
lines := strings.Split(statusLines, "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) > 1 {
res = append(res, response.ProcessStatus{Name: fields[0]})
}
}
var wg sync.WaitGroup
wg.Add(len(res))
for i := 0; i < len(res); i++ {
go func(index int) {
for t := 0; t < 3; t++ {
status, err := cmd.ExecWithTimeOut(fmt.Sprintf("supervisorctl status %s", res[index].Name), 2*time.Second)
if err != nil {
time.Sleep(2 * time.Second)
continue
}
fields := strings.Fields(status)
if len(fields) < 5 {
time.Sleep(2 * time.Second)
continue
}
res[index].Name = fields[0]
res[index].Status = fields[1]
if fields[1] != "RUNNING" {
res[index].Msg = strings.Join(fields[2:], " ")
break
}
res[index].PID = strings.TrimSuffix(fields[3], ",")
res[index].Uptime = fields[5]
break
}
if len(res[index].Status) == 0 {
res[index].Status = "FATAL"
res[index].Msg = "Timeout for getting process status"
}
wg.Done()
}(i)
}
wg.Wait()
return res, nil
}
func (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error) { func (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorProcessConfig, error) {
var ( var (
result []response.SupervisorProcessConfig result []response.SupervisorProcessConfig
@ -463,6 +409,7 @@ func (h *HostToolService) GetSupervisorProcessConfig() ([]response.SupervisorPro
if numprocs, _ := section.GetKey("numprocs"); numprocs != nil { if numprocs, _ := section.GetKey("numprocs"); numprocs != nil {
config.Numprocs = numprocs.Value() config.Numprocs = numprocs.Value()
} }
_ = getProcessStatus(&config)
result = append(result, config) result = append(result, config)
} }
} }
@ -590,3 +537,29 @@ func getProcessName(name, numprocs string) []string {
} }
return processNames return processNames
} }
func getProcessStatus(config *response.SupervisorProcessConfig) error {
var (
processNames = []string{"status"}
)
processNames = append(processNames, getProcessName(config.Name, config.Numprocs)...)
output, _ := exec.Command("supervisorctl", processNames...).Output()
lines := strings.Split(string(output), "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) >= 5 {
status := response.ProcessStatus{
Name: fields[0],
Status: fields[1],
}
if fields[1] == "RUNNING" {
status.PID = strings.TrimSuffix(fields[3], ",")
status.Uptime = fields[5]
} else {
status.Msg = strings.Join(fields[2:], " ")
}
config.Status = append(config.Status, status)
}
}
return nil
}

View File

@ -55,7 +55,6 @@ func (s *HostRouter) InitHostRouter(Router *gin.RouterGroup) {
hostRouter.POST("/tool/operate", baseApi.OperateTool) hostRouter.POST("/tool/operate", baseApi.OperateTool)
hostRouter.POST("/tool/config", baseApi.OperateToolConfig) hostRouter.POST("/tool/config", baseApi.OperateToolConfig)
hostRouter.POST("/tool/log", baseApi.GetToolLog) hostRouter.POST("/tool/log", baseApi.GetToolLog)
hostRouter.POST("/tool/supervisor/process/load", baseApi.LoadProcessStatus)
hostRouter.POST("/tool/supervisor/process", baseApi.OperateProcess) hostRouter.POST("/tool/supervisor/process", baseApi.OperateProcess)
hostRouter.GET("/tool/supervisor/process", baseApi.GetProcess) hostRouter.GET("/tool/supervisor/process", baseApi.GetProcess)
hostRouter.POST("/tool/supervisor/process/file", baseApi.GetProcessFile) hostRouter.POST("/tool/supervisor/process/file", baseApi.GetProcessFile)

View File

@ -34,7 +34,7 @@ export const LoadProcessStatus = () => {
}; };
export const GetSupervisorProcess = () => { export const GetSupervisorProcess = () => {
return http.get<HostTool.SupersivorProcess>(`/hosts/tool/supervisor/process`); return http.get<HostTool.SupersivorProcess[]>(`/hosts/tool/supervisor/process`);
}; };
export const OperateSupervisorProcessFile = (req: HostTool.ProcessFileReq) => { export const OperateSupervisorProcessFile = (req: HostTool.ProcessFileReq) => {

View File

@ -157,7 +157,7 @@ import ConfigSuperVisor from './config/index.vue';
import { computed, onMounted } from 'vue'; import { computed, onMounted } from 'vue';
import Create from './create/index.vue'; import Create from './create/index.vue';
import File from './file/index.vue'; import File from './file/index.vue';
import { GetSupervisorProcess, LoadProcessStatus, OperateSupervisorProcess } from '@/api/modules/host-tool'; import { GetSupervisorProcess, OperateSupervisorProcess } from '@/api/modules/host-tool';
import { GlobalStore } from '@/store'; import { GlobalStore } from '@/store';
import i18n from '@/lang'; import i18n from '@/lang';
import { HostTool } from '@/api/interface/host-tool'; import { HostTool } from '@/api/interface/host-tool';
@ -216,34 +216,46 @@ const search = async () => {
return; return;
} }
loading.value = true; loading.value = true;
loadStatus(); let needLoadStatus = false;
try { try {
const res = await GetSupervisorProcess(); const res = await GetSupervisorProcess();
data.value = res.data; data.value = res.data;
for (const process of data.value) {
if (process.status && process.status.length > 0) {
process.hasLoad = true;
} else {
process.hasLoad = false;
needLoadStatus = true;
}
}
if (needLoadStatus) {
setTimeout(loadStatus, 1000);
}
} catch (error) {} } catch (error) {}
loading.value = false; loading.value = false;
}; };
const loadStatus = async () => { const loadStatus = async () => {
await LoadProcessStatus() let needLoadStatus = false;
.then((res) => { try {
let stats = res.data || []; const res = await GetSupervisorProcess();
for (const process of data.value) { const stats = res.data || [];
process.status = []; for (const process of data.value) {
for (const item of stats) { for (const item of stats) {
if (process.name === item.name.split(':')[0]) { if (process.name === item.name) {
process.status.push(item); if (item.status && item.status.length > 0) {
process.status = item.status;
process.hasLoad = true;
} else {
needLoadStatus = true;
} }
} }
process.hasLoad = true;
} }
}) }
.catch(() => { if (needLoadStatus) {
for (const process of data.value) { setTimeout(loadStatus, 2000);
process.status = [{ name: '-', status: 'FATAL', msg: i18n.global.t('tool.supervisor.loadStatusErr') }]; }
process.hasLoad = true; } catch (error) {}
}
});
}; };
const mobile = computed(() => { const mobile = computed(() => {