简体   繁体   English

如何在golang中处理和获取goroutines的响应

[英]How to Handle and get response from goroutines in golang

I have go routines called in getPosition, getStatus and rerouting for my drone currently I am invoking go GetStatus in my main function which this function has go func() which handles the event for streaming grpc and sse. 我已经在getPosition,getStatus和我的无人机重新路由中调用例程我正在调用我的main函数中的GetStatus,这个函数有func()处理流grpc和sse的事件。

currently this is my code and i tried 目前这是我的代码,我试过

func GetPositionContext(ctx context.Context, uav pb.UAVControllerClient, uavID *pb.UAVID, projectID string) {
    log.Printf("getPosition start")

    stream, err := uav.GetPosition(ctx)

    if err != nil {
        fmt.Printf("ERROR getPosition:%s", err.Error())
    }

    streamID, eventName := EventsSubscribe(projectID, uavID.Aircraft, "get_position")

    position := make(chan models.DronePosition)

    // 受信ループ開始
    go func() {
        fmt.Print("start getPosition loop")
        for {
            msg, err := stream.Recv() // msg UAVPosition
            if err == io.EOF {
                // read done.
                fmt.Print("start getPosition loop closed")
                close(position)
                return
            }
            if err != nil {
                log.Fatalf("Failed to receive getPosition : %v", err)
                close(position)
                return
            }
            // log.Printf("Position point[%s](%f, %f, %f) H:%f", uavID.Aircraft, msg.Latitude, msg.Longitude, msg.Altitude, msg.Heading)

            wayPoint := models.WaypointItem{
                Latitude:  msg.Latitude,
                Longitude: msg.Longitude,
                Altitude:  msg.Altitude,
                Heading:   msg.Heading,
            }

            dronePosition := models.DronePosition{
                Name:          uavID.Aircraft,
                ItemParameter: wayPoint,
            }

            // publish to eventgo
            publishNotif(dronePosition, streamID, eventName)
            return
        }
    }()

    startMsg := pb.UAVControllerPositionRequest{
        UavID:       uavID,
        Instruction: true,
        Interval:    2,
    }

    fmt.Print("send getPosition start")

    if err := stream.Send(&startMsg); err != nil {
        log.Fatalf("Failed to send getPosition: %v", err)
    }

    <-position

    stream.CloseSend()
    fmt.Print("end of getPosition")
}

this is the part where i call this function 这是我称之为此功能的部分

go utils.GetPosition(ctx, uavService, &uavID, projectID)

I want to get the return values from go func like if everything works fine in the grpc server and no problem i should return 200 success and 500 if something failed. 我想从go func获取返回值,就好像在grpc服务器中一切正常并且没有问题我应该返回200成功,如果出现故障则返回500。

for {
        time.Sleep(time.Duration(60) * time.Second)
    }

after the call i have this code which it should return every 10s a success 在通话结束后,我有这个代码,它应该每10秒返回一次成功

return c.JSON(500, failed or pass)

I expect something to return to the ui if go routines is success or failed when streaming part is working for the response not to be pending or else other api call will not work. 我期待一些东西返回到ui如果例程成功或失败当流式传输部分正在为响应而不是待处理或者其他api调用将不起作用。

If you want to know if there was an error when processing the go func() inside GetPositionContext then you could do something like below. 如果你想知道在GetPositionContext中处理go func()时是否有错误,那么你可以做类似下面的事情。 I modified all errors to bubble up rather than call log.Fatal. 我将所有错误修改为冒泡而不是调用log.Fatal。 On a side note it might be wise to defer stream.CloseSend() , but i'm missing context and it's outside the scope of the question. 在旁注中,推迟stream.CloseSend()可能是明智的,但我缺少上下文并且它超出了问题的范围。

I did my best to understand the question, but if I missed something, let me know! 我尽力理解这个问题,但如果我错过了什么,请告诉我!

The code has been modified to bubble up errors within GetPositionContext directly rather than call log.Fatal. 代码已被修改为直接冒出GetPositionContext中的错误而不是调用log.Fatal。 Additionally, if there is an error within the go func() it will get sent back to GetPositionContext through a channel and then returned to the caller. 此外,如果go func()中存在错误,它将通过通道发送回GetPositionContext,然后返回给调用者。

func GetPositionContext(ctx context.Context, uav pb.UAVControllerClient, uavID *pb.UAVID, projectID string) error {
    log.Printf("getPosition start")

    stream, err := uav.GetPosition(ctx)

    if err != nil {
        return fmt.Errorf("ERROR getPosition: %v", err.Error())
    }

    streamID, eventName := EventsSubscribe(projectID, uavID.Aircraft, "get_position")

    errC := make(chan error)

    // 受信ループ開始
    go func() {
        fmt.Print("start getPosition loop")
        for {
            msg, err := stream.Recv() // msg UAVPosition
            if err == io.EOF {
                // read done.
                fmt.Print("start getPosition loop closed")
                close(errC)
                return
            }
            if err != nil {
                errC <- fmt.Errorf("Failed to receive getPosition : %v", err)
                return
            }
            // log.Printf("Position point[%s](%f, %f, %f) H:%f", uavID.Aircraft, msg.Latitude, msg.Longitude, msg.Altitude, msg.Heading)

            wayPoint := models.WaypointItem{
                Latitude:  msg.Latitude,
                Longitude: msg.Longitude,
                Altitude:  msg.Altitude,
                Heading:   msg.Heading,
            }

            dronePosition := models.DronePosition{
                Name:          uavID.Aircraft,
                ItemParameter: wayPoint,
            }

            // publish to eventgo
            publishNotif(dronePosition, streamID, eventName)

            // Did you mean to return here? The for loop will only ever execute one time.
            // If you didn't mean to return, then remove this close
            close(errC)
            return
        }
    }()

    startMsg := pb.UAVControllerPositionRequest{
        UavID:       uavID,
        Instruction: true,
        Interval:    2,
    }

    fmt.Print("send getPosition start")

    if err := stream.Send(&startMsg); err != nil {
        return fmt.Errorf("Failed to send getPosition: %v", err)
    }

    err = <- errC

    stream.CloseSend()
    fmt.Print("end of getPosition")
    return err
}

If you want to call this function asynchronously, but still want to know if there was an error then you could do the following. 如果要异步调用此函数,但仍想知道是否存在错误,则可以执行以下操作。

errC := make(chan error)
go func() {
  errC <- GetPositionContext(..........)
}()
// Do some other stuff here
// Until you need the result
err := <- errC


// ... and Eventually when you want to return the success or failure as JSON
if err != nil {
  return c.JSON(500, failed)
}
return c.JSON(500, pass)

I would suggest setting up a channel to receive events from the go routine. 我建议设置一个频道来接收来自go例程的事件。 In the calling function you can listen for these events (which can contain any information you want) in a loop until the go routine closes itself, signigying failure. 在调用函数中,您可以在循环中侦听这些事件(可以包含您想要的任何信息),直到go例程自行关闭,表示失败。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM