golang通用对象池管理插件库go-commons-pool的使用

Go Commons Pool 使用指南

Go Commons Pool 是一个 Golang 的通用对象池管理库,直接从 Apache Commons Pool 重写而来。

特性

  1. 支持自定义 PooledObjectFactory
  2. 丰富的池配置选项,可以精确控制池化对象的生命周期
    • 池的 LIFO(后进先出)或 FIFO(先进先出)策略
    • 池容量配置
    • 池对象验证配置
    • 池对象借用阻塞和最大等待时间配置
    • 池对象驱逐配置
    • 池对象放弃配置

池配置选项

选项 默认值 描述
LIFO true 池是否为 LIFO(后进先出)
MaxTotal 8 池的容量上限
MaxIdle 8 池中最大空闲实例数
MinIdle 0 池中最小空闲实例数
TestOnCreate false 对象创建时是否验证
TestOnBorrow false 对象借出时是否验证
TestOnReturn false 对象归还时是否验证
TestWhileIdle false 对象空闲时是否验证
BlockWhenExhausted true 池耗尽时是否阻塞
MinEvictableIdleTime 30m 最小可驱逐空闲时间
SoftMinEvictableIdleTime math.MaxInt64 软最小可驱逐空闲时间
NumTestsPerEvictionRun 3 每次驱逐检查的对象数
TimeBetweenEvictionRuns 0 驱逐检查间隔时间(毫秒)

使用方法

使用简单工厂

import (
	"context"
	"fmt"
	"strconv"
	"sync/atomic"

	"github.com/jolestar/go-commons-pool/v2"
)

func Example_simple() {
	type myPoolObject struct {
		s string
	}

	v := uint64(0)
	factory := pool.NewPooledObjectFactorySimple(
		func(context.Context) (interface{}, error) {
			return &myPoolObject{
					s: strconv.FormatUint(atomic.AddUint64(&v, 1), 10),
				},
				nil
		})

	ctx := context.Background()
	p := pool.NewObjectPoolWithDefaultConfig(ctx, factory)

	obj, err := p.BorrowObject(ctx)
	if err != nil {
		panic(err)
	}

	o := obj.(*myPoolObject)
	fmt.Println(o.s)

	err = p.ReturnObject(ctx, obj)
	if err != nil {
		panic(err)
	}

	// Output: 1
}

使用自定义工厂

import (
	"context"
	"fmt"
	"strconv"
	"sync/atomic"

	"github.com/jolestar/go-commons-pool/v2"
)

type MyPoolObject struct {
	s string
}

type MyCustomFactory struct {
	v uint64
}

func (f *MyCustomFactory) MakeObject(ctx context.Context) (*pool.PooledObject, error) {
	return pool.NewPooledObject(
			&MyPoolObject{
				s: strconv.FormatUint(atomic.AddUint64(&f.v, 1), 10),
			}),
		nil
}

func (f *MyCustomFactory) DestroyObject(ctx context.Context, object *pool.PooledObject) error {
	// 销毁对象逻辑
	return nil
}

func (f *MyCustomFactory) ValidateObject(ctx context.Context, object *pool.PooledObject) bool {
	// 验证对象逻辑
	return true
}

func (f *MyCustomFactory) ActivateObject(ctx context.Context, object *pool.PooledObject) error {
	// 激活对象逻辑
	return nil
}

func (f *MyCustomFactory) PassivateObject(ctx context.Context, object *pool.PooledObject) error {
	// 钝化对象逻辑
	return nil
}

func Example_customFactory() {
	ctx := context.Background()
	p := pool.NewObjectPoolWithDefaultConfig(ctx, &MyCustomFactory{})
	p.Config.MaxTotal = 100
    
	obj1, err := p.BorrowObject(ctx)
	if err != nil {
		panic(err)
	}

	o := obj1.(*MyPoolObject)
	fmt.Println(o.s)

	err = p.ReturnObject(ctx, obj1)
	if err != nil {
		panic(err)
	}

	// Output: 1
}

注意事项

PooledObjectFactory.MakeObject 必须返回指针,而不是值。以下代码会报错:

p := pool.NewObjectPoolWithDefaultConfig(ctx, pool.NewPooledObjectFactorySimple(
    func(context.Context) (interface{}, error) {
        return "hello", nil
    }))
obj, _ := p.BorrowObject()
p.ReturnObject(obj)

正确的方式是:

p := pool.NewObjectPoolWithDefaultConfig(ctx, pool.NewPooledObjectFactorySimple(
    func(context.Context) (interface{}, error) {
        s := "hello"
        return &s, nil
    }))

性能测试

运行 pool_perf_test 的结果几乎与 Java 版本相同:

go test --perf=true

对于 Apache Commons Pool 用户

  • 直接使用 pool.Config.xxx 来更改池配置
  • 默认配置值与 Java 版本相同
  • 如果在创建 ObjectPool 后更改了 TimeBetweenEvictionRuns,应该调用 ObjectPool.StartEvictor 使其生效
  • 没有 KeyedObjectPool (TODO)
  • 没有 ProxiedObjectPool
  • 没有池统计信息 (TODO)

如何贡献

  1. 选择一个要解决的开放问题,如果没有则创建一个并描述您想要更改的内容
  2. 在 GitHub 上 fork 仓库
  3. 编写代码解决问题
  4. 创建 PR 并链接到问题
  5. 确保测试和覆盖率通过
  6. 等待维护者合并

许可证

Go Commons Pool 采用 Apache License, Version 2.0 许可证。


更多关于golang通用对象池管理插件库go-commons-pool的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang通用对象池管理插件库go-commons-pool的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


go-commons-pool 使用指南

go-commons-pool 是一个通用的 Golang 对象池管理库,类似于 Java 的 Apache Commons Pool。它提供了高效的对象池管理功能,适用于需要频繁创建和销毁对象的场景,如数据库连接池、网络连接池等。

基本概念

  1. ObjectPool: 对象池接口,定义了借出、归还对象等基本操作
  2. PooledObject: 池中管理的对象包装器
  3. PooledObjectFactory: 对象工厂接口,负责创建、销毁和验证对象

安装

go get github.com/jolestar/go-commons-pool/v2

基本使用示例

1. 定义池化对象

首先定义一个需要被池化的对象类型:

type MyObject struct {
    Name string
    // 其他字段...
}

2. 实现对象工厂

实现 PooledObjectFactory 接口:

type MyObjectFactory struct{}

func (f *MyObjectFactory) MakeObject(ctx context.Context) (*pool.PooledObject, error) {
    obj := &MyObject{
        Name: fmt.Sprintf("obj-%d", time.Now().UnixNano()),
    }
    return pool.NewPooledObject(obj), nil
}

func (f *MyObjectFactory) DestroyObject(ctx context.Context, object *pool.PooledObject) error {
    // 清理资源
    return nil
}

func (f *MyObjectFactory) ValidateObject(ctx context.Context, object *pool.PooledObject) bool {
    // 验证对象是否有效
    return true
}

func (f *MyObjectFactory) ActivateObject(ctx context.Context, object *pool.PooledObject) error {
    // 激活对象
    return nil
}

func (f *MyObjectFactory) PassivateObject(ctx context.Context, object *pool.PooledObject) error {
    // 钝化对象
    return nil
}

3. 创建对象池

func createObjectPool() *pool.ObjectPool {
    factory := &MyObjectFactory{}
    config := pool.NewDefaultPoolConfig()
    
    // 配置池参数
    config.MaxTotal = 10       // 最大对象数
    config.MaxIdle = 5         // 最大空闲对象数
    config.MinIdle = 1         // 最小空闲对象数
    config.BlockWhenExhausted = true  // 当池耗尽时是否阻塞
    config.MaxWaitMillis = 3000       // 最大等待毫秒数
    config.TestOnBorrow = true        // 借出时是否验证
    config.TestOnReturn = true        // 归还时是否验证
    config.TestWhileIdle = true       // 空闲时是否验证
    
    return pool.NewObjectPool(context.Background(), factory, config)
}

4. 使用对象池

func main() {
    objectPool := createObjectPool()
    defer objectPool.Close(context.Background())
    
    // 从池中借出对象
    obj, err := objectPool.BorrowObject(context.Background())
    if err != nil {
        log.Fatal(err)
    }
    
    // 类型断言获取实际对象
    myObj := obj.(*MyObject)
    fmt.Println("使用对象:", myObj.Name)
    
    // 使用完毕后归还对象
    err = objectPool.ReturnObject(context.Background(), obj)
    if err != nil {
        log.Fatal(err)
    }
    
    // 统计信息
    stats := objectPool.GetNumActive()
    fmt.Println("活跃对象数:", stats)
}

高级功能

1. 带超时的借出

ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()

obj, err := objectPool.BorrowObject(ctx)
if err != nil {
    if errors.Is(err, context.DeadlineExceeded) {
        fmt.Println("借出对象超时")
    } else {
        log.Fatal(err)
    }
}

2. 自动回收泄漏对象

config := pool.NewDefaultPoolConfig()
config.TimeBetweenEvictionRuns = 30 * time.Second  // 回收间隔
config.MinEvictableIdleTime = 10 * time.Minute    // 最小空闲时间

3. 自定义对象验证

func (f *MyObjectFactory) ValidateObject(ctx context.Context, object *pool.PooledObject) bool {
    obj := object.Object.(*MyObject)
    // 自定义验证逻辑
    return obj.Name != "" // 示例验证
}

最佳实践

  1. 合理配置池大小:根据系统资源和实际需求设置 MaxTotal 和 MaxIdle
  2. 及时归还对象:使用 defer 确保对象归还
  3. 处理对象失效:在 ValidateObject 中实现严格的验证逻辑
  4. 监控池状态:定期检查 NumActive、NumIdle 等统计信息
  5. 避免泄漏:确保所有借出的对象最终都被归还

完整示例

package main

import (
    "context"
    "fmt"
    "log"
    "time"
    
    "github.com/jolestar/go-commons-pool/v2"
)

type MyObject struct {
    ID   string
    Data string
}

type MyObjectFactory struct{}

func (f *MyObjectFactory) MakeObject(ctx context.Context) (*pool.PooledObject, error) {
    obj := &MyObject{
        ID:   fmt.Sprintf("obj-%d", time.Now().UnixNano()),
        Data: "initial data",
    }
    return pool.NewPooledObject(obj), nil
}

func (f *MyObjectFactory) DestroyObject(ctx context.Context, object *pool.PooledObject) error {
    // 清理资源
    return nil
}

func (f *MyObjectFactory) ValidateObject(ctx context.Context, object *pool.PooledObject) bool {
    obj := object.Object.(*MyObject)
    return obj.ID != "" && obj.Data != ""
}

func (f *MyObjectFactory) ActivateObject(ctx context.Context, object *pool.PooledObject) error {
    // 激活逻辑
    return nil
}

func (f *MyObjectFactory) PassivateObject(ctx context.Context, object *pool.PooledObject) error {
    // 钝化逻辑
    return nil
}

func main() {
    factory := &MyObjectFactory{}
    config := pool.NewDefaultPoolConfig()
    config.MaxTotal = 5
    config.MaxIdle = 3
    config.MinIdle = 1
    
    pool := pool.NewObjectPool(context.Background(), factory, config)
    defer pool.Close(context.Background())
    
    // 使用对象
    for i := 0; i < 10; i++ {
        go func(i int) {
            obj, err := pool.BorrowObject(context.Background())
            if err != nil {
                log.Printf("协程 %d 借出失败: %v", i, err)
                return
            }
            
            myObj := obj.(*MyObject)
            log.Printf("协程 %d 使用对象 %s", i, myObj.ID)
            
            // 模拟工作
            time.Sleep(time.Duration(i%3+1) * time.Second)
            
            err = pool.ReturnObject(context.Background(), obj)
            if err != nil {
                log.Printf("协程 %d 归还失败: %v", i, err)
            }
        }(i)
    }
    
    time.Sleep(5 * time.Second)
    log.Printf("最终统计 - 活跃: %d, 空闲: %d", 
        pool.GetNumActive(), pool.GetNumIdle())
}

go-commons-pool 提供了灵活且强大的对象池管理功能,通过合理配置可以显著提高应用程序性能,特别是在高并发场景下需要频繁创建销毁对象的场景。

回到顶部