Java9中的响应式编程

367 阅读3分钟

前言

在Java9中也支持了响应式编程, 其中包含4个接口:

  1. Publisher:发布者,负责发布消息;
  2. Subscriber:订阅者,负责订阅处理消息;
  3. Subscription:订阅控制类,可用于发布者和订阅者之间通信;
  4. 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)
}