Files
flvx/go-gost/x/socket/service.go
T
sagit eec6cb4298 fix(agent): add port cleanup before resuming paused services (#398)
When user traffic quota is exceeded, services are paused with
ForceClosePortConnections to kill active connections. However,
when admin resets quota and resumes services, the resume logic
was missing this cleanup, causing "address already in use" errors.

Changes:
- Add ForceClosePortConnections call in resumeServices (socket & api)
- Add ForceClosePortConnections call in resumeService (api single)
- Increase wait time from 100ms to 500ms for port release

Fixes #387
2026-03-31 10:28:28 +08:00

540 lines
13 KiB
Go

package socket
import (
"errors"
"fmt"
"strings"
"time"
"github.com/go-gost/core/service"
"github.com/go-gost/x/config"
parser "github.com/go-gost/x/config/parsing/service"
kill "github.com/go-gost/x/internal/util/port"
"github.com/go-gost/x/registry"
)
func createServices(req createServicesRequest) error {
if len(req.Data) == 0 {
return errors.New("services list cannot be empty")
}
// 第一阶段:验证所有服务配置
var parsedServices []struct {
config config.ServiceConfig
service service.Service
}
for _, serviceConfig := range req.Data {
name := strings.TrimSpace(serviceConfig.Name)
if name == "" {
return errors.New("service name is required")
}
serviceConfig.Name = name
if registry.ServiceRegistry().IsRegistered(name) {
return errors.New("service " + name + " already exists")
}
svc, err := parser.ParseService(&serviceConfig)
if err != nil {
return errors.New("create service " + name + " failed: " + err.Error())
}
parsedServices = append(parsedServices, struct {
config config.ServiceConfig
service service.Service
}{serviceConfig, svc})
}
// 第二阶段:注册所有服务
var registeredServices []string
for _, ps := range parsedServices {
if err := registry.ServiceRegistry().Register(ps.config.Name, ps.service); err != nil {
// 如果注册失败,回滚已注册的服务
for _, regName := range registeredServices {
if svc := registry.ServiceRegistry().Get(regName); svc != nil {
registry.ServiceRegistry().Unregister(regName)
svc.Close()
}
}
return errors.New("service " + ps.config.Name + " already exists")
}
registeredServices = append(registeredServices, ps.config.Name)
}
// 第三阶段:启动所有服务
for _, ps := range parsedServices {
if svc := registry.ServiceRegistry().Get(ps.config.Name); svc != nil {
go svc.Serve()
}
}
// 第四阶段:更新配置
config.OnUpdate(func(c *config.Config) error {
for _, ps := range parsedServices {
c.Services = append(c.Services, &ps.config)
}
return nil
})
return nil
}
func updateServices(req updateServicesRequest) error {
if len(req.Data) == 0 {
return errors.New("services list cannot be empty")
}
// 第一阶段:验证所有服务名称有效性
for i := range req.Data {
name := strings.TrimSpace(req.Data[i].Name)
if name == "" {
return errors.New("service name is required")
}
req.Data[i].Name = name
}
// 第二阶段:逐个更新服务(Upsert模式:存在则更新,不存在则创建)
for i := range req.Data {
serviceConfig := &req.Data[i]
name := serviceConfig.Name
// 1. 获取旧服务
old := registry.ServiceRegistry().Get(name)
// 2. 关闭旧服务 (如果存在)
if old != nil {
old.Close()
// 3. 从注册表移除旧服务
registry.ServiceRegistry().Unregister(name)
}
// 4. 解析新服务配置
svc, err := parser.ParseService(serviceConfig)
if err != nil {
return errors.New("create service " + name + " failed: " + err.Error())
}
// 5. 注册新服务
if err := registry.ServiceRegistry().Register(name, svc); err != nil {
svc.Close()
return errors.New("service " + name + " already exists")
}
// 6. 启动新服务
go svc.Serve()
}
// 第三阶段:更新配置
config.OnUpdate(func(c *config.Config) error {
for i := range req.Data {
// 创建副本以确保指针安全
cfgCopy := req.Data[i]
found := false
for j := range c.Services {
if c.Services[j].Name == cfgCopy.Name {
c.Services[j] = &cfgCopy
found = true
break
}
}
if !found {
c.Services = append(c.Services, &cfgCopy)
}
}
return nil
})
return nil
}
func deleteServices(req deleteServicesRequest) error {
if len(req.Services) == 0 {
return errors.New("services list cannot be empty")
}
// 第一阶段:验证所有服务是否存在
var servicesToDelete []struct {
name string
service service.Service
}
var namesToRemove []string
for _, serviceName := range req.Services {
name := strings.TrimSpace(serviceName)
if name == "" {
return errors.New("service name is required")
}
namesToRemove = append(namesToRemove, name)
svc := registry.ServiceRegistry().Get(name)
if svc != nil {
servicesToDelete = append(servicesToDelete, struct {
name string
service service.Service
}{name, svc})
}
}
// 第二阶段:删除所有服务
for _, std := range servicesToDelete {
registry.ServiceRegistry().Unregister(std.name)
std.service.Close()
}
// 确保所有请求删除的服务都从注册表中移除(即使之前未找到实例)
for _, name := range namesToRemove {
if registry.ServiceRegistry().IsRegistered(name) {
registry.ServiceRegistry().Unregister(name)
}
}
// 第三阶段:更新配置
config.OnUpdate(func(c *config.Config) error {
services := c.Services
c.Services = nil
for _, s := range services {
shouldDelete := false
for _, name := range namesToRemove {
if s.Name == name {
shouldDelete = true
break
}
}
if !shouldDelete {
c.Services = append(c.Services, s)
}
}
return nil
})
return nil
}
func pauseServices(req pauseServicesRequest) error {
if len(req.Services) == 0 {
return errors.New("services list cannot be empty")
}
// 第一阶段:验证所有服务是否存在,并筛选需要暂停的服务
var servicesToPause []struct {
name string
service service.Service
}
//var skippedServices []string
cfg := config.Global()
for _, serviceName := range req.Services {
name := strings.TrimSpace(serviceName)
if name == "" {
return errors.New("service name is required")
}
svc := registry.ServiceRegistry().Get(name)
if svc == nil {
return errors.New(fmt.Sprintf("service %s not found", name))
}
//// 检查服务是否已经暂停
//var serviceConfig *config.ServiceConfig
//for _, s := range cfg.Services {
// if s.Name == name {
// serviceConfig = s
// break
// }
//}
//
//// 如果服务已经暂停,跳过
//if serviceConfig != nil && serviceConfig.Metadata != nil {
// if pausedVal, exists := serviceConfig.Metadata["paused"]; exists && pausedVal == true {
// skippedServices = append(skippedServices, name)
// continue
// }
//}
servicesToPause = append(servicesToPause, struct {
name string
service service.Service
}{name, svc})
}
// 第二阶段:事务性暂停所有服务
var pausedServices []struct {
name string
service service.Service
serviceConfig *config.ServiceConfig
}
// 获取服务配置
serviceConfigs := make(map[string]*config.ServiceConfig)
for _, s := range cfg.Services {
serviceConfigs[s.Name] = s
}
// 逐个暂停服务,如果失败则回滚
for _, stp := range servicesToPause {
serviceConfig := serviceConfigs[stp.name]
if serviceConfig == nil {
// 找不到配置,回滚已暂停的服务
rollbackPausedServices(pausedServices)
return errors.New(fmt.Sprintf("service %s configuration not found", stp.name))
}
// 暂停服务
stp.service.Close()
// 强制断开端口的所有连接
if serviceConfig.Addr != "" {
_ = kill.ForceClosePortConnections(serviceConfig.Addr)
}
// 记录已暂停的服务
pausedServices = append(pausedServices, struct {
name string
service service.Service
serviceConfig *config.ServiceConfig
}{stp.name, stp.service, serviceConfig})
}
// 第三阶段:更新配置,标记暂停状态
err := config.OnUpdate(func(c *config.Config) error {
for _, stp := range servicesToPause {
for i := range c.Services {
if c.Services[i].Name == stp.name {
if c.Services[i].Metadata == nil {
c.Services[i].Metadata = make(map[string]any)
}
c.Services[i].Metadata["paused"] = true
break
}
}
}
return nil
})
if err != nil {
// 配置更新失败,需要回滚所有暂停的服务
rollbackPausedServices(pausedServices)
return errors.New(fmt.Sprintf("Failed to update config, rolling back paused services: %v", err))
}
return nil
}
func resumeServices(req resumeServicesRequest) error {
if len(req.Services) == 0 {
return errors.New("services list cannot be empty")
}
// 第一阶段:验证所有服务是否存在,并筛选需要恢复的服务
var servicesToResume []struct {
name string
service service.Service
serviceConfig *config.ServiceConfig
}
var skippedServices []string
cfg := config.Global()
for _, serviceName := range req.Services {
name := strings.TrimSpace(serviceName)
if name == "" {
return errors.New("service name is required")
}
// 检查服务是否存在
svc := registry.ServiceRegistry().Get(name)
if svc == nil {
return errors.New(fmt.Sprintf("service %s not found", name))
}
// 查找配置中的服务
var serviceConfig *config.ServiceConfig
for _, s := range cfg.Services {
if s.Name == name {
serviceConfig = s
break
}
}
if serviceConfig == nil {
return errors.New(fmt.Sprintf("service %s configuration not found", name))
}
// 检查是否处于暂停状态
paused := false
if serviceConfig.Metadata != nil {
if pausedVal, exists := serviceConfig.Metadata["paused"]; exists && pausedVal == true {
paused = true
}
}
// 如果服务没有暂停(即正在运行),跳过
if !paused {
skippedServices = append(skippedServices, name)
continue
}
servicesToResume = append(servicesToResume, struct {
name string
service service.Service
serviceConfig *config.ServiceConfig
}{name, svc, serviceConfig})
}
// 第二阶段:事务性恢复所有服务
var resumedServices []struct {
name string
service service.Service
serviceConfig *config.ServiceConfig
}
// 逐个恢复服务,如果失败则回滚
for _, str := range servicesToResume {
// 先关闭现有服务
str.service.Close()
registry.ServiceRegistry().Unregister(str.name)
// 强制断开端口的所有连接
if str.serviceConfig.Addr != "" {
_ = kill.ForceClosePortConnections(str.serviceConfig.Addr)
}
// 等待端口释放
time.Sleep(500 * time.Millisecond)
// 重新解析并启动服务
svc, err := parser.ParseService(str.serviceConfig)
if err != nil {
// 恢复失败,回滚已恢复的服务
rollbackResumedServices(resumedServices)
return errors.New(fmt.Sprintf("resume service %s failed: %s", str.name, err.Error()))
}
if err := registry.ServiceRegistry().Register(str.name, svc); err != nil {
svc.Close()
// 恢复失败,回滚已恢复的服务
rollbackResumedServices(resumedServices)
return errors.New(fmt.Sprintf("service %s already exists", str.name))
}
go svc.Serve()
// 记录已成功恢复的服务
resumedServices = append(resumedServices, str)
}
// 第三阶段:更新配置,移除暂停状态
err := config.OnUpdate(func(c *config.Config) error {
for _, str := range servicesToResume {
for i := range c.Services {
if c.Services[i].Name == str.name {
if c.Services[i].Metadata != nil {
delete(c.Services[i].Metadata, "paused")
// 如果 metadata 为空,设置为 nil
if len(c.Services[i].Metadata) == 0 {
c.Services[i].Metadata = nil
}
}
break
}
}
}
return nil
})
if err != nil {
// 配置更新失败,回滚所有已恢复的服务
rollbackResumedServices(resumedServices)
return errors.New(fmt.Sprintf("Failed to update config, rolling back resumed services: %v", err))
}
return nil
}
func rollbackPausedServices(pausedServices []struct {
name string
service service.Service
serviceConfig *config.ServiceConfig
}) {
for _, pss := range pausedServices {
// 重新解析并启动服务
svc, err := parser.ParseService(pss.serviceConfig)
if err != nil {
continue // 回滚失败,记录日志但继续处理其他服务
}
if err := registry.ServiceRegistry().Register(pss.name, svc); err != nil {
svc.Close()
continue // 回滚失败,记录日志但继续处理其他服务
}
go svc.Serve()
// 移除暂停状态标记
config.OnUpdate(func(c *config.Config) error {
for i := range c.Services {
if c.Services[i].Name == pss.name {
if c.Services[i].Metadata != nil {
delete(c.Services[i].Metadata, "paused")
if len(c.Services[i].Metadata) == 0 {
c.Services[i].Metadata = nil
}
}
break
}
}
return nil
})
}
}
func rollbackResumedServices(resumedServices []struct {
name string
service service.Service
serviceConfig *config.ServiceConfig
}) {
for _, rss := range resumedServices {
// 关闭已恢复的服务
if svc := registry.ServiceRegistry().Get(rss.name); svc != nil {
svc.Close()
}
// 重新标记为暂停状态
config.OnUpdate(func(c *config.Config) error {
for i := range c.Services {
if c.Services[i].Name == rss.name {
if c.Services[i].Metadata == nil {
c.Services[i].Metadata = make(map[string]any)
}
c.Services[i].Metadata["paused"] = true
break
}
}
return nil
})
}
}
type resumeServicesRequest struct {
Services []string `json:"services"`
}
type pauseServicesRequest struct {
Services []string `json:"services"`
}
type deleteServicesRequest struct {
Services []string `json:"services"`
}
type updateServicesRequest struct {
Data []config.ServiceConfig `json:"data"`
}
type createServicesRequest struct {
Data []config.ServiceConfig `json:"data"`
}