我有一个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
}
发布于 2017-01-26 02:39:21
看起来您每次从connection接收到单个字节时都会调用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
}
就足以达到目的。
https://stackoverflow.com/questions/41856794
复制相似问题