前言
在Java9中也支持了响应式编程, 其中包含4个接口:
- Publisher:发布者,负责发布消息;
- Subscriber:订阅者,负责订阅处理消息;
- Subscription:订阅控制类,可用于发布者和订阅者之间通信;
- Processor:处理者,同时充当Publisher和Subscriber的角色。
Subscription是Publisher、Subscriber 之间沟通的桥梁,可以进行流量控制,这是为了避免订阅者来不及处理来自生产方的数据,而引发出一些问题,Subscriber可以通过传入的Subscription,使用 request(n) 向 Publisher请求n个数据,或者是通过 cancel() 要求 Publisher 停止向自己发送数据并释放资源。
request方法有点像pull模式,只有订阅者告诉我需要多少条数据后,生产者会在有数据时,尽可能的推送给他,而如果有数据,订阅者没有要求推送数据时,生产者也不会贸然行动。
但是当我尝试看他的源码时,发现各种队列、循环、CAS操作,回到上面,发现这是Doug Lea写的,便直接放弃。
例子
以下代码会输出"1",但是你会发现,推送者发提交了2条数据,为什么只有1条被消费,这就是上面所说,订阅者在onSubscribe的事件上,通知Subscription,我只要求一条数据。
class MySubscriber:Flow.Subscriber<String>{
override fun onSubscribe(subscription: Flow.Subscription) {
println(subscription)
subscription.request(1)
}
override fun onNext(item: String?) {
println(item)
}
override fun onError(throwable: Throwable?) {
println(throwable)
}
override fun onComplete() {
}
}
fun main() {
var submissionPublisher = SubmissionPublisher<String>()
submissionPublisher.subscribe(MySubscriber())
submissionPublisher.submit("1")
submissionPublisher.submit("2")
Thread.sleep(10000)
}
而如果想源源不断的消费数据,需要在onNext重新request一下。
class MySubscriber:Flow.Subscriber<String>{
lateinit var subscription :Flow.Subscription
override fun onSubscribe(subscription: Flow.Subscription) {
this.subscription=subscription
subscription.request(1)
}
override fun onNext(item: String?) {
println(item)
subscription.request(1)
}
override fun onError(throwable: Throwable?) {
println(throwable)
}
override fun onComplete() {
}
}
fun main() {
var submissionPublisher = SubmissionPublisher<String>()
submissionPublisher.subscribe(MySubscriber())
submissionPublisher.submit("1")
submissionPublisher.submit("2")
Thread.sleep(10000)
}
缓冲区
如果发布者不停的发布数据,且发布速率远高于订阅者处理的能力时,Subscriber会将Publisher发布的数据缓冲在Subscription中,其长度默认为256,并阻止发布者继续发布,直到订阅者处理之后。
缓冲区大小可以更改,如下,我们将其设置为5。
class MySubscriber:Flow.Subscriber<String>{
lateinit var subscription :Flow.Subscription
override fun onSubscribe(subscription: Flow.Subscription) {
this.subscription=subscription
subscription.request(1)
}
override fun onNext(item: String?) {
println(item)
TimeUnit.SECONDS.sleep(1)
subscription.request(1)
}
override fun onError(throwable: Throwable?) {
println(throwable)
}
override fun onComplete() {
}
}
fun main() {
var submissionPublisher = SubmissionPublisher<String>(ForkJoinPool.commonPool(),5)
submissionPublisher.subscribe(MySubscriber())
while (true){
submissionPublisher.submit("1")
println("submit")
}
Thread.sleep(10000)
}
offer
有一种情况是,换冲区满了,而再通过submit发布会被阻塞,如果我们不想这样做,就可以通过offer,并且,如果onDrop参数的值返回true,还可以在重试一次,这里的意义是,当在X秒内无法提交,但回过头来再试一遍可能会成功。
他的返回值如果是负数,则表示没有提交成功。
而我们通常都使用第二个重载参数,指定一个等待时间,而不是立刻重新尝试。
public int offer(T item, long timeout, TimeUnit unit, BiPredicate<Subscriber<? super T>, ? super T> onDrop)
class MySubscriber : Flow.Subscriber<String> {
lateinit var subscription: Flow.Subscription
override fun onSubscribe(subscription: Flow.Subscription) {
this.subscription = subscription
subscription.request(1)
}
override fun onNext(item: String?) {
println("handler $item")
TimeUnit.SECONDS.sleep(2)
subscription.request(1)
}
override fun onError(throwable: Throwable?) {
println(throwable)
}
override fun onComplete() {
}
}
fun main() {
var submissionPublisher = SubmissionPublisher<String>(ForkJoinPool.commonPool(), 2)
var mySubscriber = MySubscriber()
submissionPublisher.subscribe(mySubscriber)
for (i in 1..6) {
println (submissionPublisher.offer("$i") { subscriber: Flow.Subscriber<in String>?, s: String? ->
println("push $s")
false
})
}
Thread.sleep(10000)
}