fixed loadbalancer with idlewatcher, fixed reload issue

This commit is contained in:
yusing
2024-10-20 09:46:02 +08:00
parent 01ffe0d97c
commit a278711421
78 changed files with 906 additions and 609 deletions

View File

@@ -21,7 +21,7 @@ type DirWatcher struct {
mu sync.Mutex
eventCh chan Event
errCh chan E.NestedError
errCh chan E.Error
ctx context.Context
}
@@ -48,14 +48,14 @@ func NewDirectoryWatcher(ctx context.Context, dirPath string) *DirWatcher {
w: w,
fwMap: F.NewMapOf[string, *fileWatcher](),
eventCh: make(chan Event),
errCh: make(chan E.NestedError),
errCh: make(chan E.Error),
ctx: ctx,
}
go helper.start()
return helper
}
func (h *DirWatcher) Events(_ context.Context) (<-chan Event, <-chan E.NestedError) {
func (h *DirWatcher) Events(_ context.Context) (<-chan Event, <-chan E.Error) {
return h.eventCh, h.errCh
}
@@ -71,7 +71,7 @@ func (h *DirWatcher) Add(relPath string) Watcher {
s = &fileWatcher{
relPath: relPath,
eventCh: make(chan Event),
errCh: make(chan E.NestedError),
errCh: make(chan E.Error),
}
go func() {
defer func() {

View File

@@ -36,6 +36,14 @@ var (
NewDockerFilter = filters.NewArgs
optionsDefault = DockerListOptions{Filters: NewDockerFilter(
DockerFilterContainer,
DockerFilterStart,
// DockerFilterStop,
DockerFilterDie,
DockerFilterDestroy,
)}
dockerWatcherRetryInterval = 3 * time.Second
)
@@ -61,13 +69,13 @@ func NewDockerWatcherWithClient(client D.Client) DockerWatcher {
WithField("host", client.DaemonHost()))}
}
func (w DockerWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.NestedError) {
return w.EventsWithOptions(ctx, optionsWatchAll)
func (w DockerWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.Error) {
return w.EventsWithOptions(ctx, optionsDefault)
}
func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerListOptions) (<-chan Event, <-chan E.NestedError) {
func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerListOptions) (<-chan Event, <-chan E.Error) {
eventCh := make(chan Event)
errCh := make(chan E.NestedError)
errCh := make(chan E.Error)
go func() {
defer close(eventCh)
@@ -80,7 +88,7 @@ func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerList
}()
if !w.client.Connected() {
var err E.NestedError
var err E.Error
attempts := 0
for {
w.client, err = D.ConnectClient(w.host)
@@ -141,11 +149,3 @@ func (w DockerWatcher) EventsWithOptions(ctx context.Context, options DockerList
return eventCh, errCh
}
var optionsWatchAll = DockerListOptions{Filters: NewDockerFilter(
DockerFilterContainer,
DockerFilterStart,
// DockerFilterStop,
DockerFilterDie,
DockerFilterDestroy,
)}

View File

@@ -3,20 +3,22 @@ package events
import (
"time"
"github.com/yusing/go-proxy/internal/common"
E "github.com/yusing/go-proxy/internal/error"
"github.com/yusing/go-proxy/internal/task"
)
type (
EventQueue struct {
task task.Task
queue []Event
ticker *time.Ticker
onFlush OnFlushFunc
onError OnErrorFunc
task task.Task
queue []Event
ticker *time.Ticker
flushInterval time.Duration
onFlush OnFlushFunc
onError OnErrorFunc
}
OnFlushFunc = func(flushTask task.Task, events []Event)
OnErrorFunc = func(err E.NestedError)
OnErrorFunc = func(err E.Error)
)
const eventQueueCapacity = 10
@@ -35,40 +37,45 @@ const eventQueueCapacity = 10
// flushTask.Finish must be called after the flush is done,
// but the onFlush function can return earlier (e.g. run in another goroutine).
//
// If task is cancelled before the flushInterval is reached, the events in queue will be discarded.
// If task is canceled before the flushInterval is reached, the events in queue will be discarded.
func NewEventQueue(parent task.Task, flushInterval time.Duration, onFlush OnFlushFunc, onError OnErrorFunc) *EventQueue {
return &EventQueue{
task: parent.Subtask("event queue"),
queue: make([]Event, 0, eventQueueCapacity),
ticker: time.NewTicker(flushInterval),
onFlush: onFlush,
onError: onError,
task: parent.Subtask("event queue"),
queue: make([]Event, 0, eventQueueCapacity),
ticker: time.NewTicker(flushInterval),
flushInterval: flushInterval,
onFlush: onFlush,
onError: onError,
}
}
func (e *EventQueue) Start(eventCh <-chan Event, errCh <-chan E.NestedError) {
func (e *EventQueue) Start(eventCh <-chan Event, errCh <-chan E.Error) {
go func() {
defer e.ticker.Stop()
for {
select {
case <-e.task.Context().Done():
e.task.Finish(e.task.FinishCause().Error())
return
case <-e.ticker.C:
if len(e.queue) > 0 {
flushTask := e.task.Subtask("flush events")
queue := e.queue
e.queue = make([]Event, 0, eventQueueCapacity)
go func() {
defer func() {
if err := recover(); err != nil {
e.onError(E.PanicRecv("onFlush: %s", err).Subject(e.task.Parent().Name()))
}
if !common.IsDebug {
go func() {
defer func() {
if err := recover(); err != nil {
e.onError(E.PanicRecv("onFlush: %s", err).Subject(e.task.Parent().Name()))
}
}()
e.onFlush(flushTask, queue)
}()
e.onFlush(flushTask, queue)
}()
} else {
go e.onFlush(flushTask, queue)
}
flushTask.Wait()
}
e.ticker.Reset(e.flushInterval)
case event, ok := <-eventCh:
e.queue = append(e.queue, event)
if !ok {

View File

@@ -9,9 +9,9 @@ import (
type fileWatcher struct {
relPath string
eventCh chan Event
errCh chan E.NestedError
errCh chan E.Error
}
func (fw *fileWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.NestedError) {
func (fw *fileWatcher) Events(ctx context.Context) (<-chan Event, <-chan E.Error) {
return fw.eventCh, fw.errCh
}

View File

@@ -0,0 +1,28 @@
package health
import (
"encoding/json"
"fmt"
"time"
"github.com/yusing/go-proxy/internal/net/types"
"github.com/yusing/go-proxy/internal/task"
)
type (
HealthMonitor interface {
task.TaskStarter
task.TaskFinisher
fmt.Stringer
json.Marshaler
Status() Status
Uptime() time.Duration
Name() string
}
HealthChecker interface {
CheckHealth() (healthy bool, detail string, err error)
URL() types.URL
Config() *HealthCheckConfig
UpdateURL(url types.URL)
}
)

View File

@@ -2,9 +2,7 @@ package health
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
E "github.com/yusing/go-proxy/internal/error"
@@ -15,21 +13,6 @@ import (
)
type (
HealthMonitor interface {
task.TaskStarter
task.TaskFinisher
fmt.Stringer
json.Marshaler
Status() Status
Uptime() time.Duration
Name() string
}
HealthChecker interface {
CheckHealth() (healthy bool, detail string, err error)
URL() types.URL
Config() *HealthCheckConfig
UpdateURL(url types.URL)
}
HealthCheckFunc func() (healthy bool, detail string, err error)
monitor struct {
service string
@@ -71,7 +54,7 @@ func (mon *monitor) ContextWithTimeout(cause string) (ctx context.Context, cance
}
// Start implements task.TaskStarter.
func (mon *monitor) Start(routeSubtask task.Task) E.NestedError {
func (mon *monitor) Start(routeSubtask task.Task) E.Error {
mon.service = routeSubtask.Parent().Name()
mon.task = routeSubtask
@@ -84,7 +67,6 @@ func (mon *monitor) Start(routeSubtask task.Task) E.NestedError {
if mon.status.Load() != StatusError {
mon.status.Store(StatusUnknown)
}
mon.task.Finish(mon.task.FinishCause().Error())
}()
if err := mon.checkUpdateHealth(); err != nil {
@@ -115,7 +97,7 @@ func (mon *monitor) Start(routeSubtask task.Task) E.NestedError {
}
// Finish implements task.TaskFinisher.
func (mon *monitor) Finish(reason string) {
func (mon *monitor) Finish(reason any) {
mon.task.Finish(reason)
}
@@ -169,10 +151,10 @@ func (mon *monitor) MarshalJSON() ([]byte, error) {
}).MarshalJSON()
}
func (mon *monitor) checkUpdateHealth() E.NestedError {
func (mon *monitor) checkUpdateHealth() E.Error {
healthy, detail, err := mon.checkHealth()
if err != nil {
defer mon.task.Finish(err.Error())
defer mon.task.Finish(err)
mon.status.Store(StatusError)
if !errors.Is(err, context.Canceled) {
return E.Failure("check health").With(err)

View File

@@ -10,5 +10,5 @@ import (
type Event = events.Event
type Watcher interface {
Events(ctx context.Context) (<-chan Event, <-chan E.NestedError)
Events(ctx context.Context) (<-chan Event, <-chan E.Error)
}