Go 어프어간트 소개
ants
는 대규모 고루틴의 스케줄링과 관리를 구현하여 리소스의 한계와 재사용을 가능하게 함으로써 동시 프로그램을 개발할 때 작업을 효율적으로 실행할 수 있도록 하는 고성능 고루틴 풀입니다.
특징
- 대규모 고루틴을 자동으로 스케줄링하고 재사용합니다.
- 만료된 고루틴을 정기적으로 정리하여 추가로 리소스를 절약합니다.
- 작업 제출, 실행 중인 고루틴 수 얻기, 풀 크기 동적 조정, 풀 해제, 풀 재시작과 같은 많은 유용한 인터페이스를 제공합니다.
- 프로그램 충돌을 방지하기 위해 패닉을 정상적으로 처리합니다.
- 리소스 재사용은 메모리 사용량을 크게 줄입니다. 대규모 일괄 동시 작업 시 기본 고루틴 동시성보다 높은 성능을 보여줍니다.
- 비차단 메커니즘
ants
작동 방식
플로우 차트
애니메이션 이미지들
설치
ants
v1 버전 사용:
go get -u github.com/panjf2000/ants
ants
v2 버전 사용 (GO111MODULE=on 활성화):
go get -u github.com/panjf2000/ants/v2
사용법
대량의 고루틴을 실행하는 Go 동시 프로그램을 작성할 때 시스템 리소스(메모리, CPU)를 상당량 소비하는데, ants
를 사용하면 고루틴을 재사용하는 고루틴 풀을 생성하여 리소스를 절약하고 성능을 향상시킬 수 있습니다.
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
// 공통 풀 사용.
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("실행 중인 고루틴 수: %d\n", ants.Running())
fmt.Printf("모든 작업 완료.\n")
// 함수가 있는 풀 사용,
// 고루틴 풀의 용량을 10으로, 만료 기간을 1초로 설정.
p, _ := ants.NewPoolWithFunc(10, func(i interface{}) {
myFunc(i)
wg.Done()
})
defer p.Release()
// 작업을 하나씩 제출.
for i := 0; i < runTimes; i++ {
wg.Add(1)
_ = p.Invoke(int32(i))
}
wg.Wait()
fmt.Printf("실행 중인 고루틴 수: %d\n", p.Running())
fmt.Printf("모든 작업 완료, 결과는 %d\n", sum)
}
풀 구성
// Option은 선택적인 함수를 나타냅니다.
type Option func(opts *Options)
// Options는 ants 풀을 인스턴스화할 때 적용될 모든 옵션을 포함합니다.
type Options struct {
// ExpiryDuration은 청소용 고루틴이 만료된 워커를 정리하는 기간입니다.
// 청소용 고루틴은 모든 `ExpiryDuration`마다 모든 워커를 스캔하고
// `ExpiryDuration` 이상 사용되지 않은 워커를 정리합니다.
ExpiryDuration time.Duration
// PreAlloc은 Pool을 초기화할 때 메모리를 사전 할당할지 여부를 나타냅니다.
PreAlloc bool
// pool.Submit에서 블로킹되는 고루틴의 최대 수입니다.
// 0(기본값)은 해당 제한이 없음을 의미합니다.
MaxBlockingTasks int
// Nonblocking이 true인 경우 Pool.Submit은 절대로 블로킹되지 않습니다.
// Pool.Submit이 한 번에 처리할 수 없는 경우 ErrPoolOverload가 반환됩니다.
// Nonblocking이 true인 경우 MaxBlockingTasks는 작동하지 않습니다.
Nonblocking bool
// PanicHandler는 각 워커 고루틴에서의 패닉을 처리하는 데 사용됩니다.
// nil인 경우 패닉은 다시 워커 고루틴에서 발생합니다.
PanicHandler func(interface{})
// Logger는 정보를 기록하는 사용자 정의 로거입니다. 설정되지 않은 경우
// log 패키지의 기본 표준 로거가 사용됩니다.
Logger Logger
}
// WithOptions는 전체 옵션 구성을 허용합니다.
func WithOptions(options Options) Option {
return func(opts *Options) {
*opts = options
}
}
// WithExpiryDuration은 고루틴 정리 간격 시간을 설정합니다.
func WithExpiryDuration(expiryDuration time.Duration) Option {
return func(opts *Options) {
opts.ExpiryDuration = expiryDuration
}
}
// WithPreAlloc은 워커를 위해 메모리를 사전 할당해야 하는지를 나타냅니다.
func WithPreAlloc(preAlloc bool) Option {
return func(opts *Options) {
opts.PreAlloc = preAlloc
}
}
// WithMaxBlockingTasks는 풀 용량에 도달하면 블로킹되는 고루틴의 최대 수를 설정합니다.
func WithMaxBlockingTasks(maxBlockingTasks int) Option {
return func(opts *Options) {
opts.MaxBlockingTasks = maxBlockingTasks
}
}
// WithNonblocking은 사용 가능한 워커가 없을 때 풀이 nil을 반환해야 하는지를 나타냅니다.
func WithNonblocking(nonblocking bool) Option {
return func(opts *Options) {
opts.Nonblocking = nonblocking
}
}
// WithPanicHandler는 패닉 핸들러를 설정합니다.
func WithPanicHandler(panicHandler func(interface{})) Option {
return func(opts *Options) {
opts.PanicHandler = panicHandler
}
}
// WithLogger는 사용자 정의 로거를 설정합니다.
func WithLogger(logger Logger) Option {
return func(opts *Options) {
opts.Logger = logger
}
}
NewPool
/NewPoolWithFunc
를 호출할 때 다양한 선택적 함수를 사용하여 ants.Options
의 각 구성 항목의 값을 설정할 수 있으며, 이후 고루틴 풀을 사용자 정의할 수 있습니다.
사용자 정의 풀
특정 풀 용량으로 사용자 고유의 Pool을 인스턴스화하는 데 ants
를 지원합니다. 다음과 같이 NewPool
메서드를 호출하여 지정된 용량으로 새로운 Pool을 인스턴스화할 수 있습니다.
p, _ := ants.NewPool(10000)
작업 제출
작업은 ants.Submit(func())
메서드를 호출하여 제출됩니다.
ants.Submit(func(){})
고루틴 풀 용량 동적 조정
고루틴 풀 용량을 동적으로 조정하려면 Tune(int)
메서드를 사용할 수 있습니다.
pool.Tune(1000) // 용량을 1000으로 조정
pool.Tune(100000) // 용량을 100000으로 조정
이 방법은 스레드로부터 안전합니다.
고루틴 큐 메모리 사전 할당
ants
를 사용하면 전체 풀에 메모리를 사전 할당할 수 있으며, 이는 특정 시나리오에서 고루틴 풀의 성능을 향상시킬 수 있습니다. 예를 들어, 매우 큰 용량이 필요하고 각 고루틴 내의 작업이 시간이 오래 걸리는 시나리오에서는 고루틴 큐에 대한 메모리 사전 할당이 불필요한 메모리 재할당을 줄일 수 있습니다.
// 이 함수를 호출하면 ants는 풀의 전체 용량을 미리 할당합니다.
p, _ := ants.NewPool(100000, ants.WithPreAlloc(true))
풀 해제
pool.Release()
풀 재시작
// Reboot() 메서드를 호출하여 이전에 파괴된 풀을 다시 활성화하고 다시 사용할 수 있습니다.
pool.Reboot()
작업 실행 순서에 관하여
ants
는 작업 실행 순서를 보장하지 않으며, 실행 순서는 제출 순서와 일치하지 않을 수 있습니다. 이는 ants
가 모든 제출된 작업을 동시에 처리하고, 작업이 동시에 실행 중인 워커에 할당되어 작업이 동시에 실행되어 결정론적이지 않은 순서로 실행되기 때문입니다.