2017-01-25 2 views
1

У меня есть сервер Go, который принимает данные от нескольких клиентов TCP, которые передают данные. Формат - это настраиваемый формат, и конечный разделитель может появляться в потоке байтов, поэтому он использует байтовую начинку, чтобы обойти эту проблему.Оптимизация сетевого кода

Я ищу горячие точки в своем коде, и это поднимет ОГРОМНУЮ, и я уверен, что ее можно было бы сделать более эффективной, но я не совсем уверен, как в данный момент предоставлены предоставленные функции Go.

Код ниже и pprof показывает, что точка доступа должна быть popPacketFromBuffer. Это смотрит на текущий буфер, после того как каждый байт получен и ищет endDelimiter на своем собственном. Если их 2 подряд подряд, то он находится внутри самого пакета.

Я посмотрел на использование ReadBytes() вместо ReadByte(), но похоже, что мне нужно указать разделитель, и я боюсь, что это отключит средний поток пакетов? А также в любом случае это будет более эффективно, чем то, что я делаю?

Внутри функции 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

Похоже, вы звоните popPacketFromBuffer() каждый раз, когда каждый байт получен из связи. Однако 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 
      } 
     } 
    } 

в popPacketFromBuffer() Может быть, вместо цикла только тестирование последние два байта

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

будет достаточно для цели.

+0

Спасибо, я думаю, что это имеет смысл, так что это даст. Вы имели в виду, что оба они были Лен (буфер) -1? –

+1

Извините, это опечатка – Uvelichitel

+0

Огромное спасибо. Работал очень хорошо. В конце я пошел для 'if ((tempBuffer [length-3]! = EndDelimiter) && (tempBuffer [length-2] == endDelimiter)) && (tempBuffer [length-1] == startDelimiter) {' так, чтобы он будет убедиться, что он был в конце пакета –