ants

package module
v0.0.0-...-1fa3943 Latest Latest
Warning

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

Go to latest
Published: Nov 30, 2022 License: MIT Imports: 9 Imported by: 0

README

Go 语言的 goroutine 池


英文 | 中文

📖 简介

ants是一个高性能的 goroutine 池,实现了对大规模 goroutine 的调度管理、goroutine 复用,允许使用者在开发并发程序的时候限制 goroutine 数量,复用资源,达到更高效执行任务的效果。

🚀 功能:

  • 自动调度海量的 goroutines,复用 goroutines
  • 定期清理过期的 goroutines,进一步节省资源
  • 提供了大量有用的接口:任务提交、获取运行中的 goroutine 数量、动态调整 Pool 大小、释放 Pool、重启 Pool
  • 优雅处理 panic,防止程序崩溃
  • 资源复用,极大节省内存使用量;在大规模批量并发任务场景下比原生 goroutine 并发具有更高的性能
  • 非阻塞机制

⚔️ 目前测试通过的 Golang 版本:

Go 1.8.xGo 1.18.x 的所有版本。

💡 ants 是如何运行的

流程图

ants-flowchart-cn

动态图

🧰 安装

使用 ants v1 版本:

go get -u github.com/panjf2000/ants

使用 ants v2 版本 (开启 GO111MODULE=on):

go get -u github.com/panjf2000/ants/v2

🛠 使用

写 go 并发程序的时候如果程序会启动大量的 goroutine ,势必会消耗大量的系统资源(内存,CPU),通过使用 ants,可以实例化一个 goroutine 池,复用 goroutine ,节省资源,提升性能:

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
	"time"

	"github.com/panjf2000/ants/v2"
)

var sum int32

func myFunc(i interface{}) {
	n := i.(int32)
	atomic.AddInt32(&sum, n)
	fmt.Printf("run with %d\n", n)
}

func demoFunc() {
	time.Sleep(10 * time.Millisecond)
	fmt.Println("Hello World!")
}

func main() {
	defer ants.Release()

	runTimes := 1000

	// Use the common pool.
	var wg sync.WaitGroup
	syncCalculateSum := func() {
		demoFunc()
		wg.Done()
	}
	for i := 0; i < runTimes; i++ {
		wg.Add(1)
		_ = ants.Submit(syncCalculateSum)
	}
	wg.Wait()
	fmt.Printf("running goroutines: %d\n", ants.Running())
	fmt.Printf("finish all tasks.\n")

	// Use the pool with a function,
	// set 10 to the capacity of goroutine pool and 1 second for expired duration.
	p, _ := ants.NewPoolWithFunc(10, func(i interface{}) {
		myFunc(i)
		wg.Done()
	})
	defer p.Release()
	// Submit tasks one by one.
	for i := 0; i < runTimes; i++ {
		wg.Add(1)
		_ = p.Invoke(int32(i))
	}
	wg.Wait()
	fmt.Printf("running goroutines: %d\n", p.Running())
	fmt.Printf("finish all tasks, result is %d\n", sum)
}

Pool 配置

// Option represents the optional function.
type Option func(opts *Options)

// Options contains all options which will be applied when instantiating a ants pool.
type Options struct {
	// ExpiryDuration is a period for the scavenger goroutine to clean up those expired workers,
	// the scavenger scans all workers every `ExpiryDuration` and clean up those workers that haven't been
	// used for more than `ExpiryDuration`.
	ExpiryDuration time.Duration

	// PreAlloc indicates whether to make memory pre-allocation when initializing Pool.
	PreAlloc bool

	// Max number of goroutine blocking on pool.Submit.
	// 0 (default value) means no such limit.
	MaxBlockingTasks int

	// When Nonblocking is true, Pool.Submit will never be blocked.
	// ErrPoolOverload will be returned when Pool.Submit cannot be done at once.
	// When Nonblocking is true, MaxBlockingTasks is inoperative.
	Nonblocking bool

	// PanicHandler is used to handle panics from each worker goroutine.
	// if nil, panics will be thrown out again from worker goroutines.
	PanicHandler func(interface{})

	// Logger is the customized logger for logging info, if it is not set,
	// default standard logger from log package is used.
	Logger Logger
}

// WithOptions accepts the whole options config.
func WithOptions(options Options) Option {
	return func(opts *Options) {
		*opts = options
	}
}

// WithExpiryDuration sets up the interval time of cleaning up goroutines.
func WithExpiryDuration(expiryDuration time.Duration) Option {
	return func(opts *Options) {
		opts.ExpiryDuration = expiryDuration
	}
}

// WithPreAlloc indicates whether it should malloc for workers.
func WithPreAlloc(preAlloc bool) Option {
	return func(opts *Options) {
		opts.PreAlloc = preAlloc
	}
}

// WithMaxBlockingTasks sets up the maximum number of goroutines that are blocked when it reaches the capacity of pool.
func WithMaxBlockingTasks(maxBlockingTasks int) Option {
	return func(opts *Options) {
		opts.MaxBlockingTasks = maxBlockingTasks
	}
}

// WithNonblocking indicates that pool will return nil when there is no available workers.
func WithNonblocking(nonblocking bool) Option {
	return func(opts *Options) {
		opts.Nonblocking = nonblocking
	}
}

// WithPanicHandler sets up panic handler.
func WithPanicHandler(panicHandler func(interface{})) Option {
	return func(opts *Options) {
		opts.PanicHandler = panicHandler
	}
}

// WithLogger sets up a customized logger.
func WithLogger(logger Logger) Option {
	return func(opts *Options) {
		opts.Logger = logger
	}
}

通过在调用NewPool/NewPoolWithFunc之时使用各种 optional function,可以设置ants.Options中各个配置项的值,然后用它来定制化 goroutine pool.

自定义池

ants支持实例化使用者自己的一个 Pool ,指定具体的池容量;通过调用 NewPool 方法可以实例化一个新的带有指定容量的 Pool ,如下:

// Set 10000 the size of goroutine pool
p, _ := ants.NewPool(10000)

任务提交

提交任务通过调用 ants.Submit(func())方法:

ants.Submit(func(){})

动态调整 goroutine 池容量

需要动态调整 goroutine 池容量可以通过调用Tune(int)

pool.Tune(1000) // Tune its capacity to 1000
pool.Tune(100000) // Tune its capacity to 100000

该方法是线程安全的。

预先分配 goroutine 队列内存

ants允许你预先把整个池的容量分配内存, 这个功能可以在某些特定的场景下提高 goroutine 池的性能。比如, 有一个场景需要一个超大容量的池,而且每个 goroutine 里面的任务都是耗时任务,这种情况下,预先分配 goroutine 队列内存将会减少不必要的内存重新分配。

// ants will pre-malloc the whole capacity of pool when you invoke this function
p, _ := ants.NewPool(100000, ants.WithPreAlloc(true))

释放 Pool

pool.Release()

重启 Pool

// 只要调用 Reboot() 方法,就可以重新激活一个之前已经被销毁掉的池,并且投入使用。
pool.Reboot()

⚙️ 关于任务执行顺序

ants 并不保证提交的任务被执行的顺序,执行的顺序也不是和提交的顺序保持一致,因为在 ants 是并发地处理所有提交的任务,提交的任务会被分派到正在并发运行的 workers 上去,因此那些任务将会被并发且无序地被执行。

🧲 Benchmarks

上图中的前两个 benchmark 测试结果是基于100w 任务量的条件,剩下的几个是基于 1000w 任务量的测试结果,`ants` 的默认池容量是 5w。
  • BenchmarkGoroutine-4 代表原生 goroutine

  • BenchmarkPoolGroutine-4 代表使用 goroutine 池 ants

Benchmarks with Pool

这里为了模拟大规模 goroutine 的场景,两次测试的并发次数分别是 100w 和 1000w,前两个测试分别是执行 100w 个并发任务不使用 Pool 和使用了ants的 Goroutine Pool 的性能,后两个则是 1000w 个任务下的表现,可以直观的看出在执行速度和内存使用上,ants的 Pool 都占有明显的优势。100w 的任务量,使用ants,执行速度与原生 goroutine 相当甚至略快,但只实际使用了不到 5w 个 goroutine 完成了全部任务,且内存消耗仅为原生并发的 40%;而当任务量达到 1000w,优势则更加明显了:用了 70w 左右的 goroutine 完成全部任务,执行速度比原生 goroutine 提高了 100%,且内存消耗依旧保持在不使用 Pool 的 40% 左右。

Benchmarks with PoolWithFunc

因为PoolWithFunc这个 Pool 只绑定一个任务函数,也即所有任务都是运行同一个函数,所以相较于Pool对原生 goroutine 在执行速度和内存消耗的优势更大,上面的结果可以看出,执行速度可以达到原生 goroutine 的 300%,而内存消耗的优势已经达到了两位数的差距,原生 goroutine 的内存消耗达到了ants的35倍且原生 goroutine 的每次执行的内存分配次数也达到了ants45倍,1000w 的任务量,ants的初始分配容量是 5w,因此它完成了所有的任务依旧只使用了 5w 个 goroutine!事实上,ants的 Goroutine Pool 的容量是可以自定义的,也就是说使用者可以根据不同场景对这个参数进行调优直至达到最高性能。

吞吐量测试(适用于那种只管提交异步任务而无须关心结果的场景)

10w 任务量

100w 任务量

1000w 任务量

📊 性能小结

从该 demo 测试吞吐性能对比可以看出,使用ants的吞吐性能相较于原生 goroutine 可以保持在 2-6 倍的性能压制,而内存消耗则可以达到 10-20 倍的节省优势。

👏 贡献者

请在提 PR 之前仔细阅读 Contributing Guidelines,感谢那些为 ants 贡献过代码的开发者!

📄 证书

ants 的源码允许用户在遵循 MIT 开源证书 规则的前提下使用。

📚 相关文章

🖥 用户案例

商业公司

以下公司/组织在生产环境上使用了 ants

                

开源软件

  • gnet: A high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go.
  • nps: A lightweight, high-performance, powerful intranet penetration proxy server, with a powerful web management terminal.
  • milvus: An open-source vector database for scalable similarity search and AI applications.
  • osmedeus: A Workflow Engine for Offensive Security.
  • jitsu: An open-source Segment alternative. Fully-scriptable data ingestion engine for modern data teams. Set-up a real-time data pipeline in minutes, not days.
  • triangula: Generate high-quality triangulated and polygonal art from images.
  • bsc: A Binance Smart Chain client based on the go-ethereum fork.
  • jaeles: The Swiss Army knife for automated Web Application Testing.
  • devlake: The open-source dev data platform & dashboard for your DevOps tools.
所有案例:

如果你的项目也在使用 ants,欢迎给我提 Pull Request 来更新这份用户案例列表。

🔋 JetBrains 开源证书支持

ants 项目一直以来都是在 JetBrains 公司旗下的 GoLand 集成开发环境中进行开发,基于 free JetBrains Open Source license(s) 正版免费授权,在此表达我的谢意。

💰 支持

如果有意向,可以通过每个月定量的少许捐赠来支持这个项目。

💎 赞助

每月定量捐赠 10 刀即可成为本项目的赞助者,届时您的 logo 或者 link 可以展示在本项目的 README 上。

☕️ 打赏

当您通过以下方式进行捐赠时,请务必留下姓名、GitHub 账号或其他社交媒体账号,以便我将其添加到捐赠者名单中,以表谢意。

        

资助者

Patrick Othmer Jimmy ChenZhen Mai Yang 王开帅 Unger Alejandro Weng Wei

🔋 赞助商

## 参考文章-Gogods添加

Documentation

Index

Constants

View Source
const (
	// DefaultAntsPoolSize is the default capacity for a default goroutine pool.
	DefaultAntsPoolSize = math.MaxInt32

	// DefaultCleanIntervalTime is the interval time to clean up goroutines.
	DefaultCleanIntervalTime = time.Second
)
View Source
const (
	// OPENED represents that the pool is opened.
	OPENED = iota

	// CLOSED represents that the pool is closed.
	CLOSED
)

Variables

View Source
var (

	// ErrLackPoolFunc will be returned when invokers don't provide function for pool.
	ErrLackPoolFunc = errors.New("must provide function for pool")

	// ErrInvalidPoolExpiry will be returned when setting a negative number as the periodic duration to purge goroutines.
	ErrInvalidPoolExpiry = errors.New("invalid expiry for pool")

	// ErrPoolClosed will be returned when submitting task to a closed pool.
	ErrPoolClosed = errors.New("this pool has been closed")

	// ErrPoolOverload will be returned when the pool is full and no workers available.
	ErrPoolOverload = errors.New("too many goroutines blocked on submit or Nonblocking is set")

	// ErrInvalidPreAllocSize will be returned when trying to set up a negative capacity under PreAlloc mode.
	ErrInvalidPreAllocSize = errors.New("can not set up a negative capacity under PreAlloc mode")

	// ErrTimeout will be returned after the operations timed out.
	ErrTimeout = errors.New("operation timed out")
)

Functions

func Cap

func Cap() int

Cap returns the capacity of this default pool.

func Free

func Free() int

Free returns the available goroutines to work.

func NewSpinLock

func NewSpinLock() sync.Locker

NewSpinLock instantiates a spin-lock.

func Reboot

func Reboot()

Reboot reboots the default pool.

func Release

func Release()

Release Closes the default pool.

func Running

func Running() int

Running returns the number of the currently running goroutines.

func Submit

func Submit(task func()) error

Submit submits a task to pool.

Types

type Logger

type Logger interface {
	// Printf must have the same semantics as log.Printf.
	Printf(format string, args ...interface{})
}

Logger is used for logging formatted messages.

type Option

type Option func(opts *Options)

Option represents the optional function.

func WithDisablePurge

func WithDisablePurge(disable bool) Option

WithDisablePurge indicates whether we turn off automatically purge.

func WithExpiryDuration

func WithExpiryDuration(expiryDuration time.Duration) Option

WithExpiryDuration sets up the interval time of cleaning up goroutines.

func WithLogger

func WithLogger(logger Logger) Option

WithLogger sets up a customized logger.

func WithMaxBlockingTasks

func WithMaxBlockingTasks(maxBlockingTasks int) Option

WithMaxBlockingTasks sets up the maximum number of goroutines that are blocked when it reaches the capacity of pool.

func WithNonblocking

func WithNonblocking(nonblocking bool) Option

WithNonblocking indicates that pool will return nil when there is no available workers.

func WithOptions

func WithOptions(options Options) Option

WithOptions accepts the whole options config.

func WithPanicHandler

func WithPanicHandler(panicHandler func(interface{})) Option

WithPanicHandler sets up panic handler.

func WithPreAlloc

func WithPreAlloc(preAlloc bool) Option

WithPreAlloc indicates whether it should malloc for workers.

type Options

type Options struct {
	// ExpiryDuration is a period for the scavenger goroutine to clean up those expired workers,
	// the scavenger scans all workers every `ExpiryDuration` and clean up those workers that haven't been
	// used for more than `ExpiryDuration`.
	ExpiryDuration time.Duration

	// PreAlloc indicates whether to make memory pre-allocation when initializing Pool.
	PreAlloc bool

	// Max number of goroutine blocking on pool.Submit.
	// 0 (default value) means no such limit.
	MaxBlockingTasks int

	// When Nonblocking is true, Pool.Submit will never be blocked.
	// ErrPoolOverload will be returned when Pool.Submit cannot be done at once.
	// When Nonblocking is true, MaxBlockingTasks is inoperative.
	Nonblocking bool

	// PanicHandler is used to handle panics from each worker goroutine.
	// if nil, panics will be thrown out again from worker goroutines.
	PanicHandler func(interface{})

	// Logger is the customized logger for logging info, if it is not set,
	// default standard logger from log package is used.
	Logger Logger

	// When DisablePurge is true, workers are not purged and are resident.
	DisablePurge bool
}

Options contains all options which will be applied when instantiating an ants pool.

type Pool

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

Pool accepts the tasks from client, it limits the total of goroutines to a given number by recycling goroutines.

func NewPool

func NewPool(size int, options ...Option) (*Pool, error)

NewPool generates an instance of ants pool.

func (*Pool) Cap

func (p *Pool) Cap() int

Cap returns the capacity of this pool.

func (*Pool) Free

func (p *Pool) Free() int

Free returns the number of available goroutines to work, -1 indicates this pool is unlimited.

func (*Pool) IsClosed

func (p *Pool) IsClosed() bool

IsClosed indicates whether the pool is closed.

func (*Pool) Reboot

func (p *Pool) Reboot()

Reboot reboots a closed pool.

func (*Pool) Release

func (p *Pool) Release()

Release closes this pool and releases the worker queue.

func (*Pool) ReleaseTimeout

func (p *Pool) ReleaseTimeout(timeout time.Duration) error

ReleaseTimeout is like Release but with a timeout, it waits all workers to exit before timing out.

func (*Pool) Running

func (p *Pool) Running() int

Running returns the number of workers currently running.

func (*Pool) Submit

func (p *Pool) Submit(task func()) error

Submit submits a task to this pool.

Note that you are allowed to call Pool.Submit() from the current Pool.Submit(), but what calls for special attention is that you will get blocked with the latest Pool.Submit() call once the current Pool runs out of its capacity, and to avoid this, you should instantiate a Pool with ants.WithNonblocking(true).

func (*Pool) Tune

func (p *Pool) Tune(size int)

Tune changes the capacity of this pool, note that it is noneffective to the infinite or pre-allocation pool.

func (*Pool) Waiting

func (p *Pool) Waiting() int

Waiting returns the number of tasks which are waiting be executed.

type PoolWithFunc

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

PoolWithFunc accepts the tasks from client, it limits the total of goroutines to a given number by recycling goroutines.

func NewPoolWithFunc

func NewPoolWithFunc(size int, pf func(interface{}), options ...Option) (*PoolWithFunc, error)

NewPoolWithFunc generates an instance of ants pool with a specific function.

func (*PoolWithFunc) Cap

func (p *PoolWithFunc) Cap() int

Cap returns the capacity of this pool.

func (*PoolWithFunc) Free

func (p *PoolWithFunc) Free() int

Free returns the number of available goroutines to work, -1 indicates this pool is unlimited.

func (*PoolWithFunc) Invoke

func (p *PoolWithFunc) Invoke(args interface{}) error

Invoke submits a task to pool.

Note that you are allowed to call Pool.Invoke() from the current Pool.Invoke(), but what calls for special attention is that you will get blocked with the latest Pool.Invoke() call once the current Pool runs out of its capacity, and to avoid this, you should instantiate a PoolWithFunc with ants.WithNonblocking(true).

func (*PoolWithFunc) IsClosed

func (p *PoolWithFunc) IsClosed() bool

IsClosed indicates whether the pool is closed.

func (*PoolWithFunc) Reboot

func (p *PoolWithFunc) Reboot()

Reboot reboots a closed pool.

func (*PoolWithFunc) Release

func (p *PoolWithFunc) Release()

Release closes this pool and releases the worker queue.

func (*PoolWithFunc) ReleaseTimeout

func (p *PoolWithFunc) ReleaseTimeout(timeout time.Duration) error

ReleaseTimeout is like Release but with a timeout, it waits all workers to exit before timing out.

func (*PoolWithFunc) Running

func (p *PoolWithFunc) Running() int

Running returns the number of workers currently running.

func (*PoolWithFunc) Tune

func (p *PoolWithFunc) Tune(size int)

Tune changes the capacity of this pool, note that it is noneffective to the infinite or pre-allocation pool.

func (*PoolWithFunc) Waiting

func (p *PoolWithFunc) Waiting() int

Waiting returns the number of tasks which are waiting be executed.

Jump to

Keyboard shortcuts

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