【发布时间】:2021-12-25 11:39:23
【问题描述】:
我们在使用 grpc 流式传输交易的 golang 函数中存在可疑的高 CPU 使用率。功能很简单,当我们从前端收到ORDER ID数据变化的请求,然后我们消费并流回来。
这里是代码
func (consumer OrderChangesConsumer) Serve(message string) {
response := messages.OrderChanges{}
if err := json.Unmarshal([]byte(message), &response); err != nil {
logData := map[string]interface{}{
"message": message,
}
seelog.Error(commonServices.GenerateLog("parse_message_error", err.Error(), &logData))
}
if response.OrderID > 0 {
services.PublishChanges(response.OrderID, &response)
}
}
// PublishChanges sends the order change message to the changes channel.
func PublishChanges(orderID int, orderChanges *messages.OrderChanges) {
orderMutex.RLock()
defer orderMutex.RUnlock()
orderChan, ok := orderChans[orderID]
if !ok {
return
}
orderChan <- orderChanges
}
我们如何改进和测试这个案例的最佳实践?
【问题讨论】:
-
写信给
orderChan的时候,是不是一定要持有锁?如果您在测试ok之前明确解锁,会更顺利吗?通道写入可能会阻塞,从而使持有锁的代码保持原样。 -
一些想法:Serve 不是指针接收器方法,因此每次调用时都会复制 OrderChangesConsumer。消息在转换为 []byte 时也会被复制,因此如果消息很大,您将花费大量时间来复制它们。您还应该在进入频道之前解锁您的互斥锁。
-
@jdizzle Go 是复制值的 COW,所以我认为这不会看到复制命中?我还认为编译器在 []byte 到 string 的转换方面更聪明一些,但不太确定。
-
检查,编译器对字节复制并不那么聪明,这很可能调用 runtime.slicebytetostring,它实际上只是复制字节。我会说,由于在 json 解析中逐字节读取生成的字节切片,因此字节切片副本的性能损失可能远低于 json 解组的开销。