響應式編程——Flow API

1.官方文檔

Interrelated interfaces and static methods for establishing flow-
controlled components in which Publishers produce items 
consumed by one or more Subscribers, each managed by a 
Subscription.

These interfaces correspond to the reactive-streams specification. 
They apply in both concurrent and distributed asynchronous 
settings: All (seven) methods are defined in void "one-way" 
message style. Communication relies on a simple form of flow 
control (method Flow.Subscription.request(long)) that can be used 
to avoid resource management problems that may otherwise occur 
in "push" based systems.

Examples. A Flow.Publisher usually defines its own 
Flow.Subscription implementation; constructing one in method 
subscribe and issuing it to the calling Flow.Subscriber. It publishes 
items to the subscriber asynchronously, normally using an 
Executor. For example, here is a very simple publisher that only 
issues (when requested) a single TRUE item to a single 
subscriber. Because the subscriber receives only a single item, 
this class does not use buffering and ordering control required in 
most implementations (for example SubmissionPublisher).

用於建立流控制組件的相互關聯的接口和靜態方法,其中Publishers生產由一個或多個Subscribers使用的元素,Subscriber由Subscription管理。

這些接口對應於reactive-streams規範。它們適用於併發和分佈式異步環境:所有(七種)方法都以void“單向”消息樣式定義。通信依賴於簡單形式的流控制(方法Flow.Subscription.request(long)),可用於避免在“基於推送”的系統中可能發生的資源管理問題。

例子。 Flow.Publisher通常定義自己的Flow.Subscription實現;在方法subscribe中構造一個並將其發佈到調用Flow.Subscriber。它通常使用Executor異步地向訂閱者發佈items。例如,這是一個非常簡單的發佈者,它只向單個訂閱者發出(如果請求)單個TRUE元素。由於訂閱者只接收單個元素,因此該類不使用大多數實現中所需的緩衝和排序控制(例如SubmissionPublisher)。

 class OneShotPublisher implements Publisher<Boolean> {
   private final ExecutorService executor = ForkJoinPool.commonPool(); // daemon-based
   private boolean subscribed; // true after first subscribe
   public synchronized void subscribe(Subscriber<? super Boolean> subscriber) {
     if (subscribed)
       subscriber.onError(new IllegalStateException()); // only one allowed
     else {
       subscribed = true;
       subscriber.onSubscribe(new OneShotSubscription(subscriber, executor));
     }
   }
   static class OneShotSubscription implements Subscription {
     private final Subscriber<? super Boolean> subscriber;
     private final ExecutorService executor;
     private Future<?> future; // to allow cancellation
     private boolean completed;
     OneShotSubscription(Subscriber<? super Boolean> subscriber,
                         ExecutorService executor) {
       this.subscriber = subscriber;
       this.executor = executor;
     }
     public synchronized void request(long n) {
       if (n != 0 && !completed) {
         completed = true;
         if (n < 0) {
           IllegalArgumentException ex = new IllegalArgumentException();
           executor.execute(() -> subscriber.onError(ex));
         } else {
           future = executor.submit(() -> {
             subscriber.onNext(Boolean.TRUE);
             subscriber.onComplete();
           });
         }
       }
     }
     public synchronized void cancel() {
       completed = true;
       if (future != null) future.cancel(false);
     }
   }
 }
A Flow.Subscriber arranges that items be requested and 
processed. Items (invocations of Flow.Subscriber.onNext(T)) are 
not issued unless requested, but multiple items may be requested.
 Many Subscriber implementations can arrange this in the style of 
the following example, where a buffer size of 1 single-steps, and 
larger sizes usually allow for more efficient overlapped processing 
with less communication; for example with a value of 64, this 
keeps total outstanding requests between 32 and 64. Because 
Subscriber method invocations for a given Flow.Subscription are 
strictly ordered, there is no need for these methods to use locks or 
volatiles unless a Subscriber maintains multiple Subscriptions (in 
which case it is better to instead define multiple Subscribers, each 
with its own Subscription).

Flow.Subscriber安排要請求和處理的元素。 除非請求,否則不會發出元素(Flow.Subscriber.onNext(T)的調用),但可能會請求多個元素。 許多訂閱者實現可以按照以下示例的樣式進行排列,其中緩衝區大小爲1個單步,而較大的大小通常允許更有效的重疊處理和更少的通信; 例如,值爲64,這使得未完成的請求總數保持在32到64之間。由於對給定Flow.Subscription的訂閱者方法調用是嚴格排序的,因此除非訂閱者維護多個訂閱,否則這些方法不需要使用鎖或volatile。 (在多個訂閱情況下,最好定義多個Subscribers,每個訂閱者都有自己的Subscription)。

 class SampleSubscriber<T> implements Subscriber<T> {
   final Consumer<? super T> consumer;
   Subscription subscription;
   final long bufferSize;
   long count;
   SampleSubscriber(long bufferSize, Consumer<? super T> consumer) {
     this.bufferSize = bufferSize;
     this.consumer = consumer;
   }
   public void onSubscribe(Subscription subscription) {
     long initialRequestSize = bufferSize;
     count = bufferSize - bufferSize / 2; // re-request when half consumed
     (this.subscription = subscription).request(initialRequestSize);
   }
   public void onNext(T item) {
     if (--count <= 0)
       subscription.request(count = bufferSize - bufferSize / 2);
     consumer.accept(item);
   }
   public void onError(Throwable ex) { ex.printStackTrace(); }
   public void onComplete() {}
 }
The default value of defaultBufferSize() may provide a useful 
starting point for choosing request sizes and capacities in Flow 
components based on expected rates, resources, and usages. Or, 
when flow control is never needed, a subscriber may initially 
request an effectively unbounded number of items, as in:

defaultBufferSize()的默認值可以提供一個有用的起點,用於根據預期的速率、資源和用法選擇Flow組件中的請求大小和容量。 或者,當從不需要流量控制時,subscriber最初可以請求有效無限數量的元素,如:

 class UnboundedSubscriber<T> implements Subscriber<T> {
   public void onSubscribe(Subscription subscription) {
     subscription.request(Long.MAX_VALUE); // effectively unbounded
   }
   public void onNext(T item) { use(item); }
   public void onError(Throwable ex) { ex.printStackTrace(); }
   public void onComplete() {}
   void use(T item) { ... }
 }

2.源碼


3.Pull、Push和Pull-Push模式(處理數據模式)

  • pull-based。client端不斷輪詢服務端以獲取數據。這種模式的優點是當client端資源有限時可以更好的控制數據流(停止輪詢),而缺點是當服務端沒有數據時輪詢是對計算資源和網絡資源的浪費。
  • push-based,生產者不關心消費者的消費能力,直接推送數據。這種模式的缺點是當消費資源低於生產資源時會造成緩衝區溢出從而數據丟失,當丟失率維持在較小的數值時還可以接受,但是當這個比率變大時我們會希望生產者降速以避免大規模數據丟失。
  • 響應式編程是一種pull-push混合模式以綜合他們的優點,這種模式下消費者負責請求數據以控制生產者數據流,同時當處理資源不足時也可以選擇阻斷或者丟棄數據。
    要指出是,當消費速度低於生產速度時,消費者要求生產者降低速度以完全消費數據(這個現象稱作back-pressure)。

4.Flow與Stream

  • Java8中引入的StreamAPI通過map、reduce以及其他操作可以完美的處理數據集
  • FlowAPI則專注於處理數據的流通,比如對數據的請求,減速,丟棄,阻塞等。reactive streams不僅兼容傳統編程方式,而且還支持函數式編程以極大的提高可讀性和可維護性。
  • 從技術上講,我們完全可以使用Flows來替換Streams,但任何時候都這麼做就顯得過於偏激。比如,我們創建一個Publisher來作爲int數組的數據源,然後在Processor中轉換Integer爲String,最後創建一個Subscriber來歸併到一個String中。這個時候就完全沒有必要使用Flows,因爲這不是在控制兩個模塊或兩個線程間的數據通信,這個時候使用Streams更爲合理。

總之,Flow更關注於通信處理,Stream更關注於數據處理。

5.雜誌出版商示例

  • 假設出版商有兩個訂閱客戶
  • 出版商將爲每個訂閱客戶出版20本雜誌。出版商知道他們的客戶有時在郵遞雜誌時會不在家,而當他們的郵箱(subscriber buffer)不巧被塞滿時郵遞員會退回或丟棄雜誌,出版商不希望出現這種情況。
  • 於是出版商發明了一個郵遞系統:當客戶在家時再給出版商致電,出版商會立即郵遞一份雜誌。出版商打算在辦公室爲每個客戶保留一個小號的郵箱以防當雜誌出版時客戶沒有第一時間致電獲取。出版商認爲爲每個客戶預留一個可以容納8份雜誌的郵件已經足夠(publisher buffer)

總結場景:

  • 如果客戶請求雜誌足夠迅速,將不會存在郵箱容量的問題。
  • 如果客戶沒有以雜誌出版的速度發出請求,那麼郵箱將被塞滿。這位員工提出以下幾種處理方案:
    a. 增加郵箱容量,爲每位客戶提供可容納20份雜誌的郵箱。(publisher增加buffer)
    b. 直到客戶請求下一份雜誌之前停止印刷,並且根據客戶請求的速度降低印刷速度以清空郵箱。
    c. 新的雜誌直接丟掉。
    d. 一個折中的方案: 如果郵箱滿了,在下次打印之前等待一段時間,如果還是沒有足夠的空間則丟棄新的雜誌。

最終選擇了方案d。

package com.wz.concurrent.other;

import java.util.concurrent.Flow;
import java.util.stream.IntStream;

//import org.slf4j.Logger;
//import org.slf4j.LoggerFactory;

/**
 * @Author : Wang Zhen.
 * @Date : Created in 14:50 2019/7/29
 * @Description :
 * @Modified By   :
 * @Version :
 */
public class MagazineSubscriber implements Flow.Subscriber<Integer> {

    public static final String JACK = "Jack";
    public static final String PETE = "Pete";

//    private static final Logger log = LoggerFactory.
//            getLogger(MagazineSubscriber.class);

    private final long sleepTime;
    private final String subscriberName;
    private Flow.Subscription subscription;
    private int nextMagazineExpected;
    private int totalRead;

    MagazineSubscriber(final long sleepTime, final String subscriberName) {
        this.sleepTime = sleepTime;
        this.subscriberName = subscriberName;
        this.nextMagazineExpected = 1;
        this.totalRead = 0;
    }

    @Override
    public void onSubscribe(final Flow.Subscription subscription) {
        this.subscription = subscription;
        subscription.request(1);
    }

    @Override
    public void onNext(final Integer magazineNumber) {
        if (magazineNumber != nextMagazineExpected) {
            IntStream.range(nextMagazineExpected, magazineNumber).forEach(
                    (msgNumber) ->
                            log("Oh no! I missed the magazine " + msgNumber)
            );
            // Catch up with the number to keep tracking missing ones
            nextMagazineExpected = magazineNumber;
        }
        log("Great! I got a new magazine: " + magazineNumber);
        takeSomeRest();
        nextMagazineExpected++;
        totalRead++;

        log("I'll get another magazine now, next one should be: " +
                nextMagazineExpected);
        subscription.request(1);
    }

    @Override
    public void onError(final Throwable throwable) {
        log("Oops I got an error from the Publisher: " + throwable.getMessage());
    }

    @Override
    public void onComplete() {
        log("Finally! I completed the subscription, I got in total " +
                totalRead + " magazines.");
    }

    private void log(final String logMessage) {
        System.out.println("<=========== [" + subscriberName + "] : " + logMessage);
    }

    public String getSubscriberName() {
        return subscriberName;
    }

    private void takeSomeRest() {
        try {
            Thread.sleep(sleepTime);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }
}

MagazineSubscriber中的方法:

  • onSubscriber(subscription) Publisher在被指定一個新的Subscriber時調用此方法。 一般來說你需要在subscriber內部保存這個subscrition實例,因爲後面會需要通過她向publisher發送信號來完成:請求更多數據,或者取消訂閱。 一般在這裏我們會直接請求第一個數據,正如代碼中所示。
  • onNext(magazineNumber) 每當新的數據產生,這個方法會被調用。在我們的示例中,我們用到了最經典的使用方式:處理這個數據的同時再請求下一個數據。然而我們在這中間添加了一段可配置的sleep時間,這樣我們可以嘗試訂閱者在不同場景下的表現。剩下的一段邏輯判斷僅僅是記錄下丟失的雜誌(當publisher出現丟棄數據的時候)。
  • onError(throwable) 當publisher出現異常時會調用subscriber的這個方法。在我們的實現中publisher丟棄數據時會產生異常。
  • onComplete() 當publisher數據推送完畢時會調用此方法,於是整個訂閱過程結束。
package com.wz.concurrent.other;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.SubmissionPublisher;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Author : Wang Zhen.
 * @Date : Created in 14:53 2019/7/29
 * @Description :
 * @Modified By   :
 * @Version :
 */
public class ReactiveFlowApp {

    private static final int NUMBER_OF_MAGAZINES = 20;
    private static final long MAX_SECONDS_TO_KEEP_IT_WHEN_NO_SPACE = 2;
//    private static final Logger log =
//            LoggerFactory.getLogger(ReactiveFlowApp.class);

    public static void main(String[] args) throws Exception {
        final ReactiveFlowApp app = new ReactiveFlowApp();

        System.out.println("\n\n### CASE 1: Subscribers are fast, buffer size is not so " +
                "important in this case.");
        app.magazineDeliveryExample(100L, 100L, 8);

        System.out.println("\n\n### CASE 2: A slow subscriber, but a good enough buffer " +
                "size on the publisher's side to keep all items until they're picked up");
        app.magazineDeliveryExample(1000L, 3000L, NUMBER_OF_MAGAZINES);

        System.out.println("\n\n### CASE 3: A slow subscriber, and a very limited buffer " +
                "size on the publisher's side so it's important to keep the slow " +
                "subscriber under control");
        app.magazineDeliveryExample(1000L, 3000L, 8);

    }

    void magazineDeliveryExample(final long sleepTimeJack,
                                 final long sleepTimePete,
                                 final int maxStorageInPO) throws Exception {
        final SubmissionPublisher<Integer> publisher =
                new SubmissionPublisher<>(ForkJoinPool.commonPool(), maxStorageInPO);

        final MagazineSubscriber jack = new MagazineSubscriber(
                sleepTimeJack,
                MagazineSubscriber.JACK
        );
        final MagazineSubscriber pete = new MagazineSubscriber(
                sleepTimePete,
                MagazineSubscriber.PETE
        );

        publisher.subscribe(jack);
        publisher.subscribe(pete);

        System.out.println("Printing 20 magazines per subscriber, with room in publisher for "
                + maxStorageInPO + ". They have " + MAX_SECONDS_TO_KEEP_IT_WHEN_NO_SPACE +
                " seconds to consume each magazine.");
        IntStream.rangeClosed(1, 20).forEach((number) -> {
            System.out.println("Offering magazine " + number + " to consumers");
            final int lag = publisher.offer(
                    number,
                    MAX_SECONDS_TO_KEEP_IT_WHEN_NO_SPACE,
                    TimeUnit.SECONDS,
                    (subscriber, msg) -> {
                        subscriber.onError(
                                new RuntimeException("Hey " + ((MagazineSubscriber) subscriber)
                                        .getSubscriberName() + "! You are too slow getting magazines" +
                                        " and we don't have more space for them! " +
                                        "I'll drop your magazine: " + msg));
                        return false; // don't retry, we don't believe in second opportunities
                    });
            if (lag < 0) {
                log("Dropping " + -lag + " magazines");
            } else {
                log("The slowest consumer has " + lag +
                        " magazines in total to be picked up");
            }
        });

        // Blocks until all subscribers are done (this part could be improved
        // with latches, but this way we keep it simple)
        while (publisher.estimateMaximumLag() > 0) {
            Thread.sleep(500L);
        }

        // Closes the publisher, calling the onComplete() method on every subscriber
        publisher.close();
        // give some time to the slowest consumer to wake up and notice
        // that it's completed
        Thread.sleep(Math.max(sleepTimeJack, sleepTimePete));
    }

    private static void log(final String message) {
        System.out.println("===========> " + message);
    }

}

使用Java9 SubmissionPublisher類來創建publisher。正如javadoc所述, 當subscribers消費過慢,就像Reactive Streams中的Publisher一樣她會阻塞或丟棄數據。

main()方法中使用不同參數調用以上邏輯三次,以模擬之前介紹的三種不同真是場景。

  • 消費者消費速度很快,publisher緩存區不會發生問題。
  • 其中一個消費者速度很慢,以至緩存被填滿,然而緩存區足夠大以容納所有所有數據,不會發生丟棄。
  • 其中一個消費者速度很慢,同時緩存區不夠大。會發生丟失且情況。

結果:
情況1的如下:

### CASE 1: Subscribers are fast, buffer size is not so important in this case.
Printing 20 magazines per subscriber, with room in publisher for 8. They have 2 seconds to consume each magazine.
Offering magazine 1 to consumers
===========> The slowest consumer has 1 magazines in total to be picked up
Offering magazine 2 to consumers
===========> The slowest consumer has 2 magazines in total to be picked up
Offering magazine 3 to consumers
===========> The slowest consumer has 3 magazines in total to be picked up
Offering magazine 4 to consumers
===========> The slowest consumer has 4 magazines in total to be picked up
Offering magazine 5 to consumers
===========> The slowest consumer has 5 magazines in total to be picked up
Offering magazine 6 to consumers
===========> The slowest consumer has 6 magazines in total to be picked up
Offering magazine 7 to consumers
===========> The slowest consumer has 7 magazines in total to be picked up
Offering magazine 8 to consumers
===========> The slowest consumer has 8 magazines in total to be picked up
Offering magazine 9 to consumers
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 10 to consumers
<=========== [Jack] : Great! I got a new magazine: 1
<=========== [Pete] : Great! I got a new magazine: 1
<=========== [Jack] : I'll get another magazine now, next one should be: 2
<=========== [Jack] : Great! I got a new magazine: 2
<=========== [Pete] : I'll get another magazine now, next one should be: 2
<=========== [Pete] : Great! I got a new magazine: 2
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 11 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 3
<=========== [Jack] : Great! I got a new magazine: 3
<=========== [Pete] : I'll get another magazine now, next one should be: 3
<=========== [Pete] : Great! I got a new magazine: 3
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 12 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 4
<=========== [Jack] : Great! I got a new magazine: 4
<=========== [Pete] : I'll get another magazine now, next one should be: 4
<=========== [Pete] : Great! I got a new magazine: 4
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 13 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 5
<=========== [Jack] : Great! I got a new magazine: 5
<=========== [Pete] : I'll get another magazine now, next one should be: 5
<=========== [Pete] : Great! I got a new magazine: 5
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 14 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 6
<=========== [Jack] : Great! I got a new magazine: 6
<=========== [Pete] : I'll get another magazine now, next one should be: 6
<=========== [Pete] : Great! I got a new magazine: 6
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 15 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 7
<=========== [Jack] : Great! I got a new magazine: 7
<=========== [Pete] : I'll get another magazine now, next one should be: 7
<=========== [Pete] : Great! I got a new magazine: 7
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 16 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 8
<=========== [Jack] : Great! I got a new magazine: 8
<=========== [Pete] : I'll get another magazine now, next one should be: 8
<=========== [Pete] : Great! I got a new magazine: 8
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 17 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 9
<=========== [Jack] : Great! I got a new magazine: 9
<=========== [Pete] : I'll get another magazine now, next one should be: 9
<=========== [Pete] : Great! I got a new magazine: 9
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 18 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 10
<=========== [Jack] : Great! I got a new magazine: 10
<=========== [Pete] : I'll get another magazine now, next one should be: 10
<=========== [Pete] : Great! I got a new magazine: 10
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 19 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 11
<=========== [Jack] : Great! I got a new magazine: 11
<=========== [Pete] : I'll get another magazine now, next one should be: 11
<=========== [Pete] : Great! I got a new magazine: 11
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 20 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 12
<=========== [Jack] : Great! I got a new magazine: 12
<=========== [Pete] : I'll get another magazine now, next one should be: 12
<=========== [Pete] : Great! I got a new magazine: 12
===========> The slowest consumer has 9 magazines in total to be picked up
<=========== [Jack] : I'll get another magazine now, next one should be: 13
<=========== [Jack] : Great! I got a new magazine: 13
<=========== [Pete] : I'll get another magazine now, next one should be: 13
<=========== [Pete] : Great! I got a new magazine: 13
<=========== [Jack] : I'll get another magazine now, next one should be: 14
<=========== [Jack] : Great! I got a new magazine: 14
<=========== [Pete] : I'll get another magazine now, next one should be: 14
<=========== [Pete] : Great! I got a new magazine: 14
<=========== [Jack] : I'll get another magazine now, next one should be: 15
<=========== [Jack] : Great! I got a new magazine: 15
<=========== [Pete] : I'll get another magazine now, next one should be: 15
<=========== [Pete] : Great! I got a new magazine: 15
<=========== [Jack] : I'll get another magazine now, next one should be: 16
<=========== [Jack] : Great! I got a new magazine: 16
<=========== [Pete] : I'll get another magazine now, next one should be: 16
<=========== [Pete] : Great! I got a new magazine: 16
<=========== [Jack] : I'll get another magazine now, next one should be: 17
<=========== [Jack] : Great! I got a new magazine: 17
<=========== [Pete] : I'll get another magazine now, next one should be: 17
<=========== [Pete] : Great! I got a new magazine: 17
<=========== [Jack] : I'll get another magazine now, next one should be: 18
<=========== [Jack] : Great! I got a new magazine: 18
<=========== [Pete] : I'll get another magazine now, next one should be: 18
<=========== [Pete] : Great! I got a new magazine: 18
<=========== [Jack] : I'll get another magazine now, next one should be: 19
<=========== [Jack] : Great! I got a new magazine: 19
<=========== [Pete] : I'll get another magazine now, next one should be: 19
<=========== [Pete] : Great! I got a new magazine: 19
<=========== [Jack] : I'll get another magazine now, next one should be: 20
<=========== [Jack] : Great! I got a new magazine: 20
<=========== [Pete] : I'll get another magazine now, next one should be: 20
<=========== [Pete] : Great! I got a new magazine: 20
<=========== [Jack] : I'll get another magazine now, next one should be: 21
<=========== [Pete] : I'll get another magazine now, next one should be: 21
<=========== [Pete] : Finally! I completed the subscription, I got in total 20 magazines.
<=========== [Jack] : Finally! I completed the subscription, I got in total 20 magazines.

情況2:

### CASE 2: A slow subscriber, but a good enough buffer size on the publisher's side to keep all items until they're picked up
Printing 20 magazines per subscriber, with room in publisher for 20. They have 2 seconds to consume each magazine.
Offering magazine 1 to consumers
===========> The slowest consumer has 1 magazines in total to be picked up
Offering magazine 2 to consumers
===========> The slowest consumer has 2 magazines in total to be picked up
Offering magazine 3 to consumers
===========> The slowest consumer has 3 magazines in total to be picked up
Offering magazine 4 to consumers
===========> The slowest consumer has 4 magazines in total to be picked up
Offering magazine 5 to consumers
===========> The slowest consumer has 5 magazines in total to be picked up
Offering magazine 6 to consumers
===========> The slowest consumer has 6 magazines in total to be picked up
<=========== [Jack] : Great! I got a new magazine: 1
<=========== [Pete] : Great! I got a new magazine: 1
Offering magazine 7 to consumers
===========> The slowest consumer has 7 magazines in total to be picked up
Offering magazine 8 to consumers
===========> The slowest consumer has 8 magazines in total to be picked up
Offering magazine 9 to consumers
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 10 to consumers
===========> The slowest consumer has 10 magazines in total to be picked up
Offering magazine 11 to consumers
===========> The slowest consumer has 11 magazines in total to be picked up
Offering magazine 12 to consumers
===========> The slowest consumer has 12 magazines in total to be picked up
Offering magazine 13 to consumers
===========> The slowest consumer has 13 magazines in total to be picked up
Offering magazine 14 to consumers
===========> The slowest consumer has 14 magazines in total to be picked up
Offering magazine 15 to consumers
===========> The slowest consumer has 15 magazines in total to be picked up
Offering magazine 16 to consumers
===========> The slowest consumer has 16 magazines in total to be picked up
Offering magazine 17 to consumers
===========> The slowest consumer has 17 magazines in total to be picked up
Offering magazine 18 to consumers
===========> The slowest consumer has 18 magazines in total to be picked up
Offering magazine 19 to consumers
===========> The slowest consumer has 19 magazines in total to be picked up
Offering magazine 20 to consumers
===========> The slowest consumer has 20 magazines in total to be picked up
<=========== [Jack] : I'll get another magazine now, next one should be: 2
<=========== [Jack] : Great! I got a new magazine: 2
<=========== [Jack] : I'll get another magazine now, next one should be: 3
<=========== [Jack] : Great! I got a new magazine: 3
<=========== [Jack] : I'll get another magazine now, next one should be: 4
<=========== [Pete] : I'll get another magazine now, next one should be: 2
<=========== [Jack] : Great! I got a new magazine: 4
<=========== [Pete] : Great! I got a new magazine: 2
<=========== [Jack] : I'll get another magazine now, next one should be: 5
<=========== [Jack] : Great! I got a new magazine: 5
<=========== [Jack] : I'll get another magazine now, next one should be: 6
<=========== [Jack] : Great! I got a new magazine: 6
<=========== [Pete] : I'll get another magazine now, next one should be: 3
<=========== [Jack] : I'll get another magazine now, next one should be: 7
<=========== [Pete] : Great! I got a new magazine: 3
<=========== [Jack] : Great! I got a new magazine: 7
<=========== [Jack] : I'll get another magazine now, next one should be: 8
<=========== [Jack] : Great! I got a new magazine: 8
<=========== [Jack] : I'll get another magazine now, next one should be: 9
<=========== [Jack] : Great! I got a new magazine: 9
<=========== [Jack] : I'll get another magazine now, next one should be: 10
<=========== [Pete] : I'll get another magazine now, next one should be: 4
<=========== [Jack] : Great! I got a new magazine: 10
<=========== [Pete] : Great! I got a new magazine: 4
<=========== [Jack] : I'll get another magazine now, next one should be: 11
<=========== [Jack] : Great! I got a new magazine: 11
<=========== [Jack] : I'll get another magazine now, next one should be: 12
<=========== [Jack] : Great! I got a new magazine: 12
<=========== [Jack] : I'll get another magazine now, next one should be: 13
<=========== [Pete] : I'll get another magazine now, next one should be: 5
<=========== [Jack] : Great! I got a new magazine: 13
<=========== [Pete] : Great! I got a new magazine: 5
<=========== [Jack] : I'll get another magazine now, next one should be: 14
<=========== [Jack] : Great! I got a new magazine: 14
<=========== [Jack] : I'll get another magazine now, next one should be: 15
<=========== [Jack] : Great! I got a new magazine: 15
<=========== [Jack] : I'll get another magazine now, next one should be: 16
<=========== [Pete] : I'll get another magazine now, next one should be: 6
<=========== [Jack] : Great! I got a new magazine: 16
<=========== [Pete] : Great! I got a new magazine: 6
<=========== [Jack] : I'll get another magazine now, next one should be: 17
<=========== [Jack] : Great! I got a new magazine: 17
<=========== [Jack] : I'll get another magazine now, next one should be: 18
<=========== [Jack] : Great! I got a new magazine: 18
<=========== [Jack] : I'll get another magazine now, next one should be: 19
<=========== [Pete] : I'll get another magazine now, next one should be: 7
<=========== [Jack] : Great! I got a new magazine: 19
<=========== [Pete] : Great! I got a new magazine: 7
<=========== [Jack] : I'll get another magazine now, next one should be: 20
<=========== [Jack] : Great! I got a new magazine: 20
<=========== [Jack] : I'll get another magazine now, next one should be: 21
<=========== [Pete] : I'll get another magazine now, next one should be: 8
<=========== [Pete] : Great! I got a new magazine: 8
<=========== [Pete] : I'll get another magazine now, next one should be: 9
<=========== [Pete] : Great! I got a new magazine: 9
<=========== [Pete] : I'll get another magazine now, next one should be: 10
<=========== [Pete] : Great! I got a new magazine: 10
<=========== [Pete] : I'll get another magazine now, next one should be: 11
<=========== [Pete] : Great! I got a new magazine: 11
<=========== [Pete] : I'll get another magazine now, next one should be: 12
<=========== [Pete] : Great! I got a new magazine: 12
<=========== [Pete] : I'll get another magazine now, next one should be: 13
<=========== [Pete] : Great! I got a new magazine: 13
<=========== [Pete] : I'll get another magazine now, next one should be: 14
<=========== [Pete] : Great! I got a new magazine: 14
<=========== [Pete] : I'll get another magazine now, next one should be: 15
<=========== [Pete] : Great! I got a new magazine: 15
<=========== [Pete] : I'll get another magazine now, next one should be: 16
<=========== [Pete] : Great! I got a new magazine: 16
<=========== [Pete] : I'll get another magazine now, next one should be: 17
<=========== [Pete] : Great! I got a new magazine: 17
<=========== [Pete] : I'll get another magazine now, next one should be: 18
<=========== [Pete] : Great! I got a new magazine: 18
<=========== [Pete] : I'll get another magazine now, next one should be: 19
<=========== [Pete] : Great! I got a new magazine: 19
<=========== [Pete] : I'll get another magazine now, next one should be: 20
<=========== [Pete] : Great! I got a new magazine: 20
<=========== [Pete] : I'll get another magazine now, next one should be: 21
<=========== [Jack] : Finally! I completed the subscription, I got in total 20 magazines.
<=========== [Pete] : Finally! I completed the subscription, I got in total 20 magazines.

情況3:

### CASE 3: A slow subscriber, and a very limited buffer size on the publisher's side so it's important to keep the slow subscriber under control
Printing 20 magazines per subscriber, with room in publisher for 8. They have 2 seconds to consume each magazine.
Offering magazine 1 to consumers
===========> The slowest consumer has 1 magazines in total to be picked up
Offering magazine 2 to consumers
<=========== [Jack] : Great! I got a new magazine: 1
===========> The slowest consumer has 2 magazines in total to be picked up
Offering magazine 3 to consumers
<=========== [Pete] : Great! I got a new magazine: 1
===========> The slowest consumer has 3 magazines in total to be picked up
Offering magazine 4 to consumers
===========> The slowest consumer has 4 magazines in total to be picked up
Offering magazine 5 to consumers
===========> The slowest consumer has 5 magazines in total to be picked up
Offering magazine 6 to consumers
===========> The slowest consumer has 6 magazines in total to be picked up
Offering magazine 7 to consumers
===========> The slowest consumer has 7 magazines in total to be picked up
Offering magazine 8 to consumers
===========> The slowest consumer has 8 magazines in total to be picked up
Offering magazine 9 to consumers
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 10 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 2
<=========== [Jack] : Great! I got a new magazine: 2
<=========== [Jack] : I'll get another magazine now, next one should be: 3
<=========== [Jack] : Great! I got a new magazine: 3
<=========== [Jack] : I'll get another magazine now, next one should be: 4
<=========== [Jack] : Great! I got a new magazine: 4
<=========== [Pete] : I'll get another magazine now, next one should be: 2
<=========== [Pete] : Great! I got a new magazine: 2
<=========== [Pete] : Oops I got an error from the Publisher: Hey Pete! You are too slow getting magazines and we don't have more space for them! I'll drop your magazine: 10
===========> Dropping 1 magazines
Offering magazine 11 to consumers
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 12 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 5
<=========== [Jack] : Great! I got a new magazine: 5
<=========== [Jack] : I'll get another magazine now, next one should be: 6
<=========== [Jack] : Great! I got a new magazine: 6
<=========== [Pete] : Oops I got an error from the Publisher: Hey Pete! You are too slow getting magazines and we don't have more space for them! I'll drop your magazine: 12
===========> Dropping 1 magazines
Offering magazine 13 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 7
<=========== [Jack] : Great! I got a new magazine: 7
<=========== [Pete] : I'll get another magazine now, next one should be: 3
<=========== [Pete] : Great! I got a new magazine: 3
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 14 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 8
<=========== [Jack] : Great! I got a new magazine: 8
<=========== [Jack] : I'll get another magazine now, next one should be: 9
<=========== [Jack] : Great! I got a new magazine: 9
<=========== [Pete] : Oops I got an error from the Publisher: Hey Pete! You are too slow getting magazines and we don't have more space for them! I'll drop your magazine: 14
===========> Dropping 1 magazines
Offering magazine 15 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 10
<=========== [Jack] : Great! I got a new magazine: 10
<=========== [Pete] : I'll get another magazine now, next one should be: 4
<=========== [Pete] : Great! I got a new magazine: 4
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 16 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 11
<=========== [Jack] : Great! I got a new magazine: 11
<=========== [Jack] : I'll get another magazine now, next one should be: 12
<=========== [Jack] : Great! I got a new magazine: 12
<=========== [Pete] : Oops I got an error from the Publisher: Hey Pete! You are too slow getting magazines and we don't have more space for them! I'll drop your magazine: 16
===========> Dropping 1 magazines
Offering magazine 17 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 13
<=========== [Jack] : Great! I got a new magazine: 13
<=========== [Pete] : I'll get another magazine now, next one should be: 5
<=========== [Pete] : Great! I got a new magazine: 5
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 18 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 14
<=========== [Jack] : Great! I got a new magazine: 14
<=========== [Jack] : I'll get another magazine now, next one should be: 15
<=========== [Jack] : Great! I got a new magazine: 15
<=========== [Pete] : Oops I got an error from the Publisher: Hey Pete! You are too slow getting magazines and we don't have more space for them! I'll drop your magazine: 18
===========> Dropping 1 magazines
Offering magazine 19 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 16
<=========== [Jack] : Great! I got a new magazine: 16
<=========== [Pete] : I'll get another magazine now, next one should be: 6
<=========== [Pete] : Great! I got a new magazine: 6
===========> The slowest consumer has 9 magazines in total to be picked up
Offering magazine 20 to consumers
<=========== [Jack] : I'll get another magazine now, next one should be: 17
<=========== [Jack] : Great! I got a new magazine: 17
<=========== [Jack] : I'll get another magazine now, next one should be: 18
<=========== [Jack] : Great! I got a new magazine: 18
<=========== [Pete] : Oops I got an error from the Publisher: Hey Pete! You are too slow getting magazines and we don't have more space for them! I'll drop your magazine: 20
===========> Dropping 1 magazines
<=========== [Jack] : I'll get another magazine now, next one should be: 19
<=========== [Jack] : Great! I got a new magazine: 19
<=========== [Pete] : I'll get another magazine now, next one should be: 7
<=========== [Pete] : Great! I got a new magazine: 7
<=========== [Jack] : I'll get another magazine now, next one should be: 20
<=========== [Jack] : Great! I got a new magazine: 20
<=========== [Jack] : I'll get another magazine now, next one should be: 21
<=========== [Pete] : I'll get another magazine now, next one should be: 8
<=========== [Pete] : Great! I got a new magazine: 8
<=========== [Pete] : I'll get another magazine now, next one should be: 9
<=========== [Pete] : Great! I got a new magazine: 9
<=========== [Pete] : I'll get another magazine now, next one should be: 10
<=========== [Pete] : Oh no! I missed the magazine 10
<=========== [Pete] : Great! I got a new magazine: 11
<=========== [Pete] : I'll get another magazine now, next one should be: 12
<=========== [Pete] : Oh no! I missed the magazine 12
<=========== [Pete] : Great! I got a new magazine: 13
<=========== [Pete] : I'll get another magazine now, next one should be: 14
<=========== [Pete] : Oh no! I missed the magazine 14
<=========== [Pete] : Great! I got a new magazine: 15
<=========== [Pete] : I'll get another magazine now, next one should be: 16
<=========== [Pete] : Oh no! I missed the magazine 16
<=========== [Pete] : Great! I got a new magazine: 17
<=========== [Pete] : I'll get another magazine now, next one should be: 18
<=========== [Pete] : Oh no! I missed the magazine 18
<=========== [Pete] : Great! I got a new magazine: 19
<=========== [Pete] : I'll get another magazine now, next one should be: 20
<=========== [Pete] : Finally! I completed the subscription, I got in total 14 magazines.
<=========== [Jack] : Finally! I completed the subscription, I got in total 20 magazines.

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章