98 lines
2.4 KiB
Go
98 lines
2.4 KiB
Go
package config
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"sync"
|
|
|
|
"douyu_go/internal/constants"
|
|
"douyu_go/internal/types"
|
|
)
|
|
|
|
const maxInstanceSlots = 256
|
|
|
|
// InstanceLease 代表当前进程持有的实例槽位;必须在程序退出时释放。
|
|
type InstanceLease struct {
|
|
paths types.AppPaths
|
|
name string
|
|
lock instanceLock
|
|
releaseOnce sync.Once
|
|
}
|
|
|
|
// AcquireInstancePaths 按顺序占用空闲实例。第一个进程使用默认实例,
|
|
// 后续进程分别使用实例 2、实例 3,以隔离账号、配置与运行数据。
|
|
func AcquireInstancePaths() (*InstanceLease, error) {
|
|
baseDir := defaultDataDir()
|
|
instanceRoot := filepath.Join(baseDir, "instances")
|
|
lockDir := filepath.Join(instanceRoot, ".locks")
|
|
if err := osMkdirAll(lockDir); err != nil {
|
|
return nil, fmt.Errorf("创建实例锁目录失败: %w", err)
|
|
}
|
|
|
|
for slot := 1; slot <= maxInstanceSlots; slot++ {
|
|
lock, acquired, err := tryAcquireInstanceLock(filepath.Join(lockDir, instanceLockFileName(slot)))
|
|
if err != nil {
|
|
return nil, fmt.Errorf("锁定%s失败: %w", instanceName(slot), err)
|
|
}
|
|
if !acquired {
|
|
continue
|
|
}
|
|
|
|
dataDir := baseDir
|
|
if slot > 1 {
|
|
dataDir = filepath.Join(instanceRoot, fmt.Sprintf("instance-%d", slot))
|
|
}
|
|
paths := appPathsForDataDir(dataDir, slot == 1)
|
|
if slot > 1 && isWailsDev() && os.Getenv("DOUYU_LOG_DIR") == "" {
|
|
paths.LogDir = resolvePath(filepath.Join(constants.DefaultLogDir, "instances", fmt.Sprintf("instance-%d", slot)))
|
|
}
|
|
return &InstanceLease{
|
|
paths: paths,
|
|
name: instanceName(slot),
|
|
lock: lock,
|
|
}, nil
|
|
}
|
|
return nil, fmt.Errorf("最多支持同时运行 %d 个实例", maxInstanceSlots)
|
|
}
|
|
|
|
// Paths 返回当前实例独立的数据路径。
|
|
func (l *InstanceLease) Paths() types.AppPaths {
|
|
if l == nil {
|
|
return types.AppPaths{}
|
|
}
|
|
return l.paths
|
|
}
|
|
|
|
// Name 返回面向用户的实例名称。
|
|
func (l *InstanceLease) Name() string {
|
|
if l == nil {
|
|
return ""
|
|
}
|
|
return l.name
|
|
}
|
|
|
|
// Release 释放实例锁,允许新启动的程序复用该实例。
|
|
func (l *InstanceLease) Release() {
|
|
if l == nil || l.lock == nil {
|
|
return
|
|
}
|
|
l.releaseOnce.Do(func() {
|
|
_ = l.lock.Close()
|
|
})
|
|
}
|
|
|
|
func instanceName(slot int) string {
|
|
if slot <= 1 {
|
|
return "默认实例"
|
|
}
|
|
return fmt.Sprintf("实例 %d", slot)
|
|
}
|
|
|
|
func instanceLockFileName(slot int) string {
|
|
if slot <= 1 {
|
|
return "default.lock"
|
|
}
|
|
return fmt.Sprintf("instance-%d.lock", slot)
|
|
}
|