Как обращаться и получать ответ от горутин в Голанге - PullRequest
3 голосов
/ 05 июня 2019

У меня есть подпрограммы go, вызываемые в getPosition, getStatus и перенаправления для моего дрона. В настоящее время я вызываю go GetStatus в моей основной функции, которая имеет функцию go func (), которая обрабатывает событие для потоковой передачи 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 success и 500, если что-то не получилось.

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

после звонка у меня есть этот код, который он должен возвращать каждые 10 секунд успеха

return c.JSON(500, failed or pass)

Я ожидаю, что что-то вернется к пользовательскому интерфейсу, если подпрограммы go будут успешными или неуспешными, когда потоковая часть работает, чтобы ответ не ожидался, иначе другой вызов API не будет работать.

Ответы [ 2 ]

1 голос
/ 05 июня 2019

Если вы хотите знать, была ли ошибка при обработке go func () внутри GetPositionContext, вы можете сделать что-то вроде ниже. Я изменил все ошибки, чтобы всплывать, а не вызывать 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)
0 голосов
/ 05 июня 2019

Я бы предложил настроить канал для приема событий из процедуры go.В вызывающей функции вы можете прослушивать эти события (которые могут содержать любую информацию, которую вы хотите) в цикле, пока процедура go не закроется сама, сигнализируя об ошибке.

...