mirror of
https://github.com/yusing/godoxy.git
synced 2026-04-24 01:08:31 +02:00
fixed loadbalancer with idlewatcher, fixed reload issue
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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,
|
||||
)}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
28
internal/watcher/health/health_checker.go
Normal file
28
internal/watcher/health/health_checker.go
Normal 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)
|
||||
}
|
||||
)
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user