162 lines
6.4 KiB
Go
162 lines
6.4 KiB
Go
package config
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
core "git.yuntuo.hk/k2safe/orbit/model"
|
||
"net"
|
||
"os"
|
||
"path/filepath"
|
||
"strconv"
|
||
"time"
|
||
|
||
"git.yuntuo.hk/k2safe/orbit/internal/retention"
|
||
)
|
||
|
||
type Config struct {
|
||
ConsoleApplicationAdmin string // ConsoleApplicationAdmin 可跨应用管理接入的唯一控制台登录名;空时禁用。
|
||
ArchiveRoot string // ArchiveRoot 私有持久归档卷绝对路径,空表示关闭后台执行器。
|
||
ArchiveStorageID core.ID // ArchiveStorageID 32位小写十六进制卷身份,各节点须挂载同一物理卷。
|
||
ArchiveMaxBytes int64 // ArchiveMaxBytes 全卷文件预算,默认10GiB。
|
||
ArchiveTickInterval time.Duration // ArchiveTickInterval 后台扫描间隔,默认10秒。
|
||
RetentionDefaults retention.Policy // RetentionDefaults 未保存数据库策略时使用的部署默认值。
|
||
HTTPAddr string
|
||
MySQLDSN string
|
||
MaxOpen int
|
||
MaxIdle int
|
||
MetricsAddr string
|
||
WorkflowTickInterval time.Duration // WorkflowTickInterval Weave 持久化推进扫描间隔,范围100ms–1m。
|
||
ScheduleTickInterval time.Duration // ScheduleTickInterval 计划扫描间隔,范围100ms–1m。
|
||
RecoveryTickInterval time.Duration // RecoveryTickInterval 执行租约恢复扫描间隔,范围100ms–1m。
|
||
RuntimeBatchLimit int // RuntimeBatchLimit 每个后台批次的候选上限,范围1–100。
|
||
ShutdownTimeout time.Duration // ShutdownTimeout 优雅排空和关闭窗口,范围1s–5m。
|
||
WorkerConnectionLimit int // WorkerConnectionLimit 单节点 Worker WebSocket 连接上限,范围1–4096。
|
||
EventConnectionLimit int // EventConnectionLimit 单节点 Event WebSocket 连接上限,范围1–4096。
|
||
DispatchWorkers int // DispatchWorkers 单个 Hub 并发执行数据库领取调用的协程数,范围1–256。
|
||
DispatchCapacity int // DispatchCapacity 单个 Hub 领取调用的有界内存排队槽位,范围1–65536。
|
||
}
|
||
|
||
func Load() (Config, error) { return FromEnv(os.Getenv) }
|
||
func FromEnv(get func(string) string) (Config, error) {
|
||
c := Config{
|
||
ConsoleApplicationAdmin: get("ORBIT_CONSOLE_APPLICATION_ADMIN"),
|
||
ArchiveRoot: get("ORBIT_ARCHIVE_ROOT"), ArchiveStorageID: core.ID(get("ORBIT_ARCHIVE_STORAGE_ID")), ArchiveMaxBytes: 10 << 30, ArchiveTickInterval: 10 * time.Second,
|
||
HTTPAddr: get("ORBIT_HTTP_ADDR"),
|
||
MySQLDSN: get("ORBIT_MYSQL_DSN"),
|
||
MaxOpen: 20,
|
||
MaxIdle: 5,
|
||
MetricsAddr: get("ORBIT_METRICS_ADDR"),
|
||
WorkflowTickInterval: time.Second,
|
||
ScheduleTickInterval: time.Second,
|
||
RecoveryTickInterval: 2 * time.Second,
|
||
RuntimeBatchLimit: 100,
|
||
ShutdownTimeout: 10 * time.Second,
|
||
WorkerConnectionLimit: 128,
|
||
EventConnectionLimit: 128,
|
||
DispatchWorkers: 4,
|
||
DispatchCapacity: 128,
|
||
}
|
||
if c.HTTPAddr == "" {
|
||
c.HTTPAddr = "127.0.0.1:8080"
|
||
}
|
||
if _, _, err := net.SplitHostPort(c.HTTPAddr); err != nil {
|
||
return Config{}, errors.New("invalid ORBIT_HTTP_ADDR")
|
||
}
|
||
if c.MySQLDSN == "" {
|
||
return Config{}, errors.New("ORBIT_MYSQL_DSN is required")
|
||
}
|
||
for _, field := range []struct {
|
||
name string
|
||
dst *int
|
||
}{{"ORBIT_DB_MAX_OPEN", &c.MaxOpen}, {"ORBIT_DB_MAX_IDLE", &c.MaxIdle}} {
|
||
if raw := get(field.name); raw != "" {
|
||
n, err := strconv.Atoi(raw)
|
||
if err != nil || n < 0 {
|
||
return Config{}, fmt.Errorf("%s must be a nonnegative integer", field.name)
|
||
}
|
||
*field.dst = n
|
||
}
|
||
}
|
||
if c.MaxOpen <= 0 || c.MaxIdle > c.MaxOpen {
|
||
return Config{}, errors.New("database pool requires 0 <= max_idle <= max_open and max_open > 0")
|
||
}
|
||
for _, field := range []struct {
|
||
name string
|
||
dst *time.Duration
|
||
}{{"ORBIT_WORKFLOW_TICK_INTERVAL", &c.WorkflowTickInterval}, {"ORBIT_SCHEDULE_TICK_INTERVAL", &c.ScheduleTickInterval}, {"ORBIT_RECOVERY_TICK_INTERVAL", &c.RecoveryTickInterval}} {
|
||
if raw := get(field.name); raw != "" {
|
||
d, err := time.ParseDuration(raw)
|
||
if err != nil || d < 100*time.Millisecond || d > time.Minute {
|
||
return Config{}, fmt.Errorf("%s must be a duration from 100ms to 1m", field.name)
|
||
}
|
||
*field.dst = d
|
||
}
|
||
}
|
||
if raw := get("ORBIT_RUNTIME_BATCH_LIMIT"); raw != "" {
|
||
n, err := strconv.Atoi(raw)
|
||
if err != nil || n < 1 || n > 100 {
|
||
return Config{}, errors.New("ORBIT_RUNTIME_BATCH_LIMIT must be an integer from 1 to 100")
|
||
}
|
||
c.RuntimeBatchLimit = n
|
||
}
|
||
if raw := get("ORBIT_SHUTDOWN_TIMEOUT"); raw != "" {
|
||
d, err := time.ParseDuration(raw)
|
||
if err != nil || d < time.Second || d > 5*time.Minute {
|
||
return Config{}, errors.New("ORBIT_SHUTDOWN_TIMEOUT must be a duration from 1s to 5m")
|
||
}
|
||
c.ShutdownTimeout = d
|
||
}
|
||
for _, field := range []struct {
|
||
name string
|
||
dst *int
|
||
min int
|
||
max int
|
||
}{
|
||
{"ORBIT_WORKER_CONNECTION_LIMIT", &c.WorkerConnectionLimit, 1, 4096},
|
||
{"ORBIT_EVENT_CONNECTION_LIMIT", &c.EventConnectionLimit, 1, 4096},
|
||
{"ORBIT_DISPATCH_WORKERS", &c.DispatchWorkers, 1, 256},
|
||
{"ORBIT_DISPATCH_CAPACITY", &c.DispatchCapacity, 1, 65536},
|
||
} {
|
||
if raw := get(field.name); raw != "" {
|
||
n, err := strconv.Atoi(raw)
|
||
if err != nil || n < field.min || n > field.max {
|
||
return Config{}, fmt.Errorf("%s must be an integer from %d to %d", field.name, field.min, field.max)
|
||
}
|
||
*field.dst = n
|
||
}
|
||
}
|
||
if c.MetricsAddr != "" {
|
||
_, port, err := net.SplitHostPort(c.MetricsAddr)
|
||
n, parseErr := strconv.Atoi(port)
|
||
if err != nil || parseErr != nil || n < 0 || n > 65535 {
|
||
return Config{}, errors.New("invalid ORBIT_METRICS_ADDR")
|
||
}
|
||
}
|
||
|
||
if c.ArchiveRoot != "" || c.ArchiveStorageID != "" {
|
||
if !filepath.IsAbs(c.ArchiveRoot) || c.ArchiveStorageID.Validate() != nil {
|
||
return Config{}, errors.New("ORBIT_ARCHIVE_ROOT and ORBIT_ARCHIVE_STORAGE_ID must be configured together")
|
||
}
|
||
}
|
||
if raw := get("ORBIT_ARCHIVE_MAX_BYTES"); raw != "" {
|
||
n, err := strconv.ParseInt(raw, 10, 64)
|
||
if err != nil || n < 1<<20 || n > 1<<40 {
|
||
return Config{}, errors.New("ORBIT_ARCHIVE_MAX_BYTES must be 1MiB to 1TiB")
|
||
}
|
||
c.ArchiveMaxBytes = n
|
||
}
|
||
if raw := get("ORBIT_ARCHIVE_TICK_INTERVAL"); raw != "" {
|
||
d, err := time.ParseDuration(raw)
|
||
if err != nil || d < time.Second || d > time.Minute {
|
||
return Config{}, errors.New("ORBIT_ARCHIVE_TICK_INTERVAL must be 1s to 1m")
|
||
}
|
||
c.ArchiveTickInterval = d
|
||
}
|
||
var err error
|
||
c.RetentionDefaults, err = retention.FromEnv(get)
|
||
if err != nil {
|
||
return Config{}, err
|
||
}
|
||
return c, nil
|
||
}
|