【问题标题】:panic: runtime error: invalid memory address or nil pointer dereference with bigger data恐慌:运行时错误:无效的内存地址或更大数据的空指针取消引用
【发布时间】:2020-08-03 22:48:03
【问题描述】:

我正在使用 Apache Prediction IO 开发推荐引擎。在事件服务器之前,我有一个 GO api,用于侦听来自客户和进口商的事件。在客户使用导入器的特定情况下,我收集导入的身份,并将 json 从导入器 api 发送到 GO api。例如,如果用户导入一个包含 45000 个数据的 csv,我会将这 45000 个身份发送到像 {"barcodes":[...]} 这样的 json 格式的 GO api。预测 IO 事件服务器需要特定形状的数据。

type ItemEvent struct {
    Event      string              `json:"event"`
    EntityType string              `json:"entityType"`
    EntityId   string              `json:"entityId"`
    Properties map[string][]string `json:"properties"`
    EventTime  time.Time           `json:"eventTime"`
}


type ItemBulkEvent struct {
    Event     string    `json:"event"`
    Barcodes []string  `json:"barcodes"`
    EventTime time.Time `json:"eventTime"`
}

ItemEvent 是我将从 GO Api 发送到事件服务器的最终数据。 ItemBulkEvent 是我从 importer api 收到的数据。

func HandleItemBulkEvent(w http.ResponseWriter, r *http.Request) {
    var itemBulk model.ItemBulkEvent
    err := decode(r,&itemBulk)
    if err != nil {
        log.Fatalln("handleitembulkevent -> ",err)
        util.RespondWithError(w,400,err.Error())
    }else {
        var item model.ItemEvent
        item.EventTime = itemBulk.EventTime; item.EntityType = "item"; item.Event = itemBulk.Event
        itemList := make([]model.ItemEvent,0,50)
        for index, barcode := range itemBulk.Barcodes{
            item.EntityId = barcode
            if (index > 0 && (index % 49) == 0){
                itemList = append(itemList, item)
                go sendBulkItemToEventServer(w,r,itemList)
                itemList = itemList[:0]
            }else if index == len(itemBulk.Barcodes) - 1{
                itemList = append(itemList, item)
                itemList = itemList[:( (len(itemBulk.Barcodes) - 1) % 49)]
                go sendBulkItemToEventServer(w,r,itemList) // line 116 
                itemList = itemList[:0]
            } else{
                itemList = append(itemList, item)
            }
        }
        util.RespondWithJSON(w,200,"OK")
    }
}

HandleItemBulkEvent 是批量更新的处理函数。在这一步中,我应该提到预测 io 的批量上传。通过 rest api 预测 io 事件服务器每个请求需要 50 个事件。所以我创建了一个包含 50 个上限和一个项目的列表。我使用相同的项目,只是每次都更改身份部分(条形码)并添加到列表中。在每 50 个项目中,我使用了一个处理函数,将该列表发送到事件服务器,然后清理列表,依此类推。

func sendBulkItemToEventServer(w http.ResponseWriter, r *http.Request, itemList []model.ItemEvent){
    jsonedItem,err := json.Marshal(itemList)
    if err != nil{
        log.Fatalln("err marshalling -> ",err.Error())
    }
    // todo: change url to event server url
    resp, err2 := http.Post(fmt.Sprintf("http://localhost:7070/batch/events.json?accessKey=%s",
        r.Header.Get("Authorization")),
        "application/json",
        bytes.NewBuffer(jsonedItem))
    if err2 != nil{
        log.Fatalln("err http -> " , err.Error()) // line 141
    }
    defer resp.Body.Close()
}

sendBulkItemToEventServer 函数编组传入的项目列表并向预测 io 的事件服务器发出一个发布请求。在这部分中,当我尝试使用 5000+- 项目时效果很好,但是当我尝试使用 45000 项目时,应用程序崩溃并出现以下错误。

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xc05938]

goroutine 620 [running]:
api-test/service.sendBulkItemToEventServer(0x1187860, 0xc00028e0e0, 0xc00029c200, 0xc00011c000, 0x31, 0x32)
        /home/kadirakinkorkunc/Desktop/playground/recommendation-engine/pio_api/service/CollectorService.go:141 +0x468
created by api-test/service.HandleItemBulkEvent
        /home/kadirakinkorkunc/Desktop/playground/recommendation-engine/pio_api/service/CollectorService.go:116 +0x681

Debugger finished with exit code 0

知道如何解决这个问题吗?

编辑:正如 Burak Serdar 在答案中提到的,我通过在发送前使用编组解决了 err、err2 混淆和数据竞争问题。现在它给了我真正的错误(res,err2)我猜。

2020/08/03 15:11:55 err http ->  Post "http://localhost:7070/batch/events.json?accessKey=FJbGODbGzxD-CoLTdwTN2vwsuEEBJEZc4efrSUc6ekV1qUYAWPu5anDTyMGDoNq1": read tcp 127.0.0.1:54476->127.0.0.1:7070: read: connection reset by peer

对此有什么想法吗?

【问题讨论】:

  • 请注明第 141 行的代码。
  • 我在代码中将这些行声明为注释,我猜你错过了。

标签: http go nullpointerexception goroutine predictionio


【解决方案1】:

您的程序中有几个错误。运行时错误是因为您正在检查 err2 是否不为零,但是您正在打印 err,而不是 err2err 为 nil,因此运行时错误。

这意味着err2 不是零,所以你应该看看那个错误是什么。

您提到您正在分批发送 50 条消息,但这种实现是错误的。你向itemList 添加元素,然后用itemList 启动一个goroutine,然后截断它并再次开始填充。这是一场数据竞赛,你的 goroutine 将看到处理程序正在修改的 itemList 实例。无需截断,只需在向 goroutine 提交 itemList 时创建一个新的 itemList,这样每个 goroutine 都可以拥有自己的副本。

如果你想继续使用同一个切片,你可以编组切片,然后将 JSON 消息传递给 goroutine 而不是切片。

【讨论】:

  • 谢谢,我照你说的做了。现在它给了我我猜的真正原因。如果您想看一下,我编辑了问题。
  • 这些请求的接收端似乎有问题。检查该服务器的日志。也许您正在用许多并发请求使其超载。
【解决方案2】:

您收到的错误是您向其发出请求的服务器发送的错误。查看this 以了解有关该错误的更多信息。

很可能是下面的for循环

for index, barcode := range itemBulk.Barcodes{

迭代次数过多,并且由于您使用单独的 go 例程来创建请求,所有请求同时发生,这会导致服务器过载或故意关闭连接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-04-23
    • 2015-02-24
    • 2022-01-05
    • 1970-01-01
    • 2023-01-11
    • 2019-09-12
    • 1970-01-01
    • 2016-12-12
    相关资源
    最近更新 更多