go언어에서 tcp server/client 의 전형적 형태. - 라이브러리화.
By SeukWon Kang
이미 두곳을 통해서 부분부분은 소개 했었지만
http://kasw.blogspot.kr/2015/02/go-goroutine.html http://kasw.blogspot.kr/2015/02/goguelike.html
기존 netspeed 테스트 프로그램과 goguelike에서 사용하고 있던 tcp server/client 구조를 본격적으로 재사용 가능하게 수정한 것입니다. ( 그래봐야 간단합니다. ^^)
tcp책에 나오는 전형적인 tcp server의 구조는 대부분의 언어에서 새 process / thread를 실행하는 비용이 너무나도 큰 관계로 이론적으로 설명하는 용도로만 사용하고 실제 서비스를 만드는 데에는 사용할수 없습니다.
다만 go언어에서는 go routine이 존재 하기때문에 이 “전형적"인 구조가 실용가능하게 됩니다. ( 꼭 go 언어가 아니라도 python stackless, greenlet , 또는 이와 유사한 micro thread를 지원 하는 모든 언어에서 가능합니다만 이를 지원 하는 대중적 언어가 별로 없지요. )
아래와 같이 책에서 보던 구조를 그대로 go 언어로 옮긴 구조 입니다. 테스트 해보면 10k connection까지는 성능저하 없이 실용 가능합니다. ( 그 이상은 테스트 해보지 않았습니다만 보통 그 정도면 server logic을 실행하는 것만으로도 버겁기 마련이지요. )
덤으로 channel을 queue로 사용해서 concurrent connection count control 을 하거나 새 connection을 연결하기 전에 time.Sleep을 통해 connection 폭주(throttling ) 를 제어하거나하는 기능도 아주 간단히 구현 가능했습니다.
패키지 내에 같이 있는 gogueconn은 c/s packet을 여러가지로 바꿔 가며 써보기 위해 있는 중간 layer입니다. 현재는 gob와 json 만을 지원하지만 나중에 추가하게 되더라도 다른쪽 소스의 수정을 최소화 할 수 있겠지요.
//start
package gogueserver
import (
"net"
"time"
"github.com/kasworld/log"
"github.com/kasworld/netlib/gogueconn"
)
type ServerGoFn func(conn *gogueconn.GogueConn, clientQueue <-chan bool)
func TCPServer(listenString string, connNum int, connThrottle int, runConn ServerGoFn) {
log.Info("Start server %v", listenString)
// concurrent connection count control
clientQueue := make(chan bool, connNum)
listener, err := net.Listen("tcp", listenString)
if err != nil {
log.Error("%v", err)
return
}
defer listener.Close()
for {
time.Sleep(time.Duration(connThrottle) * time.Millisecond)
clientQueue <- true
conn, err := listener.Accept()
if err != nil {
log.Error("%v", err)
} else {
go runConn(gogueconn.New(conn), clientQueue)
}
}
}
//end
github위치는 https://github.com/kasworld/netlib 입니다. 기존의 netspeed package는 더이상 사용되지 않습니다.