Я пытаюсь понять, как создать SignalProducer
который будет многократно извлекать следующий фрагмент данных, когда весь поток будет использован.
У меня есть реализация, но механизм противодавления не работает, если производитель вызывает observeOn
. Кажется, что происходит то, что производитель продолжает получать новые данные, даже если клиент еще не использовал весь поток.
Есть идеи, как я могу достичь того, чего хочу? Это вообще возможно?
Спасибо!
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)"
}
_Извините за долгую задержку ответа. Мы работали над множеством старых проблем, так как некоторые участники отказались ._
К сожалению, AFAIK в настоящее время не может обеспечить противодавление между планировщиками. 😞
Знаете ли вы, есть ли способ сделать это с помощью каких-либо других реактивных фреймворков? Я определенно думаю, что это интересная концепция.
Насколько мне известно, фреймворки из семейства Rx обычно не поддерживают обратное давление, но одна конкретная реализация потоков (akka-streams) поддерживает его: http://www.smartjava.org/content/visualizing-back-pressure -и-реактивные-потоки-акка-потоки-статистика-графана-и-притокdb
Идея состоит в том, что данные идут вниз по потоку, а спрос - вверх, поэтому получатель всегда контролирует максимальную скорость входящих данных.
Также RxSwift, похоже, не имеет противодавления, но похоже, что их оператора defer было бы достаточно для удовлетворения тех же потребностей: http://reactivex.io/documentation/operators/defer.html
Насколько сложно было бы иметь такой оператор отсрочки в ReactiveCocoa?
Не трудно. Я сделал что-то подобное в Rex . Хотя, похоже, вы просите обобщенную версию. Вместо временного интервала для задержки вам понадобится сигнал запуска / параметр производителя для отсрочки подписки.
@neilpa нам нужно, чтобы подписка была отложена до тех пор, пока потребитель не закончит потребление потока. Это хорошо работает с ReactiveCocoa
(пример выше), пока вы не начнете подписываться в другом планировщике.
Я собираюсь закрыть это, так как на ваш вопрос был дан ответ. Если вы хотите что-то сделать, не стесняйтесь открывать новый выпуск или пиар!