Files
VLoop/backend/cmd/worker/main.go

298 lines
6.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package main
import (
"context"
"feedsystem_video_go/internal/config"
"feedsystem_video_go/internal/db"
rediscache "feedsystem_video_go/internal/middleware/redis"
"feedsystem_video_go/internal/observability"
"feedsystem_video_go/internal/social"
"feedsystem_video_go/internal/video"
"feedsystem_video_go/internal/worker"
"log"
"os"
"os/signal"
"strconv"
"syscall"
"time"
amqp "github.com/rabbitmq/amqp091-go"
)
const (
socialExchange = "social.events"
socialQueue = "social.events"
socialBindingKey = "social.*"
likeExchange = "like.events"
likeQueue = "like.events"
likeBindingKey = "like.*"
commentExchange = "comment.events"
commentQueue = "comment.events"
commentBindingKey = "comment.*"
popularityExchange = "video.popularity.events"
popularityQueue = "video.popularity.events"
popularityBindingKey = "video.popularity.*"
)
func main() {
// 加载配置
const configPath = "configs/config.yaml"
log.Printf("Loading config from %s", configPath)
cfg, usedDefault, err := config.LoadLocalDev(configPath)
if err != nil {
log.Fatalf("Failed to load config: %v", err)
}
if usedDefault {
log.Printf("Config File %s not found, using default local config", configPath)
} else {
log.Printf("Config loaded from file: %s", configPath)
}
// 连接数据库
sqlDB, err := db.NewDB(cfg.Database)
if err != nil {
log.Fatalf("Failed to connect database: %v", err)
}
defer db.CloseDB(sqlDB)
// 连接 Redis用于流行度更新
cache, err := rediscache.NewFromEnv(&cfg.Redis)
if err != nil {
log.Printf("Redis config error (popularity worker disabled): %v", err)
cache = nil
} else {
pingCtx, cancel := context.WithTimeout(context.Background(), 300*time.Millisecond)
defer cancel()
if err := cache.Ping(pingCtx); err != nil {
log.Printf("Redis not available (popularity worker disabled): %v", err)
_ = cache.Close()
cache = nil
} else {
defer cache.Close()
log.Printf("Redis connected (popularity worker enabled)")
}
}
// 连接 RabbitMQ
url := "amqp://" + cfg.RabbitMQ.Username + ":" + cfg.RabbitMQ.Password + "@" + cfg.RabbitMQ.Host + ":" + strconv.Itoa(cfg.RabbitMQ.Port) + "/"
conn, err := amqp.Dial(url)
if err != nil {
log.Fatalf("Failed to connect rabbitmq: %v", err)
}
defer conn.Close()
// 创建 RabbitMQ 通道
ch, err := conn.Channel()
if err != nil {
log.Fatalf("Failed to open rabbitmq channel: %v", err)
}
defer ch.Close()
// 声明 Social 交换机和队列
if err := declareSocialTopology(ch); err != nil {
log.Fatalf("Failed to declare social topology: %v", err)
}
if err := declareLikeTopology(ch); err != nil {
log.Fatalf("Failed to declare like topology: %v", err)
}
if err := declareCommentTopology(ch); err != nil {
log.Fatalf("Failed to declare comment topology: %v", err)
}
if cache != nil {
if err := declarePopularityTopology(ch); err != nil {
log.Fatalf("Failed to declare popularity topology: %v", err)
}
}
if err := ch.Qos(50, 0, false); err != nil {
log.Fatalf("Failed to set qos: %v", err)
}
repo := social.NewSocialRepository(sqlDB)
socialWorker := worker.NewSocialWorker(ch, repo, socialQueue)
videoRepo := video.NewVideoRepository(sqlDB)
likeRepo := video.NewLikeRepository(sqlDB)
commentRepo := video.NewCommentRepository(sqlDB)
likeWorker := worker.NewLikeWorker(ch, likeRepo, videoRepo, likeQueue)
commentWorker := worker.NewCommentWorker(ch, commentRepo, videoRepo, commentQueue)
var popularityWorker *worker.PopularityWorker
if cache != nil {
popularityWorker = worker.NewPopularityWorker(ch, cache, popularityQueue)
}
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
pprofServer, err := observability.StartPprofServer(
"Worker",
cfg.ObservabilityConfig.Pprof.Enabled,
cfg.ObservabilityConfig.Pprof.WorkerAddr,
)
if err != nil {
log.Printf("Failed to start worker pprof server: %v", err)
}
defer func() {
shutdownCtx, cancel := context.WithTimeout(context.Background(), 3 * time.Second)
defer cancel()
if err := observability.Shutdown(shutdownCtx, pprofServer); err != nil {
log.Printf("Failed to shutdown worker pprof server: %v", err)
}
}()
errCh := make(chan error, 4)
log.Printf("Worker started, consuming queue=%s", socialQueue)
go func() { errCh <- socialWorker.Run(ctx) }()
log.Printf("Worker started, consuming queue=%s", likeQueue)
go func() { errCh <- likeWorker.Run(ctx) }()
log.Printf("Worker started, consuming queue=%s", commentQueue)
go func() { errCh <- commentWorker.Run(ctx) }()
if popularityWorker != nil {
log.Printf("Worker started, consuming queue=%s", popularityQueue)
go func() { errCh <- popularityWorker.Run(ctx) }()
}
err = <-errCh
if err != nil && err != context.Canceled {
log.Fatalf("Worker stopped: %v", err)
}
log.Printf("Worker stopped")
}
func declareSocialTopology(ch *amqp.Channel) error {
if err := ch.ExchangeDeclare(
socialExchange,
"topic",
true,
false,
false,
false,
nil,
); err != nil {
return err
}
q, err := ch.QueueDeclare(
socialQueue,
true,
false,
false,
false,
nil,
)
if err != nil {
return err
}
if err := ch.QueueBind(
q.Name,
socialBindingKey,
socialExchange,
false,
nil,
); err != nil {
return err
}
return nil
}
func declarePopularityTopology(ch *amqp.Channel) error {
if err := ch.ExchangeDeclare(
popularityExchange,
"topic",
true,
false,
false,
false,
nil,
); err != nil {
return err
}
q, err := ch.QueueDeclare(
popularityQueue,
true,
false,
false,
false,
nil,
)
if err != nil {
return err
}
return ch.QueueBind(
q.Name,
popularityBindingKey,
popularityExchange,
false,
nil,
)
}
func declareLikeTopology(ch *amqp.Channel) error {
if err := ch.ExchangeDeclare(
likeExchange,
"topic",
true,
false,
false,
false,
nil,
); err != nil {
return err
}
q, err := ch.QueueDeclare(
likeQueue,
true,
false,
false,
false,
nil,
)
if err != nil {
return err
}
return ch.QueueBind(
q.Name,
likeBindingKey,
likeExchange,
false,
nil,
)
}
func declareCommentTopology(ch *amqp.Channel) error {
if err := ch.ExchangeDeclare(
commentExchange,
"topic",
true,
false,
false,
false,
nil,
); err != nil {
return err
}
q, err := ch.QueueDeclare(
commentQueue,
true,
false,
false,
false,
nil,
)
if err != nil {
return err
}
return ch.QueueBind(
q.Name,
commentBindingKey,
commentExchange,
false,
nil,
)
}