使用RxJava實現快取

banq發表於2019-01-05

在這篇文章中將使用reactiveX建立一個快取。RxJava / Kotlin沒有本機快取實現。因此,我必須使用RxJava和Kotlin為單個元素建立自己的快取(可擴充套件為Observable,Maybe等)。
主要思想是在一定時間內返回相同的元素。在那之後,我們將不得不從頭開始執行所有操作。
此快取有三個元件:SingleTransformerSingleSourceConsumer
消費者是一個簡單的回撥。它會接受我們想要的任何東西,因為它有一個void accept(T t)方法。當Observer執行onSuccess方法時執行此方法。
SingleSource是管道將訂閱的源。它有一個void subscribe(SingleObserver <T>observer)方法,其中SingleObserver只發出一個元素。當有人訂閱管道時執行此方法。
SingleTransformer對於組合單曲很有用。它有一個SingleSource<Downstream> apply(Single<Upstream> upstream) 方法,其中上游是傳入元素,下游是傳出元素。建立管道時將執行此方法。所以,它必須只執行一次。否則,將始終建立LastElementSeen,並且快取將不起作用。

首先,我們可以建立我們的消費者:

class LastElementSeen<T>(private val timeout: Long, private val unit: TimeUnit) : Consumer<T> {
private var lastEmissionTimestamp: Long = 0
    var value: T? = null
override fun accept(latest: T) {
        lastEmissionTimestamp = DateTime.now().millis
        value = latest
    }
// I used JodaTime: https://www.joda.org/joda-time/ for this piece of code.
    fun isValid(): Boolean {
        return value?.let { DateTime.now().minus(lastEmissionTimestamp).isBefore(unit.toMillis(timeout)) } ?: false
    }
}


該元素具有我們想要的任何物件,並且在接受時具有最後的發射時間戳。我們可以呼叫LastElementSeen,因為它只代表那個。此外,此物件還有責任是否仍然有效或已過期。
下一步是建立一個自定義SingleSource的實現來決定元素是否有效,我們必須發出相同的元素,否則我們必須再次訂閱。

class LastElementSeenSingle<T>(private var upstream: Single<T>, private val lastElementSeen: LastElementSeen<T>) : SingleSource<T> {
override fun subscribe(observer: SingleObserver<in T>) {
        if (lastElementSeen.isValid()) {
            lastElementSeen.value?.let(observer::onSuccess)
        } else {
            upstream.subscribe(observer)
        }
    }
}


最後一步是建立我們的SingleTransformer:

class SingleRxCache<T>(private val timeout: Long, private val unit: TimeUnit) : SingleTransformer<T, T> {
override fun apply(upstream: Single<T>): SingleSource<T> {
        val lastElementSeen = LastElementSeen<T>(timeout, unit)
        return LastElementSeenSingle(upstream.doOnSuccess(lastElementSeen), lastElementSeen)
    }
}

我們必須建立LastElementSeen和LastElementSeenSingle並使用upstream.doOnSuccess(lastElementSeen)方法從流中發出元素。
所以,現在我們有了反應式快取。我們來試試吧!快速測試:

@Test
    fun reactiveCacheTest() {
  val single = Single.create<Long> { emitter ->
            println("Creating single")
            emitter.onSuccess(counter++)
        }.map {
            println("Map")
            it
        }.compose(SingleRxCache<Long>(3, TimeUnit.SECONDS))
for (i in 1..5) {
            single.subscribe { value -> println(value) }
            Thread.sleep(1000)
        }
    }


輸出:

Creating single
Map
1
1
1
Creating single
Map
2
2


gist中看到完整的程式碼。
 

相關文章