topfans/backend/services/notificationService/main.go
2026-06-16 21:30:58 +08:00

206 lines
5.8 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 是 notification service 的入口程序。
//
// 设计要点(参考 socialService/main.go 的结构):
// - 通过 flag + 环境变量注入运行参数端口、DB 等)。
// - 启动顺序logger -> DB -> AutoMigrate -> service -> provider -> Dubbo server。
// - 使用 Dubbo Triple 协议暴露 notification.proto 中的 NotificationService。
// - 优雅关闭:监听 SIGINT/SIGTERM关停 health server 与 DB 连接。
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"strconv"
"syscall"
"dubbo.apache.org/dubbo-go/v3/protocol"
"dubbo.apache.org/dubbo-go/v3/server"
_ "dubbo.apache.org/dubbo-go/v3/imports"
"github.com/topfans/backend/pkg/database"
"github.com/topfans/backend/pkg/health"
"github.com/topfans/backend/pkg/logger"
notifPb "github.com/topfans/backend/pkg/proto/notification"
"github.com/topfans/backend/services/notificationService/model"
"github.com/topfans/backend/services/notificationService/provider"
"github.com/topfans/backend/services/notificationService/service"
)
var (
port = flag.Int("port", getEnvInt("PORT", 20010), "Dubbo service port")
dbHost = flag.String("db-host", getEnv("DB_HOST", "localhost"), "Database host")
dbPort = flag.Int("db-port", getEnvInt("DB_PORT", 5432), "Database port")
dbUser = flag.String("db-user", getEnv("DB_USER", "postgres"), "Database user")
dbPassword = flag.String("db-password", getEnv("DB_PASSWORD", ""), "Database password")
dbName = flag.String("db-name", getEnv("DB_NAME", "top-fans"), "Database name")
healthHndl *health.Handler
)
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
func getEnvInt(key string, fallback int) int {
if v := os.Getenv(key); v != "" {
if n, err := strconv.Atoi(v); err == nil {
return n
}
}
return fallback
}
func main() {
flag.Parse()
// 1) 初始化日志(必须在最前面,便于后续捕获启动错误)
env := os.Getenv("ENV")
if env == "" {
env = "development"
}
if err := logger.Init(logger.Config{
ServiceName: "notification-service",
Environment: env,
LogLevel: os.Getenv("LOG_LEVEL"),
}); err != nil {
panic(fmt.Sprintf("Failed to initialize logger: %v", err))
}
defer logger.Sync()
logger.Sugar.Info("Starting Notification Service...")
// 2) 初始化数据库
if err := initDatabase(); err != nil {
logger.Sugar.Fatalf("Failed to initialize database: %v", err)
}
// 3) 自动迁移数据库表notification + notification_stats
if err := autoMigrate(); err != nil {
logger.Sugar.Fatalf("Failed to migrate database: %v", err)
}
// 4) 启动 Dubbo server + 注册 NotificationService
if err := initDubboService(); err != nil {
logger.Sugar.Fatalf("Failed to initialize Dubbo service: %v", err)
}
// 5) 等待退出信号(优雅关闭)
logger.Sugar.Info("Notification service started successfully. Press Ctrl+C to exit.")
gracefulShutdown()
}
// initDatabase 初始化数据库连接(复用 socialService 的写法)。
func initDatabase() error {
config := database.Config{
Host: *dbHost,
Port: *dbPort,
User: *dbUser,
Password: *dbPassword,
DBName: *dbName,
SSLMode: "disable",
TimeZone: "Asia/Shanghai",
}
return database.Init(config)
}
// autoMigrate 自动迁移 notification 相关表。
func autoMigrate() error {
db := database.GetDB()
if db == nil {
return fmt.Errorf("database is not initialized")
}
tables := []interface{}{
&model.Notification{},
&model.NotificationStats{},
}
for _, table := range tables {
if err := db.AutoMigrate(table); err != nil {
return fmt.Errorf("failed to migrate table: %w", err)
}
}
logger.Sugar.Info("Database migration completed successfully")
return nil
}
// gracefulShutdown 优雅关闭health server + DB
func gracefulShutdown() {
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
logger.Sugar.Info("Shutting down server...")
if healthHndl != nil {
healthHndl.Stop()
}
if err := database.Close(); err != nil {
logger.Sugar.Errorf("Error closing database: %v", err)
}
logger.Sugar.Info("Server exited")
}
// initDubboService 初始化 Dubbo-go 服务。
//
// 启动健康检查 HTTP server端口 = dubbo port + 1000例如 21010
// 然后构造 service / provider注册到 Triple 协议的 Dubbo server。
func initDubboService() error {
// 健康检查 HTTP server
healthPort := *port + 1000 // e.g., 20010 -> 21010
healthHndl = health.NewHandler("notification-service", healthPort)
healthHndl.Start()
db := database.GetDB()
if db == nil {
return fmt.Errorf("database is not initialized")
}
// 业务层service 只依赖 DBrepository 内部自行 New
notifService := service.NewNotificationService(db)
// RPC Provider
notifProvider := provider.NewNotificationProvider(notifService)
// Dubbo ServerTriple 协议)
srv, err := server.NewServer(
server.WithServerProtocol(
protocol.WithPort(*port),
protocol.WithTriple(),
),
)
if err != nil {
return fmt.Errorf("failed to create Dubbo server: %w", err)
}
// 注册 NotificationServiceHandler来自 notification.triple.go
if err := notifPb.RegisterNotificationServiceHandler(srv, notifProvider); err != nil {
return fmt.Errorf("failed to register NotificationService handler: %w", err)
}
logger.Sugar.Info("Dubbo-go notification provider registered successfully",
"service", notifPb.NotificationServiceName,
"port", *port,
)
// 后台启动 Dubbo server阻塞当前 goroutine 时 main 会卡在 srv.Serve 上,
// 所以放 goroutine 里,由 gracefulShutdown 通过 os.Signal 触发退出)。
go func() {
if err := srv.Serve(); err != nil {
logger.Sugar.Fatalf("Failed to serve Dubbo: %v", err)
}
}()
return nil
}