package main import ( "context" "encoding/json" "errors" "io" "log/slog" "net/http" "platform/pkg/env" "platform/pkg/logs" "platform/pkg/rds" "reflect" "strconv" "strings" "sync" "time" "github.com/redis/go-redis/v9" ) func main() { Start() } var taskList = make(map[string]func(ctx context.Context, curr time.Time) error) func Start() { ctx := context.Background() env.Init() logs.Init() rds.Init() taskList["stopChannels"] = stopChannels ticker := time.NewTicker(time.Second) defer ticker.Stop() // 互斥锁确保同一时间只有一个协程运行 // 如果之前的 tick 操作未完成,则跳过当前 tick var mutex = &sync.Mutex{} for curr := range ticker.C { if mutex.TryLock() { err := process(ctx, curr) if err != nil { panic(err) } mutex.Unlock() } else { slog.Warn("skip tick", slog.String("tick", curr.Format("2006-01-02 15:04:05"))) } } } func process(ctx context.Context, curr time.Time) error { // todo 异步化 for name, task := range taskList { err := task(ctx, curr) if err != nil { slog.Error("task failed", slog.String("task", name), slog.String("error", err.Error())) } } return nil } func stopChannels(ctx context.Context, curr time.Time) error { // 获取并删除 script := redis.NewScript(` local result = redis.call('ZRANGEBYSCORE', KEYS[1], 0, ARGV[1]) if #result > 0 then redis.call('ZREMRANGEBYSCORE', KEYS[1], 0, ARGV[1]) end return result `) // 执行脚本 result, err := script.Run(ctx, rds.Client, []string{"tasks:channel"}, curr.Unix()).Result() if err != nil { return err } // 处理结果 list, ok := result.([]any) if !ok { return errors.New("failed to convert result to []string") } var ids = make([]int, len(list)) for i, item := range list { idStr, ok := item.(string) if !ok { slog.Debug(reflect.TypeOf(item).String()) return errors.New("failed to convert item to string") } id, err := strconv.Atoi(idStr) if err != nil { return err } ids[i] = id } if len(ids) == 0 { return nil } var body = map[string]any{ "by_ids": ids, } bodyStr, err := json.Marshal(body) if err != nil { return err } req, err := http.NewRequest( http.MethodPost, "http://localhost:8080/api/channel/remove", // todo 环境变量获取服务地址 strings.NewReader(string(bodyStr)), ) if err != nil { return err } req.Header.Set("Content-Type", "application/json") req.SetBasicAuth("tasks", "tasks") resp, err := http.DefaultClient.Do(req) if err != nil { return err } if resp.StatusCode != http.StatusOK { body, err := io.ReadAll(resp.Body) if err != nil { return err } return errors.New("failed to stop channels: " + string(body)) } return nil }