Reactivecocoa: comment fournir une contre-pression (producteur de signal paresseux qui récupère plus de données au fur et à mesure que les données sont consommées)

Créé le 20 avr. 2016  ·  6Commentaires  ·  Source: ReactiveCocoa/ReactiveCocoa

J'essaie de comprendre comment créer un SignalProducer qui récupérera à plusieurs reprises le prochain morceau de données lorsque tout le flux aura été consommé.
J'ai une implémentation, mais le mécanisme de contre-pression ne fonctionne pas si observeOn est appelé sur le producteur. Ce qui semble se produire, c'est que le producteur continue de récupérer les nouvelles données même si le client n'a toujours pas consommé l'intégralité du flux.

Une idée de comment je pourrais réaliser ce que je veux? Est-ce vraiment possible?

Merci!

public func streamMessages(from startOffset: Offset = Offset(value: 0), toExclusive endOffsetOpt : Offset? = .None,
                               includeTransient: Bool = true) -> SignalProducer<Message, NoError> {

        func streamMessagesChunk(from: Offset) -> SignalProducer<Message, NoError> {
            func waitForNewMessageAvailable(from: Offset) -> SignalProducer<Offset?, NoError> {
                return self.lastOffsetIncludingTransient(includeTransient).producer
                    .filter{ offsetOpt in offsetOpt.map {offset in offset >= from } ?? false }
                    .take(1)
            }

            let streamMessagesProducer = self.fetchMessages(10, from: from, includeTransientMessages: includeTransient)
                .flatMap(.Concat){ messages in SignalProducer<Message, NoError>(values: messages)}

            return waitForNewMessageAvailable(from)
                .then(streamMessagesProducer)
        }

        func streamNextBatch(from: Offset, observer: Observer<Message, NoError>, observerDisposable: CompositeDisposable) -> Void {
            func hasReachedEndOffset(currentOffset: Offset) -> Bool {
                return endOffsetOpt.map{ endOffset in endOffset == currentOffset } ?? false
            }

            print("StreamNextBatch \(from)")

            streamMessagesChunk(from).startWithSignal { signal, signalDisposable in
                var lastOffset: Offset = from
                let disposableHandle = observerDisposable.addDisposable(signalDisposable)

                signal.observe { switch $0 {
                    case let .Failed(error): observer.sendFailed(error)
                    case .Interrupted: observer.sendInterrupted()
                    case .Completed:
                        disposableHandle.remove()
                        streamNextBatch(lastOffset.next, observer: observer, observerDisposable: observerDisposable)
                    case .Next(let message):
                        if hasReachedEndOffset(message.offset) {
                            disposableHandle.remove()
                            observer.sendCompleted()
                        } else {
                            lastOffset = message.offset
                            observer.sendNext(message)
                        }
                    }
                }
            }
        }

        return SignalProducer<Message, NoError> { observer, observerDisposable in
            streamNextBatch(startOffset, observer: observer, observerDisposable: observerDisposable)
        }
    }

func testShouldStreamMessagesWaitingForFutureMessages() {
        let expectation = self.expectationWithDescription("Test")
        let messages = (0...50000).map{value in self.createKafkaData(UInt64(value)) }
        let nextMessages = (50001...65000).map{value in self.createKafkaData(UInt64(value)) }

        try! self.sut.publishMessages(messages, persist: false).get()

        let messageCountFuture = self.sut
            .streamMessages(from: Offset(value: 45), toExclusive: Offset(value: 60000), includeTransient: true)
            .observeOn(QueueScheduler())
            .map{ m in print("sleeping at \(m.data)"); sleep(1); return 1 }
            .reduce(0, +)
            .toFuture()

        messageCountFuture.onSuccess{ count in
            expect(count) == 15
            expectation.fulfill()
        }

        try! self.sut.publishMessages(nextMessages, persist: false).get()

        self.waitForExpectationsWithTimeout(30, handler: nil)
    }

     func createKafkaData(number: UInt64) -> String {
        return "message \(number)"
    }
question

Tous les 6 commentaires

_Désolé pour le long délai de réponse. Nous avons travaillé sur beaucoup d'anciens problèmes car certains contributeurs ont abandonné._

Malheureusement, AFAIK n'existe actuellement aucun moyen de fournir une contre-pression entre les planificateurs. 😞

Savez-vous si d'autres cadres réactifs offrent un moyen de le faire? Je pense vraiment que c'est un concept intéressant.

Autant que je sache, les frameworks de la famille Rx ne prennent généralement pas en charge la contre-pression, mais une implémentation particulière de streams (akka-streams) la prend en charge: http://www.smartjava.org/content/visualizing-back-pressure -et-flux-réactifs-akka-streams-statsd-grafana-et-influxdb

L'idée est que les données circulent en aval et la demande circule en amont, de sorte que le destinataire contrôle toujours le débit maximal de données entrantes.

De plus, RxSwift ne semble pas avoir de contre-pression, mais il semble que leur opérateur différé suffirait à répondre aux mêmes besoins: http://reactivex.io/documentation/operators/defer.html

À quel point serait-il difficile d'avoir un tel opérateur différé dans ReactiveCocoa?

Pas dur. J'ai fait quelque chose de similaire à Rex . Cependant, cela ressemble à votre demande d'une version généralisée. Au lieu d'un intervalle de temps pour le retard, vous voudriez qu'un signal de déclenchement / paramètre de producteur reporte l'abonnement.

@neilpa nous aurions besoin que l'abonnement soit différé jusqu'à ce que le consommateur ait fini de consommer le flux. Cela fonctionne bien avec ReactiveCocoa (exemple ci-dessus) jusqu'à ce que vous commenciez à vous abonner sur un autre planificateur.

Je vais clore cela puisque votre question a reçu une réponse. Si vous souhaitez faire une proposition pour quelque chose, n'hésitez pas à ouvrir un nouveau numéro ou PR!

Cette page vous a été utile?
0 / 5 - 0 notes