优化结构
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package conf
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// Runtime owns the active immutable configuration snapshot. Callers always
|
||||
// receive clones so a config file reload cannot race with request handling.
|
||||
type Runtime struct {
|
||||
snapshot atomic.Pointer[runtimeSnapshot]
|
||||
mu sync.RWMutex
|
||||
nextID uint64
|
||||
listeners map[uint64]func(*Data, *AdminBackend)
|
||||
}
|
||||
|
||||
type runtimeSnapshot struct {
|
||||
data *Data
|
||||
admin *AdminBackend
|
||||
}
|
||||
|
||||
func NewRuntime(data *Data, admin *AdminBackend) *Runtime {
|
||||
runtime := &Runtime{listeners: make(map[uint64]func(*Data, *AdminBackend))}
|
||||
runtime.Replace(data, admin)
|
||||
return runtime
|
||||
}
|
||||
|
||||
func cloneData(value *Data) *Data {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return proto.Clone(value).(*Data)
|
||||
}
|
||||
|
||||
func cloneAdmin(value *AdminBackend) *AdminBackend {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
return proto.Clone(value).(*AdminBackend)
|
||||
}
|
||||
|
||||
func (r *Runtime) Replace(data *Data, admin *AdminBackend) {
|
||||
data = cloneData(data)
|
||||
admin = cloneAdmin(admin)
|
||||
r.snapshot.Store(&runtimeSnapshot{data: data, admin: admin})
|
||||
r.mu.RLock()
|
||||
listeners := make([]func(*Data, *AdminBackend), 0, len(r.listeners))
|
||||
for _, listener := range r.listeners {
|
||||
listeners = append(listeners, listener)
|
||||
}
|
||||
r.mu.RUnlock()
|
||||
for _, listener := range listeners {
|
||||
listener(cloneData(data), cloneAdmin(admin))
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe registers a callback invoked after every successful runtime
|
||||
// configuration replacement. It is used by long-lived clients such as the
|
||||
// logger and other long-lived clients that must follow runtime config changes.
|
||||
func (r *Runtime) Subscribe(listener func(*Data, *AdminBackend)) func() {
|
||||
if listener == nil {
|
||||
return func() {}
|
||||
}
|
||||
r.mu.Lock()
|
||||
r.nextID++
|
||||
id := r.nextID
|
||||
r.listeners[id] = listener
|
||||
r.mu.Unlock()
|
||||
return func() {
|
||||
r.mu.Lock()
|
||||
delete(r.listeners, id)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Runtime) Data() *Data {
|
||||
snapshot := r.snapshot.Load()
|
||||
if snapshot == nil {
|
||||
return nil
|
||||
}
|
||||
return cloneData(snapshot.data)
|
||||
}
|
||||
|
||||
func (r *Runtime) Admin() *AdminBackend {
|
||||
snapshot := r.snapshot.Load()
|
||||
if snapshot == nil {
|
||||
return nil
|
||||
}
|
||||
return cloneAdmin(snapshot.admin)
|
||||
}
|
||||
|
||||
func (r *Runtime) Values() (*Data, *AdminBackend) {
|
||||
snapshot := r.snapshot.Load()
|
||||
if snapshot == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return cloneData(snapshot.data), cloneAdmin(snapshot.admin)
|
||||
}
|
||||
|
||||
func (r *Runtime) ConfigPath() string {
|
||||
snapshot := r.snapshot.Load()
|
||||
if snapshot == nil || snapshot.admin == nil {
|
||||
return ""
|
||||
}
|
||||
return snapshot.admin.ConfigPath
|
||||
}
|
||||
|
||||
func (r *Runtime) UpdateDatabase(database *Data_Database) {
|
||||
for {
|
||||
current := r.snapshot.Load()
|
||||
var data *Data
|
||||
var admin *AdminBackend
|
||||
if current != nil {
|
||||
data = cloneData(current.data)
|
||||
admin = cloneAdmin(current.admin)
|
||||
}
|
||||
if data == nil {
|
||||
data = &Data{}
|
||||
}
|
||||
data.Database = proto.Clone(database).(*Data_Database)
|
||||
next := &runtimeSnapshot{data: data, admin: admin}
|
||||
if r.snapshot.CompareAndSwap(current, next) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user