Files
platform/cmd/tasks/main.go

142 lines
2.7 KiB
Go

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
}