feat: split openresty settings into performance page

This commit is contained in:
ryan
2026-03-12 23:51:57 +08:00
parent 97b67720bc
commit 238546b358
16 changed files with 1562 additions and 1031 deletions
+93 -42
View File
@@ -116,6 +116,31 @@ const (
nginxRouteConfigPlaceholder = "__ATSF_ROUTE_CONFIG__"
)
var requiredMainConfigTemplatePlaceholders = []string{
"{{OpenRestyWorkerProcesses}}",
"{{OpenRestyWorkerConnections}}",
"{{OpenRestyWorkerRlimitNofile}}",
"{{OpenRestyEventsUseDirective}}",
"{{OpenRestyEventsMultiAcceptDirective}}",
"{{OpenRestyKeepaliveTimeout}}",
"{{OpenRestyKeepaliveRequests}}",
"{{OpenRestyClientHeaderTimeout}}",
"{{OpenRestyClientBodyTimeout}}",
"{{OpenRestySendTimeout}}",
"{{OpenRestyProxyConnectTimeout}}",
"{{OpenRestyProxySendTimeout}}",
"{{OpenRestyProxyReadTimeout}}",
"{{OpenRestyProxyBuffering}}",
"{{OpenRestyProxyBuffers}}",
"{{OpenRestyProxyBufferSize}}",
"{{OpenRestyProxyBusyBuffersSize}}",
"{{OpenRestyGzip}}",
"{{OpenRestyGzipMinLength}}",
"{{OpenRestyGzipCompLevel}}",
"{{OpenRestyCacheBlock}}",
"{{OpenRestyRouteConfigInclude}}",
}
func ListConfigVersions() ([]*model.ConfigVersion, error) {
return model.ListConfigVersions()
}
@@ -569,51 +594,77 @@ func renderRouteConfig(routes []*model.ProxyRoute) (string, []SupportFile, error
}
func renderMainConfig(cfg openRestyConfigSnapshot) string {
var builder strings.Builder
builder.WriteString("# This file is generated by ATSFlare. Do not edit manually.\n")
builder.WriteString(fmt.Sprintf("worker_processes %s;\n", cfg.WorkerProcesses))
builder.WriteString(fmt.Sprintf("worker_rlimit_nofile %d;\n", cfg.WorkerRlimitNofile))
builder.WriteString("pid logs/nginx.pid;\n\n")
builder.WriteString("events {\n")
builder.WriteString(fmt.Sprintf(" worker_connections %d;\n", cfg.WorkerConnections))
if strings.TrimSpace(cfg.EventsUse) != "" {
builder.WriteString(fmt.Sprintf(" use %s;\n", cfg.EventsUse))
templateText := common.OpenRestyMainConfigTemplate
if strings.TrimSpace(templateText) == "" {
templateText = defaultOpenRestyMainConfigTemplate()
}
if cfg.EventsMultiAcceptEnabled {
builder.WriteString(" multi_accept on;\n")
return renderMainConfigTemplate(templateText, cfg)
}
func ValidateOpenRestyMainConfigTemplate(templateText string) error {
trimmed := strings.TrimSpace(templateText)
if trimmed == "" {
return errors.New("OpenRestyMainConfigTemplate 不能为空")
}
builder.WriteString("}\n\n")
builder.WriteString("http {\n")
builder.WriteString(" include mime.types;\n")
builder.WriteString(" default_type application/octet-stream;\n")
builder.WriteString(" sendfile on;\n")
builder.WriteString(" tcp_nopush on;\n")
builder.WriteString(" tcp_nodelay on;\n")
builder.WriteString(fmt.Sprintf(" keepalive_timeout %d;\n", cfg.KeepaliveTimeout))
builder.WriteString(fmt.Sprintf(" keepalive_requests %d;\n", cfg.KeepaliveRequests))
builder.WriteString(fmt.Sprintf(" client_header_timeout %d;\n", cfg.ClientHeaderTimeout))
builder.WriteString(fmt.Sprintf(" client_body_timeout %d;\n", cfg.ClientBodyTimeout))
builder.WriteString(fmt.Sprintf(" send_timeout %d;\n", cfg.SendTimeout))
builder.WriteString(fmt.Sprintf(" proxy_connect_timeout %d;\n", cfg.ProxyConnectTimeout))
builder.WriteString(fmt.Sprintf(" proxy_send_timeout %d;\n", cfg.ProxySendTimeout))
builder.WriteString(fmt.Sprintf(" proxy_read_timeout %d;\n", cfg.ProxyReadTimeout))
builder.WriteString(fmt.Sprintf(" proxy_buffering %s;\n", onOff(cfg.ProxyBufferingEnabled)))
builder.WriteString(fmt.Sprintf(" proxy_buffers %s;\n", cfg.ProxyBuffers))
builder.WriteString(fmt.Sprintf(" proxy_buffer_size %s;\n", cfg.ProxyBufferSize))
builder.WriteString(fmt.Sprintf(" proxy_busy_buffers_size %s;\n", cfg.ProxyBusyBuffersSize))
builder.WriteString(fmt.Sprintf(" gzip %s;\n", onOff(cfg.GzipEnabled)))
builder.WriteString(fmt.Sprintf(" gzip_min_length %d;\n", cfg.GzipMinLength))
builder.WriteString(fmt.Sprintf(" gzip_comp_level %d;\n", cfg.GzipCompLevel))
if cfg.CacheEnabled {
builder.WriteString(fmt.Sprintf(" proxy_cache_path %s levels=%s keys_zone=atsflare_cache:10m inactive=%s max_size=%s;\n", cfg.CachePath, cfg.CacheLevels, cfg.CacheInactive, cfg.CacheMaxSize))
builder.WriteString(fmt.Sprintf(" proxy_cache_key \"%s\";\n", cfg.CacheKeyTemplate))
builder.WriteString(fmt.Sprintf(" proxy_cache_lock %s;\n", onOff(cfg.CacheLockEnabled)))
builder.WriteString(fmt.Sprintf(" proxy_cache_lock_timeout %s;\n", cfg.CacheLockTimeout))
builder.WriteString(fmt.Sprintf(" proxy_cache_use_stale %s;\n", cfg.CacheUseStale))
for _, placeholder := range requiredMainConfigTemplatePlaceholders {
if !strings.Contains(trimmed, placeholder) {
return fmt.Errorf("OpenRestyMainConfigTemplate 必须保留占位符 %s", placeholder)
}
}
builder.WriteString(fmt.Sprintf(" include %s;\n", nginxRouteConfigPlaceholder))
builder.WriteString("}\n")
return builder.String()
return nil
}
func defaultOpenRestyMainConfigTemplate() string {
return common.OpenRestyMainConfigTemplate
}
func renderMainConfigTemplate(templateText string, cfg openRestyConfigSnapshot) string {
replacer := strings.NewReplacer(
"{{OpenRestyWorkerProcesses}}", cfg.WorkerProcesses,
"{{OpenRestyWorkerConnections}}", fmt.Sprintf("%d", cfg.WorkerConnections),
"{{OpenRestyWorkerRlimitNofile}}", fmt.Sprintf("%d", cfg.WorkerRlimitNofile),
"{{OpenRestyEventsUseDirective}}", renderTemplateDirective(cfg.EventsUse != "", fmt.Sprintf("use %s;", cfg.EventsUse)),
"{{OpenRestyEventsMultiAcceptDirective}}", renderTemplateDirective(cfg.EventsMultiAcceptEnabled, "multi_accept on;"),
"{{OpenRestyKeepaliveTimeout}}", fmt.Sprintf("%d", cfg.KeepaliveTimeout),
"{{OpenRestyKeepaliveRequests}}", fmt.Sprintf("%d", cfg.KeepaliveRequests),
"{{OpenRestyClientHeaderTimeout}}", fmt.Sprintf("%d", cfg.ClientHeaderTimeout),
"{{OpenRestyClientBodyTimeout}}", fmt.Sprintf("%d", cfg.ClientBodyTimeout),
"{{OpenRestySendTimeout}}", fmt.Sprintf("%d", cfg.SendTimeout),
"{{OpenRestyProxyConnectTimeout}}", fmt.Sprintf("%d", cfg.ProxyConnectTimeout),
"{{OpenRestyProxySendTimeout}}", fmt.Sprintf("%d", cfg.ProxySendTimeout),
"{{OpenRestyProxyReadTimeout}}", fmt.Sprintf("%d", cfg.ProxyReadTimeout),
"{{OpenRestyProxyBuffering}}", onOff(cfg.ProxyBufferingEnabled),
"{{OpenRestyProxyBuffers}}", cfg.ProxyBuffers,
"{{OpenRestyProxyBufferSize}}", cfg.ProxyBufferSize,
"{{OpenRestyProxyBusyBuffersSize}}", cfg.ProxyBusyBuffersSize,
"{{OpenRestyGzip}}", onOff(cfg.GzipEnabled),
"{{OpenRestyGzipMinLength}}", fmt.Sprintf("%d", cfg.GzipMinLength),
"{{OpenRestyGzipCompLevel}}", fmt.Sprintf("%d", cfg.GzipCompLevel),
"{{OpenRestyCacheBlock}}", renderOpenRestyCacheTemplateBlock(cfg),
"{{OpenRestyRouteConfigInclude}}", nginxRouteConfigPlaceholder,
)
return replacer.Replace(templateText)
}
func renderTemplateDirective(enabled bool, statement string) string {
if !enabled {
return ""
}
return fmt.Sprintf(" %s\n", statement)
}
func renderOpenRestyCacheTemplateBlock(cfg openRestyConfigSnapshot) string {
if !cfg.CacheEnabled {
return ""
}
return strings.Join([]string{
fmt.Sprintf(" proxy_cache_path %s levels=%s keys_zone=atsflare_cache:10m inactive=%s max_size=%s;", cfg.CachePath, cfg.CacheLevels, cfg.CacheInactive, cfg.CacheMaxSize),
fmt.Sprintf(" proxy_cache_key \"%s\";", cfg.CacheKeyTemplate),
fmt.Sprintf(" proxy_cache_lock %s;", onOff(cfg.CacheLockEnabled)),
fmt.Sprintf(" proxy_cache_lock_timeout %s;", cfg.CacheLockTimeout),
fmt.Sprintf(" proxy_cache_use_stale %s;", cfg.CacheUseStale),
"",
}, "\n")
}
func onOff(value bool) string {
+39
View File
@@ -251,6 +251,45 @@ func TestCreateTLSCertificateRejectsInvalidPEM(t *testing.T) {
}
}
func TestOpenRestyMainConfigTemplateRenderAndValidate(t *testing.T) {
setupServiceTestDB(t)
customTemplate := strings.ReplaceAll(
common.OpenRestyMainConfigTemplate,
"pid logs/nginx.pid;",
"pid logs/nginx.pid;\nworker_shutdown_timeout 10s;",
)
if err := ValidateOpenRestyMainConfigTemplate(customTemplate); err != nil {
t.Fatalf("ValidateOpenRestyMainConfigTemplate failed: %v", err)
}
if err := model.UpdateOption("OpenRestyMainConfigTemplate", customTemplate); err != nil {
t.Fatalf("UpdateOption OpenRestyMainConfigTemplate failed: %v", err)
}
preview, err := PreviewConfigVersion()
if err != nil {
t.Fatalf("PreviewConfigVersion failed: %v", err)
}
if !strings.Contains(preview.MainConfig, "worker_shutdown_timeout 10s;") {
t.Fatal("expected preview main config to include custom template content")
}
if strings.Contains(preview.MainConfig, "{{OpenRestyWorkerProcesses}}") {
t.Fatal("expected preview main config placeholders to be rendered")
}
if !strings.Contains(preview.MainConfig, "include __ATSF_ROUTE_CONFIG__;") {
t.Fatal("expected preview main config to preserve managed route include")
}
invalidTemplate := strings.ReplaceAll(
common.OpenRestyMainConfigTemplate,
"{{OpenRestyRouteConfigInclude}}",
"",
)
if err := ValidateOpenRestyMainConfigTemplate(invalidTemplate); err == nil {
t.Fatal("expected template without managed route placeholder to fail validation")
}
}
func setupServiceTestDB(t *testing.T) {
t.Helper()
common.SQLitePath = filepath.Join(t.TempDir(), "service.db")