2017-01-25 2 views
1

私は、データをストリーミングする多数のTCPクライアントから入力を受け取るGoサーバーを持っています。フォーマットはカスタムフォーマットであり、終了デリミタはバイトストリーム内に現れるので、バイトを使ってこの問題を回避します。ネットワークコードの最適化

私は自分のコードでホットスポットを探していますが、これは巨大なものを投げ、より効率的にすることができると確信していますが、

コードは以下の通りです.pprofは、ホットスポットをpopPacketFromBufferコマンドにしています。これは、各バイトが受信された後、現在のバッファを調べ、自身のendDelimiterを探します。それらの2つが連続している場合、それはパケット自体の中にあります。

ReadByte()の代わりにReadBytes()を使用しましたが、デリミタを指定する必要があるように見えますが、これはストリームの途中でパケットがカットされる恐れがありますか?どのような場合でも、これは私がやっていることよりも効率的でしょうか?

popPacketFromBufferの機能の中では、機能はホットスポットであるforループです。

アイデア?

// Read client data from channel 
func (c *Client) listen() { 

    reader := bufio.NewReader(c.conn) 

    clientBuffer := new(bytes.Buffer) 

    for { 
     c.conn.SetDeadline(time.Now().Add(c.timeoutDuration)) 

     byte, err := reader.ReadByte() 

     if err != nil { 
      c.conn.Close() 
      c.server.onClientConnectionClosed(c, err) 
      return 
     } 

     wrErr := clientBuffer.WriteByte(byte) 
     if wrErr != nil { 
      log.Println("Write Error:", wrErr) 
     } 

     packet := popPacketFromBuffer(clientBuffer) 
     if packet != nil { 
      c.receiveMutex.Lock() 
      packetSize := uint64(len(packet)) 
      c.bytesReceived += packetSize 
      c.receiveMutex.Unlock() 

      packetBuffer := bytes.NewBuffer(packet) 

      b, err := uncompress(packetBuffer.Bytes()) 
      if err != nil { 
       log.Println("Unzip Error:", err) 
      } else { 
       c.server.onNewMessage(c, b) 
      } 
     } 

    } 
} 

func popPacketFromBuffer(buffer *bytes.Buffer) []byte { 

    bufferLength := buffer.Len() 

    if bufferLength >= 125000 { // 1MB in bytes is roughly this 
     log.Println("Buffer is too large ", bufferLength) 
     buffer.Reset() 
     return nil 
    } 

    tempBuffer := buffer.Bytes() 
    length := len(tempBuffer) 

    // Return on zero length buffer submission 
    if length == 0 { 
     return nil 
    } 

    endOfPacket := -1 

    // Determine the endOfPacket position by looking for an instance of our delimiter 
    for i := 0; i < length-1; i++ { 
     if tempBuffer[i] == endDelimiter { 
      if tempBuffer[i+1] == endDelimiter { 
       i++ 
      } else { 
       // We found a single delimiter, so consider this the end of a packet 
       endOfPacket = i - 2 
       break 
      } 
     } 
    } 

    if endOfPacket != -1 { 
     // Grab the contents of the provided packet 
     extractedPacket := buffer.Bytes() 

     // Extract the last byte as we were super greedy with the read operation to check for stuffing 
     carryByte := extractedPacket[len(extractedPacket)-1] 

     // Clear the main buffer now we have extracted a packet from it 
     buffer.Reset() 

     // Add the carryByte over to our new buffer 
     buffer.WriteByte(carryByte) 

     // Ensure packet begins with a valid startDelimiter 
     if extractedPacket[0] != startDelimiter { 
      log.Println("Popped a packet without a valid start delimiter") 
      return nil 
     } 

     // Remove the start and end caps 
     slice := extractedPacket[1 : len(extractedPacket)-2] 

     return deStuffPacket(slice) 
    } 

    return nil 
} 

答えて

2

接続から受信した1バイトごとにpopPacketFromBuffer()と呼びます。しかし、popPacketFromBuffer()ホールバッファをコピーし、各バイトごとにデリメータを検査します。多分、これは圧倒的です。私にとってあなただけの目的のために十分であろう最後の2バイト

if (buffer[len(buffer)-2] == endDelimiter) && (buffer[len(buffer)-1] != endDelimiter){ 
    //It's a packet 
} 

をテストループ

popPacketFromBuffer()
for i := 0; i < length-1; i++ { 
     if tempBuffer[i] == endDelimiter { 
      if tempBuffer[i+1] == endDelimiter { 
       i++ 
      } else { 
       // We found a single delimiter, so consider this the end of a packet 
       endOfPacket = i - 2 
       break 
      } 
     } 
    } 

たぶん代わりのループを必要としません。

+0

ありがとう、私はそれが意味を成していると思うので、それを行ってください。あなたは両方がLen(バッファ)-1であることを意味しましたか? –

+1

すみません、それはタイプミスです – Uvelichitel

+0

大変ありがとうございます。本当にうまくいった。最後に、私は 'if((tempBuffer [length-3]!= endDelimiter)&&(tempBuffer [length-2] == endDelimiter))&&(tempBuffer [length-1] == startDelimiter){'それをパケットの最後にあることを確認します –