feat: add node SSR dual-slot deployment config
This commit is contained in:
@@ -34,6 +34,10 @@ const (
|
||||
BackendPort8080 = 8080
|
||||
// BackendPort8081 蓝绿发布中 8081 槽位的端口号。
|
||||
BackendPort8081 = 8081
|
||||
// NodeSsrPort18910 Node SSR 的第一个宿主槽位端口。
|
||||
NodeSsrPort18910 = 18910
|
||||
// NodeSsrPort28910 Node SSR 的第二个宿主槽位端口。
|
||||
NodeSsrPort28910 = 28910
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,6 +61,14 @@ const (
|
||||
containerName8080 = "backend-8080"
|
||||
// containerName8081 容器后端 8081 槽位的容器名称。
|
||||
containerName8081 = "backend-8081"
|
||||
// nodeSsrContainerName18910 Node SSR 18910 槽位的容器名称。
|
||||
nodeSsrContainerName18910 = "node-ssr-18910"
|
||||
// nodeSsrContainerName28910 Node SSR 28910 槽位的容器名称。
|
||||
nodeSsrContainerName28910 = "node-ssr-28910"
|
||||
// nodeSsrHealthURL18910 Node SSR 18910 槽位的健康检查地址。
|
||||
nodeSsrHealthURL18910 = "http://127.0.0.1:18910/health"
|
||||
// nodeSsrHealthURL28910 Node SSR 28910 槽位的健康检查地址。
|
||||
nodeSsrHealthURL28910 = "http://127.0.0.1:28910/health"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -83,6 +95,8 @@ type Config struct {
|
||||
Daemon Daemon `toml:"daemon"`
|
||||
// Backend 记录显式选定的后端运行时及其蓝绿槽位。
|
||||
Backend Backend `toml:"backend"`
|
||||
// NodeSsr 记录 Node SSR 容器运行时及其蓝绿槽位。
|
||||
NodeSsr NodeSsr `toml:"nodeSsr"`
|
||||
}
|
||||
|
||||
// Daemon 描述与具体组件运行时无关的机器级行为。
|
||||
@@ -129,6 +143,28 @@ type BackendSlot struct {
|
||||
HealthEndpoint string `toml:"health_endpoint"`
|
||||
}
|
||||
|
||||
// NodeSsr 描述 Node SSR 容器运行时及其蓝绿槽位。
|
||||
type NodeSsr struct {
|
||||
// Type 当前只接受 container。
|
||||
Type string `toml:"type"`
|
||||
// StopGraceSeconds 停止旧容器时的优雅退出等待秒数,可省略。
|
||||
StopGraceSeconds *int `toml:"stop_grace_seconds"`
|
||||
// Slot 包含 18910、28910 两个宿主端口槽位。
|
||||
Slot NodeSsrSlots `toml:"slot"`
|
||||
}
|
||||
|
||||
// NodeSsrSlots 列出 Node SSR 的两个精确宿主端口槽位。
|
||||
type NodeSsrSlots struct {
|
||||
Port18910 NodeSsrSlot `toml:"18910"`
|
||||
Port28910 NodeSsrSlot `toml:"28910"`
|
||||
}
|
||||
|
||||
// NodeSsrSlot 包含一个 Node SSR 容器槽位的容器名与健康检查地址。
|
||||
type NodeSsrSlot struct {
|
||||
ContainerName string `toml:"container_name"`
|
||||
HealthEndpoint string `toml:"health_endpoint"`
|
||||
}
|
||||
|
||||
// Load 打开 path 指向的部署配置文件,进行严格的 TOML 解码与契约校验,返回解析后的 Config。
|
||||
// 它要求 path 是绝对路径且为普通文件,解码时禁用未知字段(任何大小写、拼写或层级不一致的
|
||||
// 键都会被拒绝),解码后调用 Validate 精确校验取值。任一步失败都会返回带上下文的 error。
|
||||
@@ -176,7 +212,7 @@ func validateExactDocumentKeys(document []byte) error {
|
||||
if err := toml.Unmarshal(document, &root); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := rejectUnknownKeys(root, "", "daemon", "backend"); err != nil {
|
||||
if err := rejectUnknownKeys(root, "", "daemon", "backend", "nodeSsr"); err != nil {
|
||||
return err
|
||||
}
|
||||
daemon, err := exactTable(root, "", "daemon")
|
||||
@@ -209,6 +245,29 @@ func validateExactDocumentKeys(document []byte) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
nodeSsr, err := exactTable(root, "", "nodeSsr")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := rejectUnknownKeys(nodeSsr, "nodeSsr", "type", "stop_grace_seconds", "slot"); err != nil {
|
||||
return err
|
||||
}
|
||||
nodeSlots, err := exactTable(nodeSsr, "nodeSsr", "slot")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := rejectUnknownKeys(nodeSlots, "nodeSsr.slot", "18910", "28910"); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, port := range []string{"18910", "28910"} {
|
||||
slot, err := exactTable(nodeSlots, "nodeSsr.slot", port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := rejectUnknownKeys(slot, "nodeSsr.slot."+port, "container_name", "health_endpoint"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -303,9 +362,33 @@ func (c Config) Validate() error {
|
||||
default:
|
||||
return fmt.Errorf("backend.type must be %q or %q", BackendTypeNative, BackendTypeContainer)
|
||||
}
|
||||
if c.NodeSsr.Type != BackendTypeContainer {
|
||||
return fmt.Errorf("nodeSsr.type must be %q", BackendTypeContainer)
|
||||
}
|
||||
if c.NodeSsr.StopGraceSeconds != nil && *c.NodeSsr.StopGraceSeconds <= 0 {
|
||||
return errors.New("nodeSsr.stop_grace_seconds must be a positive integer")
|
||||
}
|
||||
if err := validateNodeSsrSlot("nodeSsr.slot.18910", c.NodeSsr.Slot.Port18910, nodeSsrContainerName18910, nodeSsrHealthURL18910); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateNodeSsrSlot("nodeSsr.slot.28910", c.NodeSsr.Slot.Port28910, nodeSsrContainerName28910, nodeSsrHealthURL28910); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SlotForNodeSsrPort 返回 Node SSR 宿主端口对应的槽位配置。
|
||||
func (n NodeSsr) SlotForNodeSsrPort(port int) (NodeSsrSlot, error) {
|
||||
switch port {
|
||||
case NodeSsrPort18910:
|
||||
return n.Slot.Port18910, nil
|
||||
case NodeSsrPort28910:
|
||||
return n.Slot.Port28910, nil
|
||||
default:
|
||||
return NodeSsrSlot{}, fmt.Errorf("unsupported Node SSR port: %d", port)
|
||||
}
|
||||
}
|
||||
|
||||
// SlotForPort 返回 port 对应的后端槽位配置,仅支持 BackendPort8080 或 BackendPort8081,
|
||||
// 其他端口返回带端口号的错误。
|
||||
func (b Backend) SlotForPort(port int) (BackendSlot, error) {
|
||||
@@ -346,6 +429,17 @@ func validateContainerSlot(field string, slot BackendSlot, containerName string,
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateNodeSsrSlot 校验 Node SSR 槽位的容器名称和健康检查地址。
|
||||
func validateNodeSsrSlot(field string, slot NodeSsrSlot, containerName string, endpoint string) error {
|
||||
if slot.ContainerName != containerName {
|
||||
return fmt.Errorf("%s.container_name must be %q", field, containerName)
|
||||
}
|
||||
if slot.HealthEndpoint != endpoint {
|
||||
return fmt.Errorf("%s.health_endpoint must be %q", field, endpoint)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateAbsolutePath 校验 value 是一个非空、无首尾空白、经过 Clean 且不含 NUL 字节的绝对路径,
|
||||
// 用于保证配置中的路径标识符不会被意外篡改。field 用于拼出错误信息。
|
||||
func validateAbsolutePath(field string, value string) error {
|
||||
|
||||
@@ -26,6 +26,17 @@ health_endpoint = "http://127.0.0.1:8080/yms/actuator/health"
|
||||
unit = "yms-backend@8081.service"
|
||||
jar = "/home/yms/lib/glory-soft-yms-8081.jar"
|
||||
health_endpoint = "http://127.0.0.1:8081/yms/actuator/health"
|
||||
|
||||
[nodeSsr]
|
||||
type = "container"
|
||||
|
||||
[nodeSsr.slot.18910]
|
||||
container_name = "node-ssr-18910"
|
||||
health_endpoint = "http://127.0.0.1:18910/health"
|
||||
|
||||
[nodeSsr.slot.28910]
|
||||
container_name = "node-ssr-28910"
|
||||
health_endpoint = "http://127.0.0.1:28910/health"
|
||||
`
|
||||
|
||||
// validContainerConfig 一份满足容器后端部署契约的示例 TOML 配置,供各测试用例共享。
|
||||
@@ -43,6 +54,17 @@ health_endpoint = "http://127.0.0.1:8080/yms/actuator/health"
|
||||
[backend.slot.8081]
|
||||
container_name = "backend-8081"
|
||||
health_endpoint = "http://127.0.0.1:8081/yms/actuator/health"
|
||||
|
||||
[nodeSsr]
|
||||
type = "container"
|
||||
|
||||
[nodeSsr.slot.18910]
|
||||
container_name = "node-ssr-18910"
|
||||
health_endpoint = "http://127.0.0.1:18910/health"
|
||||
|
||||
[nodeSsr.slot.28910]
|
||||
container_name = "node-ssr-28910"
|
||||
health_endpoint = "http://127.0.0.1:28910/health"
|
||||
`
|
||||
|
||||
// TestLoadValidNativeConfiguration 验证合法原生配置能被完整加载,
|
||||
|
||||
@@ -48,6 +48,21 @@ unit = "yms-backend@8081.service"
|
||||
jar = "/home/yms/lib/glory-soft-yms-8081.jar"
|
||||
health_endpoint = "http://127.0.0.1:8081/yms/actuator/health"
|
||||
|
||||
# =============================================================================
|
||||
# Node SSR:容器双槽,宿主 18910/28910,Nginx 逻辑入口 8910
|
||||
# =============================================================================
|
||||
[nodeSsr]
|
||||
type = "container"
|
||||
# stop_grace_seconds = 30
|
||||
|
||||
[nodeSsr.slot.18910]
|
||||
container_name = "node-ssr-18910"
|
||||
health_endpoint = "http://127.0.0.1:18910/health"
|
||||
|
||||
[nodeSsr.slot.28910]
|
||||
container_name = "node-ssr-28910"
|
||||
health_endpoint = "http://127.0.0.1:28910/health"
|
||||
|
||||
# =============================================================================
|
||||
# 方式二:容器后端(切换到容器时,取消注释本块,并注释掉上面的方式一)
|
||||
# =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user