我正在尝试了解Attack()
负载测试工具/库的源代码中的vegeta
函数(https://github.com/tsenart/vegeta/blob/44a49c878dd6f28f04b9b5ce5751490b0dce1e18/lib/attack.go#L253-L312)。我创建了一个简化的示例:
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go attack(&wg)
}
// wg.Wait()
go func() {
defer wg.Wait()
}()
}
func attack(wg *sync.WaitGroup) {
defer wg.Done()
time.Sleep(1 * time.Second)
fmt.Println("foobar")
}
我注意到的是,此函数立即返回而无需打印foobar
10次。只有看到wg.Wait()
行中的注释,foobar
在一秒钟后打印了10次。这对我来说很有意义,因为main()
函数在调用wg.Wait()
之前返回。
那么,我不了解Attack()
方法在vegeta
中是如何工作的,因为它似乎遵循类似的模式:
func (a *Attacker) Attack(tr Targeter,p Pacer,du time.Duration,name string) <-chan *Result {
var wg sync.WaitGroup
workers := a.workers
if workers > a.maxWorkers {
workers = a.maxWorkers
}
results := make(chan *Result)
ticks := make(chan struct{})
for i := uint64(0); i < workers; i++ {
wg.Add(1)
go a.attack(tr,name,&wg,ticks,results)
}
go func() {
defer close(results)
defer wg.Wait()
defer close(ticks)
began,count := time.Now(),uint64(0)
for {
elapsed := time.Since(began)
if du > 0 && elapsed > du {
return
}
wait,stop := p.Pace(elapsed,count)
if stop {
return
}
time.Sleep(wait)
if workers < a.maxWorkers {
select {
case ticks <- struct{}{}:
count++
continue
case <-a.stopch:
return
default:
// all workers are blocked. start one more and try again
workers++
wg.Add(1)
go a.attack(tr,results)
}
}
select {
case ticks <- struct{}{}:
count++
case <-a.stopch:
return
}
}
}()
return results
}
attack()
方法读取的地方
func (a *Attacker) attack(tr Targeter,name string,workers *sync.WaitGroup,ticks <-chan struct{},results chan<- *Result) {
defer workers.Done()
for range ticks {
results <- a.hit(tr,name)
}
}
我不明白为什么Attack()
函数不会在不调用attack()
的情况下立即返回,因为它的wg.Wait()
位于Goroutine中?