frist
This commit is contained in:
167
internal/telegram/scheduler.go
Normal file
167
internal/telegram/scheduler.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package telegram
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"proxyrotator/internal/config"
|
||||
"proxyrotator/internal/model"
|
||||
"proxyrotator/internal/store"
|
||||
"proxyrotator/internal/tester"
|
||||
)
|
||||
|
||||
// Scheduler 定时测活调度器
|
||||
type Scheduler struct {
|
||||
mu sync.Mutex
|
||||
store store.ProxyStore
|
||||
notifier *Notifier
|
||||
tester *tester.HTTPTester
|
||||
cfg *config.Config
|
||||
|
||||
ticker *time.Ticker
|
||||
stopChan chan struct{}
|
||||
running bool
|
||||
}
|
||||
|
||||
// NewScheduler 创建调度器
|
||||
func NewScheduler(store store.ProxyStore, notifier *Notifier, cfg *config.Config) *Scheduler {
|
||||
return &Scheduler{
|
||||
store: store,
|
||||
notifier: notifier,
|
||||
tester: tester.NewHTTPTester(),
|
||||
cfg: cfg,
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Start 启动调度器
|
||||
func (s *Scheduler) Start() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.running {
|
||||
return
|
||||
}
|
||||
|
||||
interval := time.Duration(s.cfg.TelegramTestIntervalMin) * time.Minute
|
||||
if interval < 5*time.Minute {
|
||||
interval = 5 * time.Minute
|
||||
}
|
||||
|
||||
s.ticker = time.NewTicker(interval)
|
||||
s.stopChan = make(chan struct{})
|
||||
s.running = true
|
||||
|
||||
go s.loop()
|
||||
slog.Info("telegram scheduler started", "interval", interval)
|
||||
}
|
||||
|
||||
// Stop 停止调度器
|
||||
func (s *Scheduler) Stop() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if !s.running {
|
||||
return
|
||||
}
|
||||
|
||||
if s.ticker != nil {
|
||||
s.ticker.Stop()
|
||||
}
|
||||
close(s.stopChan)
|
||||
s.running = false
|
||||
slog.Info("telegram scheduler stopped")
|
||||
}
|
||||
|
||||
// loop 调度循环
|
||||
func (s *Scheduler) loop() {
|
||||
for {
|
||||
select {
|
||||
case <-s.stopChan:
|
||||
return
|
||||
case <-s.ticker.C:
|
||||
if err := s.RunTest(context.Background()); err != nil {
|
||||
slog.Error("scheduled test failed", "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RunTest 执行测活(所有分组)
|
||||
func (s *Scheduler) RunTest(ctx context.Context) error {
|
||||
return s.RunTestWithGroup(ctx, "")
|
||||
}
|
||||
|
||||
// RunTestWithGroup 执行测活(指定分组)
|
||||
func (s *Scheduler) RunTestWithGroup(ctx context.Context, group string) error {
|
||||
slog.Info("running scheduled proxy test", "group", group)
|
||||
|
||||
// 获取待测试代理
|
||||
query := model.ProxyQuery{
|
||||
Group: group,
|
||||
StatusIn: []model.ProxyStatus{model.StatusUnknown, model.StatusAlive},
|
||||
OnlyEnabled: true,
|
||||
Limit: 1000,
|
||||
}
|
||||
|
||||
proxies, err := s.store.List(ctx, query)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(proxies) == 0 {
|
||||
slog.Info("no proxies to test")
|
||||
return nil
|
||||
}
|
||||
|
||||
// 构建测试规格
|
||||
spec := model.TestSpec{
|
||||
URL: s.cfg.TelegramTestURL,
|
||||
Method: "GET",
|
||||
Timeout: time.Duration(s.cfg.TelegramTestTimeoutMs) * time.Millisecond,
|
||||
}
|
||||
|
||||
// 执行测试
|
||||
results := s.tester.TestBatch(ctx, proxies, spec, 50)
|
||||
|
||||
// 统计结果
|
||||
alive, dead := 0, 0
|
||||
for _, r := range results {
|
||||
now := r.CheckedAt
|
||||
if r.OK {
|
||||
alive++
|
||||
status := model.StatusAlive
|
||||
_ = s.store.UpdateHealth(ctx, r.ProxyID, model.HealthPatch{
|
||||
Status: &status,
|
||||
ScoreDelta: 1,
|
||||
SuccessInc: 1,
|
||||
LatencyMs: &r.LatencyMs,
|
||||
CheckedAt: &now,
|
||||
})
|
||||
} else {
|
||||
dead++
|
||||
status := model.StatusDead
|
||||
_ = s.store.UpdateHealth(ctx, r.ProxyID, model.HealthPatch{
|
||||
Status: &status,
|
||||
ScoreDelta: -3,
|
||||
FailInc: 1,
|
||||
CheckedAt: &now,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
slog.Info("scheduled test completed", "tested", len(results), "alive", alive, "dead", dead)
|
||||
|
||||
// 检查是否需要告警
|
||||
total := len(results)
|
||||
if total > 0 {
|
||||
alivePercent := float64(alive) / float64(total) * 100
|
||||
if alivePercent < float64(s.cfg.TelegramAlertThreshold) {
|
||||
s.notifier.SendAlert(ctx, alive, dead, total, alivePercent)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user