如何在golang中处理goroutine并从中获得响应

时间:2019-06-05 02:56:55

标签: go grpc server-sent-events goroutine

我在goPosition,getStatus中调用了go例程,并为无人机重新路由。目前,我正在我的主要函数中调用go GetStatus,该函数具有gofunc()函数,该函数处理用于流式处理grpc和sse的事件。

当前这是我的代码,我尝试过

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")
}

这是我调用此功能的部分

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

我想从go func获得返回值,例如,如果一切在grpc服务器中都工作正常,没问题,我应该返回200成功,如果失败则返回500。

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

通话后,我有此代码,每成功发送一次代码应成功返回

return c.JSON(500, failed or pass)

如果流例程正在处理响应以使其未挂起,或者其他api调用无法正常工作,则go例程成功或失败时,我希望可以返回到ui。

2 个答案:

答案 0 :(得分:1)

如果您想知道在GetPositionContext内部处理go func()时是否出错,则可以执行以下操作。我修改了所有错误以冒出气泡,而不是调用log.Fatal。顺便提一句,推迟stream.CloseSend()可能是明智的选择,但是我缺少上下文,这不在问题的讨论范围之内。

我尽力理解这个问题,但是如果我错过了什么,请告诉我!

已对代码进行了修改,以直接使GetPositionContext中的错误冒泡,而不是调用log.Fatal。此外,如果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
}

如果要异步调用此函数,但仍想知道是否有错误,则可以执行以下操作。

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)

答案 1 :(得分:0)

我建议设置一个通道以接收来自go例程的事件。在调用函数中,您可以循环侦听这些事件(其中可以包含您想要的任何信息),直到go例程自身关闭(表明失败)为止。