componentpkg

package
v0.0.9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 15, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

README

component

泛型懒加载组件容器、命名组件组和生命周期关闭管理,适合 Redis、MySQL、HTTP client、SDK client 等基础设施 manager 的延迟初始化与统一关闭。

目录名是 component,当前 Go 包名是 componentpkg。外部使用时建议显式设置 import alias。

单组件用法

Component[T] 会在首次 Get() 时调用 factory。初始化成功后复用同一个实例;初始化失败不会缓存结果,下次 Get() 会重新尝试。

package main

import (
	"fmt"
	"io"

	componentpkg "github.com/ikaiguang/go-kit/component"
)

type Client struct{}

func (c *Client) Close() error { return nil }

func main() {
	lc := &componentpkg.Lifecycle{}
	defer func() {
		if err := lc.Close(); err != nil {
			fmt.Println(err)
		}
	}()

	clientComponent := componentpkg.NewComponent("client", func() (*Client, error) {
		return &Client{}, nil
	}, lc)

	client, err := clientComponent.Get()
	if err != nil {
		panic(err)
	}
	_ = client

	var _ io.Closer = client
}

如果 T 实现了 io.Closer,组件初始化成功后会自动注册到 Lifecycle。调用 Lifecycle.Close() 时,组件会按注册逆序关闭。

命名组件组

ComponentGroup[T] 用于同一种组件存在多个命名实例的场景,例如多 Redis 实例、多数据库连接或多租户 SDK client。

lc := &componentpkg.Lifecycle{}
group := componentpkg.NewComponentGroup("client", func(name string) func() (*Client, error) {
	return func() (*Client, error) {
		return &Client{}, nil
	}
}, lc)

defaultClient, err := group.Get("default")
if err != nil {
	return err
}
_ = defaultClient

archiveClient, err := group.Get("archive")
if err != nil {
	return err
}
_ = archiveClient

同名实例只会创建一个 Component;不同名称会分别懒加载并注册到同一个生命周期管理器。

并发语义

  • 已初始化的 Component.Get() 通过原子读返回,热路径无锁。
  • 首次初始化使用互斥锁和双重检查,保证成功的 factory 只发布一次。
  • factory 返回错误时不缓存失败,调用方下次 Get() 会重新触发初始化。
  • ComponentGroup.Get(name) 对已存在命名实例使用读锁,多个 goroutine 可并发命中。
  • Lifecycle.Close() 会清空已注册 closer;通常只在进程退出或服务停止时调用一次。

注意事项

  • factory 内应完成连接参数校验、超时控制和必要的 ping/check。
  • factory 不应依赖未受控的全局状态;错误要原样返回给调用方处理。
  • Lifecycle 的零值可用,外部包直接使用 &componentpkg.Lifecycle{} 创建。
  • 如果组件需要关闭,请让返回值实现 io.Closer,否则不会自动注册到生命周期。
  • Close() 会尝试关闭所有已注册组件,并用 errors.Join 返回多个关闭错误。

验证

go test ./component

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Component

type Component[T any] struct {
	// contains filtered or unexported fields
}

Component 泛型懒加载组件容器 T 为底层 manager 类型(如 redisutil.RedisManager)

并发语义:

  • Get() 热路径完全无锁:通过 atomic.Pointer.Load 读取已初始化的值
  • 首次初始化通过 mu 互斥 + 双检,保证 factory 只成功执行一次
  • factory 失败不缓存,下次 Get() 会重新尝试(与原 sync.Once 重置语义一致)
  • 相较原 sync.Once+重置方案,修复了 factory 失败与并发 Get 之间的 data race 及可能返回 (零值, nil) 的正确性问题

func NewComponent

func NewComponent[T any](name string, factory func() (T, error), lc *Lifecycle) *Component[T]

NewComponent 创建组件容器

func (*Component[T]) Get

func (c *Component[T]) Get() (T, error)

Get 获取组件实例,首次调用时触发 factory 初始化 热路径(已初始化)无锁;首次初始化通过 mu 互斥;失败不缓存,下次重试

type ComponentGroup

type ComponentGroup[T any] struct {
	// contains filtered or unexported fields
}

ComponentGroup 管理同类型组件的多个命名实例

并发语义:

  • Get(name) 热路径使用 RWMutex 的读锁,多个 goroutine 可并发读已存在实例
  • 首次创建某个命名实例时升级为写锁,通过双检避免重复创建
  • 底层 Component.Get 仍保持无锁快速路径

func NewComponentGroup

func NewComponentGroup[T any](baseType string, factoryFn func(name string) func() (T, error), lc *Lifecycle) *ComponentGroup[T]

NewComponentGroup 创建命名实例容器

func (*ComponentGroup[T]) Get

func (g *ComponentGroup[T]) Get(name string) (T, error)

Get 获取指定名称的实例,首次调用时创建 Component 并懒加载

type Lifecycle

type Lifecycle struct {
	// contains filtered or unexported fields
}

Lifecycle 管理组件的生命周期,按注册逆序关闭

说明:Register 与 Close 在进程生命周期内调用次数很少(与基础设施数量同阶), 这里保留 sync.Mutex 是因为写路径简单、可读性高,lock-free 改造收益不大。

func (*Lifecycle) Close

func (l *Lifecycle) Close() error

Close 按注册逆序关闭所有组件

func (*Lifecycle) Register

func (l *Lifecycle) Register(name string, closer io.Closer)

Register 注册一个需要关闭的组件

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL