This commit is contained in:
ZuoZuo 2026-04-07 12:30:14 +08:00
parent 90aa607c9b
commit 1d03869da8
9 changed files with 118 additions and 6 deletions

View File

@ -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

View File

@ -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

View File

@ -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

4
go.mod
View File

@ -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

2
go.sum
View File

@ -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=

View File

@ -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)

View File

@ -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
}

View File

@ -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 &registryHooks{
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
}

View File

@ -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)
}()