go 언어에서 동시에 실행되는 goroutine의 수를 제어 하기.
By SeukWon Kang
goguelike용 loadtester를 만들면서 사용하는 방법입니다.
loadtester는 일정시간동안 서버에 클라이언트로 접속해서 부하를 주는 프로그램으로 부하를 주는 시간과 동시에 몇개의 connection을 사용할것인지를 인자로 받습니다. 서버 입장에서는 하나 하나가 실제 user처럼 보이게 되는 것이 중요하고, 따라서 loadtester는 UI가 없는 dummy client와 client side AI를 사용하게 됩니다. 그리고 또 중요한 것이 부하를 거는 시간 내내 접속을 유지 하는 것이 아니라 각 dummy client를 무작위 시간동안 작동한후 connection을 종료 합니다. 그러면 loadtester는 이를 감지한후 새 dummy client를 실행 접속해야 합니다.
이과정을 통해서 서버에게 가능한 실제상황과 유사한 부하를 걸어주게 됩니다.
go언어에는 goroutine이라고하는 멋진 기능이 있으니 loadtester는 각 dummy client를 goroutine으로 만들어 실행하면 되고, 문제는 총 실행중인 dummy clinet(== goroutine ) 을 어떻게 제한 할것인가 하는 점입니다.
가능한 방법은 여러가지가 있을텐데 가장 간단해 보여서 선택한 방법은 channel을 queue 처럼 사용하는 것입니다.
//start
func loadtester(connectTo string, connectioncount int, runsecond int) {
rnd := rand.GetNewRand()
runtime.GOMAXPROCS(runtime.NumCPU())
endch := make(chan bool, connectioncount)
go func() {
for i := 0; i < connectioncount; i++ {
endch <- true // push queue, blocked here
go Client(connectTo, fmt.Sprintf("test%v", i), rnd.Intn(runsecond), endch)
time.Sleep(1 * time.Millisecond)
}
for i := connectioncount; ; i++ {
endch <- true // push queue, blocked here
go Client(connectTo, fmt.Sprintf("test%v", i), rnd.Intn(runsecond), endch)
}
}()
time.Sleep(time.Duration(runsecond) * time.Second)
}
func Client(connectTo string, name string, dur int, endch chan bool) {
defer func() {
<-endch // pop queue
}()
// run dummy client
time.Sleep(time.Duration(dur) * time.Second)
}
//end
핵심은 동시 실행 수 만큼 버퍼 크기를 가지는 channel 을 만들고 이를 queue 처럼 사용하는 겁니다. 그리고 dummyclient 를 실행하기 전에 channel에 send를 하고 dummy client 를 종료하면서 recv를 하면 버퍼가 가득한 상태에선 send가 wait 되니 간단하게 원하는 결과를 얻을수 있습니다. 위 코드 역시 goguelike코드의 일부로 그대로는 실행되지 않습니다. ( import 부분도 빠져 있고. )