Java 9 Reactive Streams介绍

从 Java 9 开始提供了 Reactive Streams API ( java.util.concurrent.Flow), 实现了异步非阻塞的流处理方式。有关响应式流介绍 reactive-streams

Reactive Streams 是通过异步处理流的方式,因此他们有一组 Publisher 和 Subscriber,Publisher 将数据流 push 到 Subscriber,Subscriber 则将消费这些数据流,并通过 backpressure(个人理解为回压)来反馈 Subscriber 消费时的压力,调节 Publisher 处理的速度

image.png

有时我们必须在 Publisher 和 Subsriber 之间转换数据。 Processor 是位于 End Publisher 和 End Subsriber 之间的实体,用于转换从 Publisher 接收的数据,以便 Subsriber 可以理解它。Processor 可以使用多个组成 Processor Chain 链去处理数据

image.png

Java 9 Flow API

Java 9 Flow API 实现了 Reactive Streams 规范。 Flow API 是 Iterator 和 Observer 模式的组合。 Iterator 在 pull 模型上工作,其中应用程序从源中拉出数据,而 Observer 在 push 模型上工作,并在将数据从源推到应用程序时进行处理。
Java 9 Flow API订阅者可以在订阅发布者的同时请求 N 个项目。 然后将项目从 Publisher 推送到 Subsriber,直到没有其他项目可推送或出现一些错误为止。

java.util.concurrent.Flow.Publisher

Publisher 函数式接口用于将数据流发送到 Subscriber

public void subscribe(Subscriber subscriber);

subscribe 方法用于指定订阅者 Subscriber

java.util.concurrent.Flow.Subscriber

Subscriber 则为消费处理 Publisher 发送过来的数据流

public void onSubscribe(Subscription subscription);
public void onNext(T item);
public void onError(Throwable throwable);
public void onComplete();
  1. onSubscribe: 这是在 Subscriber 服务器订阅 Publisher 以接收消息时调用的第一种方法。 通常,我们调用 subscription.request 开始从处理器接收具体数量的数据流。
  2. onNext: 从 Publisher 处收到数据时,将调用此方法,这是我们在其中实现业务逻辑以处理 data stream,然后从 Publisher 处请求更多数据的地方。
  3. onError:当发生不可恢复的错误时,将调用此方法,我们可以使用此方法清理任务,例如关闭数据库连接。
  4. onComplete: 这类似于 finally 方法,并且在 Publisher 没有 push 任何其他 data stream 并且关闭 Publisher 时被调用。 我们可以使用它发送成功处理流的通知。

java.util.concurrent.Flow.Subscription

这用于在 Publisher 和 Subscriber 之间创建异步非阻塞连接。 Subscriber 调用其请求方法以向 Publisher 获取新的 data stream。 它还具有取消方法以取消订阅,即关闭 Subscriber 和 Publisher 之间的连接。

java.util.concurrent.Flow.Processor

此接口同时扩展了 Publisher 和 Subscriber,用于在 Publisher 和 Subscriber 之间转换消息

java.util.concurrent.SubmissionPublisher

发布者实现异步提交数据流到当前的订阅者中直到连接被关闭,通过使用 Exceutor 框架提交 reactive stream 数据到订阅者

Java Example

  1. 定义数据流实体 Employee , 用于数据传输
public class Employee {

  private int id;
  private String name;

  public int getId() {
    return id;
  }

  public void setId(int id) {
    this.id = id;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Employee() {
  }

  public Employee(int i, String s) {
    this.id = i;
    this.name = s;
  }

  @Override
  public String toString() {
    return "[id=" + id + ",name=" + name + "]";
  }
}
  1. 定义 Subsriber 订阅者 实现 Subscriber 接口,用于对数据进行处理
public class MySubscriber implements Subscriber {

  private Subscription subscription;
  private int count = 0;

  @Override
  public void onSubscribe(Subscription subscription) {

    System.out.println("Subscribed");
    this.subscription = subscription;
    this.subscription.request(1);
    System.out.println("onSubscribe requested 1 item");
  }

  @Override
  public void onNext(Employee item) {
    System.out.println("Process Employee" + count);
    count++;
    try {
      TimeUnit.SECONDS.sleep(2);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    this.subscription.request(1);
  }

  @Override
  public void onError(Throwable throwable) {
    System.out.println("some error happen" + throwable);
  }

  @Override
  public void onComplete() {
    System.out.println("all process done");
  }

  public int getCount() {
    return count;
  }

  public void setCount(int count) {
    this.count = count;
  }
}

subscription 变量需要保存其引用,以此在 onNext 方法中对 Publisher 进行请求数据流
count 变量用于记录处理的次数,将在 main thread 中通知任务是否处理完成

  1. Publisher 示例程序

我们将使用 SubmissionPublisher 作为发布者作为示例,因此让我们看一下响应流实现的测试程序。

  public static void main(String[] args) throws InterruptedException {
    //create publisher
    SubmissionPublisher publisher = new SubmissionPublisher<>();

    //register subscriber
    MySubscriber subscriber = new MySubscriber();
    publisher.subscribe(subscriber);
    List employees = xxxxx;

    //publish items
   employees.forEach(publisher::submit);
    while (employees.size() != subscriber.getCount()) {
      TimeUnit.SECONDS.sleep(5);
    }
    publisher.close();
    System.out.println("exiting app");
  }

Back Pressure

当发布者以比订阅者消耗的速度快得多的速度生成消息时,就会形成回压。 Flow API 没有提供任何机制来发出有关背压的信号或进行处理。 但是我们可以设计自己的策略来处理它,例如微调用户或降低消息产生率。 SubmissionPublisher 中提供了一个 buffer 的机制,允许 Subsriber 最大处理的量,超过该数量将被阻塞

你可能感兴趣的:(Java 9 Reactive Streams介绍)