diff --git a/config/local.yaml b/config/local.yaml index e8d0d87..0505ffc 100644 --- a/config/local.yaml +++ b/config/local.yaml @@ -4,3 +4,12 @@ app: http_addr: ":8081" grpc_addr: ":9001" shutdown_timeout: 10s + +registry: + enabled: false + provider: "" + endpoint: "" + service_name: "chatappuser" + instance_id: "" + register_timeout: 3s + deregister_timeout: 5s diff --git a/config/prod.yaml b/config/prod.yaml index 3e1f045..a351887 100644 --- a/config/prod.yaml +++ b/config/prod.yaml @@ -4,3 +4,12 @@ app: http_addr: ":8081" grpc_addr: ":9001" shutdown_timeout: 10s + +registry: + enabled: false + provider: "" + endpoint: "" + service_name: "chatappuser" + instance_id: "" + register_timeout: 3s + deregister_timeout: 5s diff --git a/config/prod.yaml.example b/config/prod.yaml.example index 3e1f045..a351887 100644 --- a/config/prod.yaml.example +++ b/config/prod.yaml.example @@ -4,3 +4,12 @@ app: http_addr: ":8081" grpc_addr: ":9001" shutdown_timeout: 10s + +registry: + enabled: false + provider: "" + endpoint: "" + service_name: "chatappuser" + instance_id: "" + register_timeout: 3s + deregister_timeout: 5s diff --git a/go.mod b/go.mod index 4fe0530..c102e03 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module gitea.haiyihy.com/hy/chatappuser go 1.23.1 require ( - gitea.haiyihy.com/hy/chatappcommon v0.0.0 + gitea.haiyihy.com/hy/chatappcommon v0.1.1-0.20260404072625-9a68eb0302e5 golang.org/x/sync v0.10.0 google.golang.org/grpc v1.67.3 gopkg.in/yaml.v3 v3.0.1 @@ -16,5 +16,3 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/protobuf v1.36.11 // indirect ) - -replace gitea.haiyihy.com/hy/chatappcommon => ../Common diff --git a/go.sum b/go.sum index e0ab4a4..439e44e 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +gitea.haiyihy.com/hy/chatappcommon v0.1.1-0.20260404072625-9a68eb0302e5 h1:RUMxeDXog9ryyLU25wQR44dQYDnDGgeAM+9X3Gvkm74= +gitea.haiyihy.com/hy/chatappcommon v0.1.1-0.20260404072625-9a68eb0302e5/go.mod h1:VXhR5abAucTWdJ7j+N09ddF57Pm5ZsKg0h55ejXjQ7s= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= diff --git a/internal/app/app.go b/internal/app/app.go index 9d73267..6a2af24 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -2,9 +2,11 @@ package app import ( "context" + "fmt" "log/slog" "gitea.haiyihy.com/hy/chatappuser/internal/config" + "gitea.haiyihy.com/hy/chatappuser/internal/lifecycle" registerservice "gitea.haiyihy.com/hy/chatappuser/internal/service/register" grpcserver "gitea.haiyihy.com/hy/chatappuser/internal/transport/grpc" httpserver "gitea.haiyihy.com/hy/chatappuser/internal/transport/http" @@ -15,19 +17,25 @@ import ( type Application struct { httpServer *httpserver.Server grpcServer *grpcserver.Server + hooks lifecycle.Hooks } // New 构造用户服务应用。 func New(cfg config.Config, logger *slog.Logger) *Application { registerHandler := registerservice.New() + registryHooks := lifecycle.NewRegistryHooks(cfg.Registry, logger.With("component", "registry_hooks")) return &Application{ - httpServer: httpserver.New(cfg.App.Name, cfg.App.HTTPAddr, cfg.App.ShutdownTimeout, logger), + httpServer: httpserver.New(cfg.App.Name, cfg.App.HTTPAddr, cfg.App.ShutdownTimeout, logger, registryHooks), grpcServer: grpcserver.New(cfg.App.GRPCAddr, cfg.App.ShutdownTimeout, logger, registerHandler), + hooks: registryHooks, } } // Run 并行启动 HTTP 与 gRPC 服务,收到取消信号后优雅停机。 func (a *Application) Run(ctx context.Context) error { + if err := a.hooks.OnRegister(ctx); err != nil { + return fmt.Errorf("register lifecycle hook: %w", err) + } group, runCtx := errgroup.WithContext(ctx) group.Go(func() error { return a.httpServer.Run(runCtx) diff --git a/internal/config/config.go b/internal/config/config.go index d7cd789..14e2546 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -14,7 +14,8 @@ const DefaultPath = "config/local.yaml" // Config 汇总用户服务的运行配置。 type Config struct { - App AppConfig `yaml:"app"` + App AppConfig `yaml:"app"` + Registry RegistryConfig `yaml:"registry"` } // AppConfig 描述用户服务自身监听参数。 @@ -26,6 +27,17 @@ type AppConfig struct { ShutdownTimeout time.Duration `yaml:"shutdown_timeout"` } +// RegistryConfig 预留服务注册中心接入参数,当前只保留注销钩子扩展点。 +type RegistryConfig struct { + Enabled bool `yaml:"enabled"` + Provider string `yaml:"provider"` + Endpoint string `yaml:"endpoint"` + ServiceName string `yaml:"service_name"` + InstanceID string `yaml:"instance_id"` + RegisterTimeout time.Duration `yaml:"register_timeout"` + DeregisterTimeout time.Duration `yaml:"deregister_timeout"` +} + // Load 从 YAML 文件加载配置并校验。 func Load(path string) (Config, error) { data, err := os.ReadFile(path) @@ -55,6 +67,10 @@ func defaultConfig() Config { GRPCAddr: ":9001", ShutdownTimeout: 10 * time.Second, }, + Registry: RegistryConfig{ + RegisterTimeout: 3 * time.Second, + DeregisterTimeout: 5 * time.Second, + }, } } @@ -71,5 +87,11 @@ func validate(cfg Config) error { if cfg.App.ShutdownTimeout <= 0 { return fmt.Errorf("app.shutdown_timeout must be greater than 0") } + if cfg.Registry.RegisterTimeout <= 0 { + return fmt.Errorf("registry.register_timeout must be greater than 0") + } + if cfg.Registry.DeregisterTimeout <= 0 { + return fmt.Errorf("registry.deregister_timeout must be greater than 0") + } return nil } diff --git a/internal/lifecycle/registry.go b/internal/lifecycle/registry.go new file mode 100644 index 0000000..dc13ccb --- /dev/null +++ b/internal/lifecycle/registry.go @@ -0,0 +1,43 @@ +package lifecycle + +import ( + "context" + "log/slog" + + "gitea.haiyihy.com/hy/chatappuser/internal/config" +) + +// Hooks 预留服务注册中心生命周期钩子。 +type Hooks interface { + OnRegister(context.Context) error + OnDeregister(context.Context) error +} + +type registryHooks struct { + cfg config.RegistryConfig + logger *slog.Logger +} + +// NewRegistryHooks 返回当前阶段的占位实现,后续可以替换成真实注册中心接入。 +func NewRegistryHooks(cfg config.RegistryConfig, logger *slog.Logger) Hooks { + return ®istryHooks{ + cfg: cfg, + logger: logger, + } +} + +func (h *registryHooks) OnRegister(_ context.Context) error { + if !h.cfg.Enabled { + return nil + } + h.logger.Info("registry register hook reserved", "provider", h.cfg.Provider, "service_name", h.cfg.ServiceName, "instance_id", h.cfg.InstanceID) + return nil +} + +func (h *registryHooks) OnDeregister(_ context.Context) error { + if !h.cfg.Enabled { + return nil + } + h.logger.Info("registry deregister hook reserved", "provider", h.cfg.Provider, "service_name", h.cfg.ServiceName, "instance_id", h.cfg.InstanceID) + return nil +} diff --git a/internal/transport/http/server.go b/internal/transport/http/server.go index 5ed1ab9..e178c2a 100644 --- a/internal/transport/http/server.go +++ b/internal/transport/http/server.go @@ -16,16 +16,23 @@ type Server struct { addr string shutdownTimeout time.Duration logger *slog.Logger + drainHook DrainHook ready atomic.Bool } +// DrainHook 在服务摘除 readiness 后执行,例如注销注册中心节点。 +type DrainHook interface { + OnDeregister(context.Context) error +} + // New 创建探针 HTTP 服务。 -func New(appName string, addr string, shutdownTimeout time.Duration, logger *slog.Logger) *Server { +func New(appName string, addr string, shutdownTimeout time.Duration, logger *slog.Logger, drainHook DrainHook) *Server { server := &Server{ appName: appName, addr: addr, shutdownTimeout: shutdownTimeout, logger: logger, + drainHook: drainHook, } server.ready.Store(true) return server @@ -49,6 +56,11 @@ func (s *Server) Run(ctx context.Context) error { s.ready.Store(false) shutdownCtx, cancel := context.WithTimeout(context.Background(), s.shutdownTimeout) defer cancel() + if s.drainHook != nil { + if err := s.drainHook.OnDeregister(shutdownCtx); err != nil { + s.logger.Error("run deregister hook failed", "error", err) + } + } _ = httpServer.Shutdown(shutdownCtx) }()