174 lines
5.9 KiB
Go
174 lines
5.9 KiB
Go
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"
|
|
activityv1 "hyapp.local/api/proto/activity/v1"
|
|
walletv1 "hyapp.local/api/proto/wallet/v1"
|
|
"hyapp/pkg/grpchealth"
|
|
"hyapp/pkg/logx"
|
|
"hyapp/pkg/tencentim"
|
|
"hyapp/services/activity-service/internal/client"
|
|
"hyapp/services/activity-service/internal/config"
|
|
activityservice "hyapp/services/activity-service/internal/service/activity"
|
|
broadcastservice "hyapp/services/activity-service/internal/service/broadcast"
|
|
messageservice "hyapp/services/activity-service/internal/service/message"
|
|
taskservice "hyapp/services/activity-service/internal/service/task"
|
|
mysqlstorage "hyapp/services/activity-service/internal/storage/mysql"
|
|
grpcserver "hyapp/services/activity-service/internal/transport/grpc"
|
|
)
|
|
|
|
// App 装配 activity-service gRPC 入口和底座依赖。
|
|
type App struct {
|
|
server *grpc.Server
|
|
listener net.Listener
|
|
health *grpchealth.ServingChecker
|
|
mysqlRepo *mysqlstorage.Repository
|
|
broadcast *broadcastservice.Service
|
|
broadcastWorkerEnabled bool
|
|
userConn *grpc.ClientConn
|
|
walletConn *grpc.ClientConn
|
|
workerCtx context.Context
|
|
workerStop context.CancelFunc
|
|
workerWG sync.WaitGroup
|
|
closeOnce sync.Once
|
|
}
|
|
|
|
// New 初始化 activity-service 应用。
|
|
func New(cfg config.Config) (*App, error) {
|
|
startupCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
// activity-service 的活动状态、消费幂等和 outbox 都必须使用 MySQL。
|
|
repository, err := mysqlstorage.Open(startupCtx, cfg.MySQLDSN)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
listener, err := net.Listen("tcp", cfg.GRPCAddr)
|
|
if err != nil {
|
|
_ = repository.Close()
|
|
return nil, err
|
|
}
|
|
userConn, err := grpc.Dial(cfg.UserServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
_ = listener.Close()
|
|
_ = repository.Close()
|
|
return nil, err
|
|
}
|
|
walletConn, err := grpc.Dial(cfg.WalletServiceAddr, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
|
if err != nil {
|
|
_ = userConn.Close()
|
|
_ = listener.Close()
|
|
_ = repository.Close()
|
|
return nil, err
|
|
}
|
|
|
|
server := grpc.NewServer(grpc.UnaryInterceptor(logx.UnaryServerInterceptor("activity-service")))
|
|
svc := activityservice.New(activityservice.Config{NodeID: cfg.NodeID}, repository)
|
|
messageSvc := messageservice.New(messageservice.Config{NodeID: cfg.NodeID}, repository, messageservice.WithTargetSource(client.NewGRPCUserTargetSource(userConn)))
|
|
taskSvc := taskservice.New(repository, walletv1.NewWalletServiceClient(walletConn))
|
|
var broadcastPublisher broadcastservice.Publisher
|
|
if cfg.TencentIM.Enabled {
|
|
tencentClient, err := tencentim.NewRESTClient(cfg.TencentIM.RESTConfig())
|
|
if err != nil {
|
|
_ = walletConn.Close()
|
|
_ = userConn.Close()
|
|
_ = listener.Close()
|
|
_ = repository.Close()
|
|
return nil, err
|
|
}
|
|
broadcastPublisher = tencentClient
|
|
}
|
|
if cfg.Broadcast.Enabled && broadcastPublisher == nil {
|
|
_ = walletConn.Close()
|
|
_ = userConn.Close()
|
|
_ = listener.Close()
|
|
_ = repository.Close()
|
|
return nil, errors.New("broadcast worker requires tencent_im.enabled")
|
|
}
|
|
broadcastSvc := broadcastservice.New(broadcastservice.Config{
|
|
NodeID: cfg.NodeID,
|
|
SuperGiftMinValue: cfg.Broadcast.SuperGiftMinValue,
|
|
WorkerBatchSize: cfg.Broadcast.WorkerBatchSize,
|
|
WorkerLockTTL: cfg.Broadcast.WorkerLockTTL,
|
|
WorkerMaxRetry: cfg.Broadcast.WorkerMaxRetry,
|
|
WorkerPollInterval: cfg.Broadcast.WorkerPollInterval,
|
|
EnsureGroupsOnStartup: cfg.Broadcast.EnsureGroupsOnStartup,
|
|
}, repository, broadcastPublisher, client.NewGRPCRegionSource(userConn))
|
|
activityv1.RegisterActivityServiceServer(server, grpcserver.NewServer(svc))
|
|
activityv1.RegisterMessageInboxServiceServer(server, grpcserver.NewMessageServer(messageSvc))
|
|
activityv1.RegisterActivityCronServiceServer(server, grpcserver.NewCronServer(messageSvc))
|
|
activityv1.RegisterTaskServiceServer(server, grpcserver.NewTaskServer(taskSvc))
|
|
activityv1.RegisterAdminTaskServiceServer(server, grpcserver.NewAdminTaskServer(taskSvc))
|
|
broadcastServer := grpcserver.NewBroadcastServer(broadcastSvc)
|
|
activityv1.RegisterBroadcastServiceServer(server, broadcastServer)
|
|
activityv1.RegisterRoomEventConsumerServiceServer(server, broadcastServer)
|
|
health := grpchealth.NewServingChecker("activity-service")
|
|
healthgrpc.RegisterHealthServer(server, grpchealth.NewServer(health))
|
|
workerCtx, workerStop := context.WithCancel(context.Background())
|
|
|
|
return &App{
|
|
server: server,
|
|
listener: listener,
|
|
health: health,
|
|
mysqlRepo: repository,
|
|
broadcast: broadcastSvc,
|
|
broadcastWorkerEnabled: cfg.Broadcast.Enabled,
|
|
userConn: userConn,
|
|
walletConn: walletConn,
|
|
workerCtx: workerCtx,
|
|
workerStop: workerStop,
|
|
}, nil
|
|
}
|
|
|
|
// Run 启动 gRPC 服务。
|
|
func (a *App) Run() error {
|
|
a.health.MarkServing()
|
|
if a.broadcastWorkerEnabled && a.broadcast != nil && a.workerCtx != nil {
|
|
a.workerWG.Go(func() {
|
|
a.broadcast.RunWorker(a.workerCtx, broadcastservice.WorkerOptions{})
|
|
})
|
|
}
|
|
err := a.server.Serve(a.listener)
|
|
if a.workerStop != nil {
|
|
a.workerStop()
|
|
}
|
|
a.workerWG.Wait()
|
|
a.health.MarkStopped()
|
|
if errors.Is(err, grpc.ErrServerStopped) {
|
|
return nil
|
|
}
|
|
|
|
return err
|
|
}
|
|
|
|
// Close 关闭 gRPC 服务。
|
|
func (a *App) Close() {
|
|
a.closeOnce.Do(func() {
|
|
a.health.MarkDraining()
|
|
if a.workerStop != nil {
|
|
a.workerStop()
|
|
}
|
|
a.workerWG.Wait()
|
|
a.server.GracefulStop()
|
|
if a.userConn != nil {
|
|
_ = a.userConn.Close()
|
|
}
|
|
if a.walletConn != nil {
|
|
_ = a.walletConn.Close()
|
|
}
|
|
if a.mysqlRepo != nil {
|
|
// MySQL 连接池最后关闭,保证 drain 中的查询或消费提交能完成。
|
|
_ = a.mysqlRepo.Close()
|
|
}
|
|
})
|
|
}
|