Http 死机:运行时错误:内存地址无效或对较大数据的零指针取消引用

Http 死机:运行时错误:内存地址无效或对较大数据的零指针取消引用,http,go,nullpointerexception,goroutine,predictionio,Http,Go,Nullpointerexception,Goroutine,Predictionio,我正在使用ApachePrediction IO开发一个推荐引擎。在使用事件服务器之前,我有一个GO api,用于侦听来自客户和导入器的事件。在特定情况下,当客户使用importer时,我收集导入的标识,并从importer api向GO api发送json。例如,如果用户导入一个包含45000个数据的csv,我会以类似于json的{“条形码”:[…]}的格式发送这些45000个identity to GO api。预测IO事件服务器需要特定形状的数据 type ItemEvent struct

我正在使用ApachePrediction IO开发一个推荐引擎。在使用事件服务器之前,我有一个GO api,用于侦听来自客户和导入器的事件。在特定情况下,当客户使用importer时,我收集导入的标识,并从importer api向GO api发送json。例如,如果用户导入一个包含45000个数据的csv,我会以类似于json的
{“条形码”:[…]}
的格式发送这些45000个identity to 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
是我从导入器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
是批量更新的处理函数。在这一步中,我应该提到prediction io的批量上传。通过RESTAPI预测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
函数封送传入的itemlist并向prediction io的事件服务器发出post请求。在这一部分中,当我尝试使用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)


对此有什么想法吗?

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

这意味着
err2
不是零,因此您应该看到该错误是什么

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


如果要继续使用同一个切片,可以封送该切片,然后将JSON消息传递给goroutine,而不是该切片。

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

很可能是以下for循环


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

请提及第141行的代码。我在代码中将这些行声明为注释,我想您错过了。谢谢,我照你说的做了。现在它给了我真正的原因,我猜。如果你想看一下,我编辑了这个问题。看起来这些请求的接收端有问题。检查该服务器的日志。可能您正在使用许多并发请求来重载它。
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
for index, barcode := range itemBulk.Barcodes{