refactor: isolate config sources (code/file/dynamic), add file-based routing config, fix Start logger trace ID (by AI)
This commit is contained in:
parent
3925767d2e
commit
2b7e11e7d2
81
config.go
81
config.go
@ -1,5 +1,11 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CertSet SSL 证书配置
|
||||
type CertSet struct {
|
||||
CertFile string
|
||||
@ -60,6 +66,81 @@ type ServiceConfig struct {
|
||||
MaxUploadBufferPerConnection int32 // 每个连接的最大上传缓冲区大小
|
||||
MaxUploadBufferPerStream int32 // 每个流的最大上传缓冲区大小
|
||||
StopTimeout int // 停止服务的超时时间 (ms)
|
||||
|
||||
// 从配置文件中加载的静态路由策略 (按 Host 分组,全局配置用 "" 或 "*")
|
||||
Proxies map[string][]ProxyRule
|
||||
Rewrites map[string][]RewriteRule
|
||||
Statics map[string]map[string]string
|
||||
}
|
||||
|
||||
var Config = ServiceConfig{}
|
||||
|
||||
// ApplyConfig 将 ServiceConfig 中的路由策略应用到内部的文件级策略中
|
||||
func ApplyConfig() {
|
||||
hostPoliciesLock.Lock()
|
||||
defer hostPoliciesLock.Unlock()
|
||||
|
||||
// 清理旧的 file 策略
|
||||
fileProxies = make(map[string][]*proxyType)
|
||||
fileRewrites = make(map[string][]*rewriteType)
|
||||
|
||||
for host, rules := range Config.Proxies {
|
||||
if host == "*" {
|
||||
host = ""
|
||||
}
|
||||
newProxies := make([]*proxyType, 0, len(rules))
|
||||
for _, r := range rules {
|
||||
p := &proxyType{authLevel: r.AuthLevel, fromPath: r.Path, toApp: r.ToApp, toPath: r.ToPath}
|
||||
if strings.ContainsRune(r.Path, '(') {
|
||||
matcher, err := regexp.Compile("^" + r.Path + "$")
|
||||
if err == nil {
|
||||
p.matcher = matcher
|
||||
}
|
||||
}
|
||||
newProxies = append(newProxies, p)
|
||||
}
|
||||
fileProxies[host] = newProxies
|
||||
rebuildProxiesUnderLock(host)
|
||||
}
|
||||
|
||||
for host, rules := range Config.Rewrites {
|
||||
if host == "*" {
|
||||
host = ""
|
||||
}
|
||||
newRewrites := make([]*rewriteType, 0, len(rules))
|
||||
for _, r := range rules {
|
||||
s := &rewriteType{fromPath: r.Path, toPath: r.ToPath}
|
||||
if strings.ContainsRune(r.Path, '(') {
|
||||
matcher, err := regexp.Compile("^" + r.Path + "$")
|
||||
if err == nil {
|
||||
s.matcher = matcher
|
||||
}
|
||||
}
|
||||
newRewrites = append(newRewrites, s)
|
||||
}
|
||||
fileRewrites[host] = newRewrites
|
||||
rebuildRewritesUnderLock(host)
|
||||
}
|
||||
|
||||
staticsByHostLock.Lock()
|
||||
defer staticsByHostLock.Unlock()
|
||||
fileStatics = make(map[string]map[string]*string)
|
||||
|
||||
for host, config := range Config.Statics {
|
||||
if host == "*" {
|
||||
host = ""
|
||||
}
|
||||
newStatics := make(map[string]*string, len(config))
|
||||
for path, rootPath := range config {
|
||||
rp := rootPath
|
||||
if !filepath.IsAbs(rp) {
|
||||
if absPath, err := filepath.Abs(rp); err == nil {
|
||||
rp = absPath
|
||||
}
|
||||
}
|
||||
newStatics[path] = &rp
|
||||
}
|
||||
fileStatics[host] = newStatics
|
||||
rebuildStaticsUnderLock(host)
|
||||
}
|
||||
}
|
||||
|
||||
17
proxy.go
17
proxy.go
@ -30,10 +30,19 @@ func (hc *HostContext) Proxy(authLevel int, path string, toApp, toPath string) *
|
||||
|
||||
hostPoliciesLock.Lock()
|
||||
defer hostPoliciesLock.Unlock()
|
||||
hostProxies[hc.host] = append(hostProxies[hc.host], p)
|
||||
codeProxies[hc.host] = append(codeProxies[hc.host], p)
|
||||
rebuildProxiesUnderLock(hc.host)
|
||||
return hc
|
||||
}
|
||||
|
||||
func rebuildProxiesUnderLock(host string) {
|
||||
var combined []*proxyType
|
||||
combined = append(combined, codeProxies[host]...)
|
||||
combined = append(combined, fileProxies[host]...)
|
||||
combined = append(combined, dynamicProxies[host]...)
|
||||
hostProxies[host] = combined
|
||||
}
|
||||
|
||||
var httpClientPool *gohttp.Client
|
||||
|
||||
func findProxy(request *Request) (int, *string, *string, string) {
|
||||
@ -171,8 +180,7 @@ type ProxyRule struct {
|
||||
ToPath string // 目标路径 (可含 $1 变量替换)
|
||||
}
|
||||
|
||||
// ReplaceProxies 使用全量指针替换的方式 (Copy-on-Write) 无缝更新指定 host 的所有代理规则。
|
||||
// 该方法非常轻量,仅在赋值瞬间短暂持有写锁,不会阻塞任何并发请求,并且自动淘汰旧规则。
|
||||
// ReplaceProxies 使用全量指针替换的方式 (Copy-on-Write) 无缝更新指定 host 的动态代理规则,不影响通过代码或文件写入的固定规则。
|
||||
func ReplaceProxies(host string, rules []ProxyRule) {
|
||||
newProxies := make([]*proxyType, 0, len(rules))
|
||||
for _, r := range rules {
|
||||
@ -188,5 +196,6 @@ func ReplaceProxies(host string, rules []ProxyRule) {
|
||||
|
||||
hostPoliciesLock.Lock()
|
||||
defer hostPoliciesLock.Unlock()
|
||||
hostProxies[host] = newProxies
|
||||
dynamicProxies[host] = newProxies
|
||||
rebuildProxiesUnderLock(host)
|
||||
}
|
||||
|
||||
16
rewrite.go
16
rewrite.go
@ -26,10 +26,19 @@ func (hc *HostContext) Rewrite(path string, toPath string) *HostContext {
|
||||
|
||||
hostPoliciesLock.Lock()
|
||||
defer hostPoliciesLock.Unlock()
|
||||
hostRewrites[hc.host] = append(hostRewrites[hc.host], s)
|
||||
codeRewrites[hc.host] = append(codeRewrites[hc.host], s)
|
||||
rebuildRewritesUnderLock(hc.host)
|
||||
return hc
|
||||
}
|
||||
|
||||
func rebuildRewritesUnderLock(host string) {
|
||||
var combined []*rewriteType
|
||||
combined = append(combined, codeRewrites[host]...)
|
||||
combined = append(combined, fileRewrites[host]...)
|
||||
combined = append(combined, dynamicRewrites[host]...)
|
||||
hostRewrites[host] = combined
|
||||
}
|
||||
|
||||
func processRewrite(request *Request, response *Response, logger *log.Logger) bool {
|
||||
host := request.Host
|
||||
hostOnly, port, _ := strings.Cut(host, ":")
|
||||
@ -108,7 +117,7 @@ type RewriteRule struct {
|
||||
ToPath string // 重写后的路径,例如 /new/$1
|
||||
}
|
||||
|
||||
// ReplaceRewrites 使用 Copy-on-Write 机制原子地替换指定 host 下的所有重写规则。
|
||||
// ReplaceRewrites 使用 Copy-on-Write 机制原子地替换指定 host 下的动态重写规则。
|
||||
func ReplaceRewrites(host string, rules []RewriteRule) {
|
||||
newRewrites := make([]*rewriteType, 0, len(rules))
|
||||
for _, r := range rules {
|
||||
@ -124,5 +133,6 @@ func ReplaceRewrites(host string, rules []RewriteRule) {
|
||||
|
||||
hostPoliciesLock.Lock()
|
||||
defer hostPoliciesLock.Unlock()
|
||||
hostRewrites[host] = newRewrites
|
||||
dynamicRewrites[host] = newRewrites
|
||||
rebuildRewritesUnderLock(host)
|
||||
}
|
||||
|
||||
33
server.go
33
server.go
@ -1,6 +1,7 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"apigo.cc/go/config"
|
||||
"apigo.cc/go/discover"
|
||||
"apigo.cc/go/log"
|
||||
"apigo.cc/go/redis"
|
||||
@ -27,6 +28,7 @@ type WebServer struct {
|
||||
Addr string
|
||||
useDiscover bool
|
||||
discoverer *discover.Discoverer
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
// NewWebServer 创建并返回一个新的 WebServer 实例
|
||||
@ -36,6 +38,11 @@ func NewWebServer() *WebServer {
|
||||
|
||||
// Start 启动服务,实现 starter.Service 接口
|
||||
func (ws *WebServer) Start(ctx context.Context, logger *log.Logger) error {
|
||||
if logger == nil {
|
||||
logger = log.DefaultLogger
|
||||
}
|
||||
ws.logger = logger
|
||||
|
||||
listenStr := Config.Listen
|
||||
ws.useDiscover = false
|
||||
|
||||
@ -182,7 +189,11 @@ func (ws *WebServer) Start(ctx context.Context, logger *log.Logger) error {
|
||||
|
||||
// Stop 停止服务,实现 starter.Service 接口
|
||||
func (ws *WebServer) Stop(ctx context.Context) error {
|
||||
log.DefaultLogger.Info("service stopping")
|
||||
logger := ws.logger
|
||||
if logger == nil {
|
||||
logger = log.DefaultLogger
|
||||
}
|
||||
logger.Info("service stopping")
|
||||
if ws.discoverer != nil {
|
||||
ws.discoverer.Stop()
|
||||
}
|
||||
@ -191,7 +202,7 @@ func (ws *WebServer) Stop(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
log.DefaultLogger.Info("service stopped")
|
||||
logger.Info("service stopped")
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -205,7 +216,23 @@ func (ws *WebServer) Health() error {
|
||||
|
||||
// Reload 实现配置重新加载,实现 starter.Reloader 接口
|
||||
func (ws *WebServer) Reload() error {
|
||||
log.DefaultLogger.Info("reloading configurations...")
|
||||
logger := ws.logger
|
||||
if logger == nil {
|
||||
logger = log.DefaultLogger
|
||||
}
|
||||
logger.Info("reloading configurations...")
|
||||
|
||||
// 重新加载配置文件中的策略
|
||||
appName := Config.App
|
||||
if appName == "" {
|
||||
appName = GetDefaultName()
|
||||
}
|
||||
if err := config.Load(&Config, appName); err != nil {
|
||||
logger.Error("failed to load config during reload", "error", err.Error())
|
||||
}
|
||||
ApplyConfig()
|
||||
|
||||
// 触发业务挂载的 Hook
|
||||
return triggerReload()
|
||||
}
|
||||
|
||||
|
||||
12
service.go
12
service.go
@ -72,9 +72,19 @@ var (
|
||||
websocketServicesLock = sync.RWMutex{}
|
||||
websocketServicesList = make([]*websocketServiceType, 0)
|
||||
|
||||
// Rewrite 与 Proxy 按 Host 隔离
|
||||
// Rewrite 与 Proxy 按 Host 隔离 (编译后的最终路由)
|
||||
hostRewrites = make(map[string][]*rewriteType)
|
||||
hostProxies = make(map[string][]*proxyType)
|
||||
|
||||
// 按来源隔离的策略,避免互相覆盖
|
||||
codeProxies = make(map[string][]*proxyType)
|
||||
fileProxies = make(map[string][]*proxyType)
|
||||
dynamicProxies = make(map[string][]*proxyType)
|
||||
|
||||
codeRewrites = make(map[string][]*rewriteType)
|
||||
fileRewrites = make(map[string][]*rewriteType)
|
||||
dynamicRewrites = make(map[string][]*rewriteType)
|
||||
|
||||
hostPoliciesLock = sync.RWMutex{}
|
||||
|
||||
// 过滤器与拦截器
|
||||
|
||||
40
static.go
40
static.go
@ -14,6 +14,11 @@ import (
|
||||
var (
|
||||
statics = make(map[string]*string)
|
||||
staticsByHost = make(map[string]map[string]*string)
|
||||
|
||||
codeStatics = make(map[string]map[string]*string)
|
||||
fileStatics = make(map[string]map[string]*string)
|
||||
dynamicStatics = make(map[string]map[string]*string)
|
||||
|
||||
staticsByHostLock = sync.RWMutex{}
|
||||
)
|
||||
|
||||
@ -33,17 +38,14 @@ func StaticByHost(path, rootPath, host string) {
|
||||
staticsByHostLock.Lock()
|
||||
defer staticsByHostLock.Unlock()
|
||||
|
||||
if host == "" {
|
||||
statics[path] = &rootPath
|
||||
} else {
|
||||
if staticsByHost[host] == nil {
|
||||
staticsByHost[host] = make(map[string]*string)
|
||||
}
|
||||
staticsByHost[host][path] = &rootPath
|
||||
if codeStatics[host] == nil {
|
||||
codeStatics[host] = make(map[string]*string)
|
||||
}
|
||||
codeStatics[host][path] = &rootPath
|
||||
rebuildStaticsUnderLock(host)
|
||||
}
|
||||
|
||||
// ReplaceStatics 使用 Copy-on-Write 机制原子地替换指定 host 下的所有静态目录规则
|
||||
// ReplaceStatics 使用 Copy-on-Write 机制原子地替换指定 host 下的动态静态目录规则
|
||||
func ReplaceStatics(host string, config map[string]string) {
|
||||
newStatics := make(map[string]*string, len(config))
|
||||
for path, rootPath := range config {
|
||||
@ -59,10 +61,28 @@ func ReplaceStatics(host string, config map[string]string) {
|
||||
staticsByHostLock.Lock()
|
||||
defer staticsByHostLock.Unlock()
|
||||
|
||||
dynamicStatics[host] = newStatics
|
||||
rebuildStaticsUnderLock(host)
|
||||
}
|
||||
|
||||
func rebuildStaticsUnderLock(host string) {
|
||||
combined := make(map[string]*string)
|
||||
|
||||
// 合并三种来源的静态路由
|
||||
for k, v := range codeStatics[host] {
|
||||
combined[k] = v
|
||||
}
|
||||
for k, v := range fileStatics[host] {
|
||||
combined[k] = v
|
||||
}
|
||||
for k, v := range dynamicStatics[host] {
|
||||
combined[k] = v
|
||||
}
|
||||
|
||||
if host == "" {
|
||||
statics = newStatics
|
||||
statics = combined
|
||||
} else {
|
||||
staticsByHost[host] = newStatics
|
||||
staticsByHost[host] = combined
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user