package app import ( "context" "errors" "net" "sync" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" healthgrpc "google.golang.org/grpc/health/grpc_health_v1" gamev1 "hyapp.local/api/proto/game/v1" "hyapp/pkg/grpchealth" "hyapp/pkg/logx" "hyapp/services/game-service/internal/client" "hyapp/services/game-service/internal/config" gameservice "hyapp/services/game-service/internal/service/game" mysqlstorage "hyapp/services/game-service/internal/storage/mysql" grpcserver "hyapp/services/game-service/internal/transport/grpc" ) // App 装配 game-service gRPC 入口和持久化依赖。 type App struct { server *grpc.Server listener net.Listener health *grpchealth.ServingChecker repo *mysqlstorage.Repository walletConn *grpc.ClientConn userConn *grpc.ClientConn closeOnce sync.Once } func New(cfg config.Config) (*App, error) { startupCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() repo, err := mysqlstorage.Open(startupCtx, cfg.MySQLDSN) if err != nil { return nil, err } walletConn, err := grpc.Dial(cfg.WalletServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { _ = repo.Close() return nil, err } userConn, err := grpc.Dial(cfg.UserServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { _ = walletConn.Close() _ = repo.Close() return nil, err } listener, err := net.Listen("tcp", cfg.GRPCAddr) if err != nil { _ = userConn.Close() _ = walletConn.Close() _ = repo.Close() return nil, err } server := grpc.NewServer(grpc.UnaryInterceptor(logx.UnaryServerInterceptor("game-service"))) svc := gameservice.New(gameservice.Config{LaunchSessionTTL: cfg.LaunchSessionTTL}, repo, client.NewWalletClient(walletConn), client.NewUserClient(userConn)) transport := grpcserver.NewServer(svc) gamev1.RegisterGameAppServiceServer(server, transport) gamev1.RegisterGameCallbackServiceServer(server, transport) gamev1.RegisterGameAdminServiceServer(server, transport) health := grpchealth.NewServingChecker("game-service") healthgrpc.RegisterHealthServer(server, grpchealth.NewServer(health)) return &App{server: server, listener: listener, health: health, repo: repo, walletConn: walletConn, userConn: userConn}, nil } func (a *App) Run() error { a.health.MarkServing() err := a.server.Serve(a.listener) a.health.MarkStopped() if errors.Is(err, grpc.ErrServerStopped) { return nil } return err } func (a *App) Close() { a.closeOnce.Do(func() { a.health.MarkDraining() a.server.GracefulStop() if a.walletConn != nil { _ = a.walletConn.Close() } if a.userConn != nil { _ = a.userConn.Close() } if a.repo != nil { _ = a.repo.Close() } }) }