Цепочка событий Swift NIO не завершается

#swift #swift-nio

#swift #swift-nio

Вопрос:

У меня есть очень простой сервис, построенный на приложении Vapor. Служба использует данные из другого семейства служб. Очевидно, что это именно то приложение map , для которого были созданы методы.

Все обратные вызовы в цепочке выполняются, но последний EventLoopFuture в цепочке никогда не завершается, в результате чего моя служба зависает на неопределенный срок.

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

Когда я вызываю этот метод, все обратные вызовы выполняются и ведут себя так, как ожидалось.

Обратите внимание, что в этом фрагменте кода я «развязал» этапы последовательности, чтобы попытаться пролить свет на то, где возникает проблема, но на общее поведение это не влияет.

Я пробовал много разных перестановок цепочки, используя flatMap , map , и flatMapError , где это уместно, без изменения конечного результата.

 let authService = remoteApi.authServices();

// EventLoopFuture<String> with sessionId
let sessionIdFuture = authService.getSessionId(username: userName, password: password)

// EventLoopFuture<Bool> with whether the user is in a particular group
let gsFuture = sessionIdFuture.flatMap { sessionId -> EventLoopFuture<Bool> in
    let groupMemberService = remoteApi.groupMemberServices()
    return groupMemberService.personIsInGroup(sessionId: sessionId, groupId: groupId, userId: userId)
}

// EventLoopFuture<Bool> if the above has an error, just return false
let errorFuture = gsFuture.flatMapErrorThrowing { error in
    // This executes successfully!
    return false
}

// EventLoopFuture<String> with the return data 
let returnFuture = errorFuture.flatMapThrowing { isMember -> String in
    // This executes successfully!
    let response = PersonIsMemberResponse(isMember: isMember)
    if let json = self.encodeResponse(response) {
        print(json) // {"isMember": false}
        return json
    } else {
        throw Abort(.internalServerError, reason: "could not encode our own dang data type");
    }
}.always { result in
    // This executes!
    do {
        try remoteApi.shutdown()
    } catch {
        print(error)
    }
}

gsFuture.whenComplete { result in
    // This executes!
    print("gsFuture complete!")
}
errorFuture.whenComplete { result in
     // This executes!
   print("errorFuture complete!")
}
returnFuture.whenComplete { result in
    // This DOES NOT execute!
    print("returnFuture complete!")
}
 

Я не вижу, как последнее flatMapThrowing может быть выполнено и вернуть значение, тогда будущее не завершено. Чего мне не хватает?

Комментарии:

1. Я только что попробовал этот код (со всеми отключенными вызовами служб), и он отлично работает для меня. Но давайте разберемся в этом: вы уверены, что try remoteApi.shutdown() это не блокирует? Вы могли бы, например, добавить a print("still here") после этой строки. Если он все еще работает, не могли бы вы описать, что remoteApi.shutdown() делает. Может быть, это закрытие EventLoopGroup или приложения Vapor или около того?

2. О, и это Linux или macOS? Если это macOS, вывод sample YourBinaryName while в зависшем состоянии также поможет.

3. Я комментирую здесь, потому что SO говорит мне, что это не очень хороший ответ, если вы «отвечаете» вопросами :).

4. Действительно, это блокирует. Похоже, что он вызывает syncShutdown() для AsyncHTTPClient . Я знаю, что он очень злится, если клиент отключается без вызова shutdown. Я думаю, мне нужно разработать правильный жизненный цикл для этой службы. (Это часть отдельной библиотеки.) В любом случае, спасибо! Если вы скопируете свой вопрос в качестве ответа, я могу поставить вам заветную галочку.

5. Круто! Идея заключается в том, что вы начинаете только с одной EventLoopGroup и повторно используете ее для всего: Vapor, AsyncHttpClient, вашего кода NIO и т. Д…

Ответ №1:

Как мы вместе выяснили в комментариях, похоже try remoteApi.shutdown() , что это блокировка, которая предотвращает дальнейшее развитие событий.