package app import ( "context" "net" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" "hyapp/services/activity-service/internal/config" mysqlstorage "hyapp/services/activity-service/internal/storage/mysql" ) type externalClients struct { userConn *grpc.ClientConn walletConn *grpc.ClientConn roomConn *grpc.ClientConn } func openRepository(ctx context.Context, cfg config.Config) (*mysqlstorage.Repository, error) { // activity-service 的活动配置、消费幂等、排行榜和 outbox 都落在 MySQL。 // migrate 放在启动阶段执行,保证本地新增活动模块时不需要手工补表后才能启动。 repository, err := mysqlstorage.Open(ctx, cfg.MySQLDSN) if err != nil { return nil, err } if cfg.MySQLAutoMigrate { if err := repository.Migrate(ctx); err != nil { _ = repository.Close() return nil, err } } return repository, nil } func listenGRPC(addr string) (net.Listener, error) { return net.Listen("tcp", addr) } func dialExternalClients(cfg config.Config) (externalClients, error) { // 三个下游连接在 App 级别统一持有;各业务 service 只拿生成后的 typed client。 // 这样 Close 时可以按连接维度回收资源,新增模块也不会重复创建 gRPC 连接池。 var clients externalClients var err error clients.userConn, err = grpc.Dial(cfg.UserServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { return externalClients{}, err } clients.walletConn, err = grpc.Dial(cfg.WalletServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { clients.Close() return externalClients{}, err } clients.roomConn, err = grpc.Dial(cfg.RoomServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials())) if err != nil { clients.Close() return externalClients{}, err } return clients, nil } func (c externalClients) Close() { if c.roomConn != nil { _ = c.roomConn.Close() } if c.walletConn != nil { _ = c.walletConn.Close() } if c.userConn != nil { _ = c.userConn.Close() } }