2017-12-15 28 views
4

golang tcp 서버를 구현하려고합니다. 동시성이 만족 스럽지만 CPU 사용량이 너무 높습니다 (동시성은 15W +/s이지만 CPU 사용량은 24 코어 Linux 머신에서 약 800 %입니다) . 동시에 C++ tcp 서버는 비슷한 동시성 (libevent 사용)으로 약 200 %의 사용률을 보이고 있습니다.golang tcp 서버에서 CPU 사용을 줄이려면 어떻게해야합니까?

다음 코드는 golang의 데모입니다 :

func main() { 
    listen, err := net.Listen("tcp", "0.0.0.0:17379") 
    if err != nil { 
     fmt.Errorf(err.Error()) 
    } 
    go acceptClient(listen) 
    var channel2 = make(chan bool) 
    <-channel2 
} 

func acceptClient(listen net.Listener) { 
    for { 
     sock, err := listen.Accept() 
     if err != nil { 
      fmt.Errorf(err.Error()) 
     } 
     tcp := sock.(*net.TCPConn) 
     tcp.SetNoDelay(true) 
     var channel = make(chan bool, 10) 
     go read(channel, sock.(*net.TCPConn)) 
     go write(channel, sock.(*net.TCPConn)) 
    } 
} 

func read(channel chan bool, sock *net.TCPConn) { 
    count := 0 
    for { 
     var buf = make([]byte, 1024) 
     n, err := sock.Read(buf) 
     if err != nil { 
      close(channel) 
      sock.CloseRead() 
      return 
     } 
     count += n 
     x := count/58 
     count = count % 58 
     for i := 0; i < x; i++ { 
      channel <- true 
     } 
    } 
} 

func write(channel chan bool, sock *net.TCPConn) { 
    buf := []byte("+OK\r\n") 
    defer func() { 
     sock.CloseWrite() 
     recover() 
    }() 
    for { 
     _, ok := <-channel 
     if !ok { 
      return 
     } 
     _, writeError := sock.Write(buf) 
     if writeError != nil { 
      return 
     } 
    } 
} 

그리고 멀티 클라이언트와 레디 스 벤치 마크하여 TCP 서버를 테스트 : 나는 또한에 의해 내 golang 코드를 분석

redis-benchmark -h 10.100.45.2 -p 17379 -n 1000 -q script load "redis.call('set','aaa','aaa')" 

pprof, 그것은 syscall에 많은 시간을 CPU 비용이라고합니다 : enter image description here

+1

아마도 코드 검토에 더 적합 할 것입니다. 몇 가지 생각 : 당신은 필요 이상으로 더 많은 채널을 사용하고 있으며, 소켓 유형 어설 션 –

+0

에 대한 메신저를 놓치고있다. (나는 단지 수면을 추가 할 것을 제안 할 수있다.). ml에 게시 하시겠습니까? https://groups.google.com/forum/#!forum/golang-nuts –

+0

io 용으로 시스템 콜에 소비되는 대부분의 시간은 예상 한 것입니다. 당신은 많은 쓰레기를 만들고 있습니다, 아마도 GC가 여분의 시간을 차지하고 있습니다. Go 프로그램은 아마도 더 많은 일을하고있을 것이기 때문에 총 CPU 사용량은 Go 프로그램에서 더 높을 것으로 예상되지만 처리량을 상당히 가깝게 얻을 수 있어야합니다. – JimB

답변

0

나는 병렬로 읽기 및 쓰기 채널 w 이 경우 더 나은 성능을 제공합니다. 적은 메모리 할당과 적은 시스템 콜을 시도해야합니다. (쓰기 기능은 많은 시스템 콜을 할 수 있습니다.)

이 버전을 사용해 볼 수 있습니까?

package main 

import (
    "bytes" 
    "fmt" 
    "net" 
) 

func main() { 
    listen, err := net.Listen("tcp", "0.0.0.0:17379") 
    if err != nil { 
     fmt.Errorf(err.Error()) 
    } 
    acceptClient(listen) 
} 

func acceptClient(listen net.Listener) { 
    for { 
     sock, err := listen.Accept() 
     if err != nil { 
      fmt.Errorf(err.Error()) 
     } 
     tcp := sock.(*net.TCPConn) 
     tcp.SetNoDelay(true) 
     go handleConn(tcp) // less go routine creation but no concurrent read/write on the same conn 
    } 
} 

var respPattern = []byte("+OK\r\n") 

// just one goroutine per conn 
func handleConn(sock *net.TCPConn) { 
    count := 0 
    buf := make([]byte, 4098) // Do not create a new buffer each time & increase the buff size 
    defer sock.Close() 

    for { 
     n, err := sock.Read(buf) 
     if err != nil { 
      return 
     } 
     count += n 
     x := count/58 
     count = count % 58 
     resp := bytes.Repeat(respPattern, x) // can be optimize 
     _, writeError := sock.Write(resp) // do less syscall 
     if writeError != nil { 
      return 
     } 
    } 
}