简体   繁体   English

在Java 9上发布数据以只有一个订阅者将使用它的方式流向订阅者

[英]Publishing data on java 9 Flow to subscribers in a way that only one subscriber will consume it

Is there a way to publish data to subscribers in a way that only one subscriber will receive it ? 有没有办法以只有一个订阅者会收到订阅者的方式向订阅者发布数据? What i am trying to achieve is that the subscriber publisher model will work as a queue that has multiple readers but one publisher. 我想要实现的是订阅者发布者模型将作为具有多个读者但只有一个发布者的队列。 Once the publisher will publish data, the first subscriber that receives it, will be the only one that will process it. 一旦发布者将发布数据,接收它的第一个订阅者将是唯一将处理它的订阅者。

Thanks in advance !!! 提前致谢 !!!

In reactive streams (at least, in their java.util.concurrent.Flow incarnation), subscribers just ask for data, and only the publisher is in control of how to publish that data. 在反应流中(至少在他们的java.util.concurrent.Flow ),订阅者只需要数据,只有发布者控制如何发布该数据。

The only general purpose implementation of Flow.Publisher that exists in Java 9 is SubmissionPublisher that follows the standard pub/sub way of publishing any published item to all the subscribers. Java 9中存在的Flow.Publisher的唯一通用实现是SubmissionPublisher ,它遵循将所有已发布项目发布给所有订阅者的标准pub / sub方式。 I did not find any easy way to hack SubmissionPublisher to make it only publish to one subscriber. 我没有找到任何简单的方法来破解SubmissionPublisher ,使其只发布给一个订阅者。

But you could try to write your own Flow.Publisher implementation, something like this: 但您可以尝试编写自己的Flow.Publisher实现,如下所示:

class QueueLikePublisher<T> implements Publisher<T> {
    private final ExecutorService executor = ForkJoinPool.commonPool(); // daemon-based
    private List<QueueLikeSubscription<? super T>> subscriptions = new CopyOnWriteArrayList<>();

    public synchronized void subscribe(Subscriber<? super T> subscriber) {
        // subscribing: adding a new subscription to the list
        QueueLikeSubscription<? super T> subscription = new QueueLikeSubscription<>(subscriber, executor);
        subscriptions.add(subscription);
        subscriber.onSubscribe(subscription);
    }

    public void submit(T item) {
        // we got some data: looking for non-completed and demanding
        // subscription and give it the data item

        for (QueueLikeSubscription<? super T> subscription : subscriptions) {
            if (!subscription.completed && subscription.demand > 0) {
                subscription.offer(item);
                // we just give it to one subscriber; probaly offer() call needs
                // to be wrapped in a try/catch
                break;
            }
        }
    }

    static class QueueLikeSubscription<T> implements Subscription {
        private final Subscriber<? super T> subscriber;
        private final ExecutorService executor;
        volatile int demand = 0;
        volatile boolean completed = false;

        QueueLikeSubscription(Subscriber<? super T> subscriber,
                ExecutorService executor) {
            this.subscriber = subscriber;
            this.executor = executor;
        }

        public synchronized void request(long n) {
            if (n != 0 && !completed) {
                if (n < 0) {
                    IllegalArgumentException ex = new IllegalArgumentException();
                    executor.execute(() -> subscriber.onError(ex));
                } else {
                    // just extending the demand
                    demand += n;
                }
            }
        }

        public synchronized void cancel() {
            completed = true;
        }

        Future<?> offer(T item) {
            return executor.submit(() -> {
                try {
                    subscriber.onNext(item);
                } catch (Exception e) {
                    subscriber.onError(e);
                }
            });
        }
    }
}

It publishes the item to the first subscriber that is not yet completed (for example, cancelled) and that has non-zero demand. 它将项目发布到尚未完成(例如,已取消)且具有非零需求的第一个订户。

Please note that this code is just an outline for illistrative purposes to demonstrate the idea . 请注意,此代码只是用于演示该想法的行政用途的大纲 For example, it should probably contain more exception handling (like handling the RejectedExecutionException ). 例如,它应该包含更多的异常处理(比如处理RejectedExecutionException )。

The case when only one subscriber should receive every data item is common. 只有一个用户应该接收每个数据项的情况很常见。 For example, subscribers can be database connections and data items - requests to a database, and the publisher - the central entry point to the whole connection pool. 例如,订户可以是数据库连接和数据项 - 对数据库的请求,以及发布者 - 整个连接池的中心入口点。 It is a different data exchange protocol, so using interfaces from jucFlow may be confusing. 它是一种不同的数据交换协议,因此使用jucFlow中的接口可能会造成混淆。

Generally, those interfaces can be used for this master-worker protocol, but there is a subtle but important difference: subscribers should not request more than one data item at a time. 通常,这些接口可用于此主工作器协议,但存在一个微妙但重要的区别:订户不应一次请求多个数据项。 Otherwise, one worker can take several items while other workers would sit without work. 否则,一名工人可以带几件物品,而其他工人则无需工作。 So the method Subscription#request() can be removed from interface. 因此可以从接口中删除Subscription#request()方法。 It is assumed that by the act of subscribing, the subscriber agrees to accept one data item. 假设通过订阅行为,订户同意接受一个数据项。 As soon as that item is submitted to the subscriber, subscriber is unsubscribed. 一旦该项目提交给订户,订户就会被取消订阅。 This allows not to scan the list of subscriptions trying to find acceptable subscriber (as in the @Roman Puchkovskiy implementation) but submit next data item to the first subscribed subscriber. 这允许不扫描试图找到可接受订户的订阅列表(如在@Roman Puchkovskiy实现中),而是将下一个数据项提交给第一个订阅订户。 As soon as subscriber needs more data, it subscribes again. 一旦订户需要更多数据,它就会再次订阅。 This is exactly how worker threads in a thread pool request next tasks. 这正是线程池中的工作线程请求下一个任务的方式。

Since the method cancel() remains the only method in Subscription , we can replace it with new method Publisher#cancel(Subscriber) and eliminate Subscription interface entirely. 由于方法cancel()仍然是Subscription唯一的方法,我们可以用新方法Publisher#cancel(Subscriber)替换它,并完全取消Subscription接口。 The method Subscriber#onSubscribe(Subscription) is replaced then with the method Subscriber#onSubscribe(Publisher) . 然后使用方法Subscriber#onSubscribe(Publisher)替换方法Subscriber#onSubscribe(Subscription) Subscriber#onSubscribe(Publisher)

I am developing an asynchronous library (it is not yet of production quality) which contains the solution for your use case: class PickPoint . 我正在开发一个异步库(它还没有生产质量),它包含了用例的解决方案:类PickPoint

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Java 9 Flow使用lambdas定义订户 - Java 9 Flow define subscriber with lambdas RxJava:“java.lang.IllegalStateException:只允许一个订阅者!” - RxJava: “java.lang.IllegalStateException: Only one subscriber allowed!” 使用RxJava 2的多用户和数据流策略 - Strategy for multiple subscribers and flow of data using RxJava 2 如何将两个订户(订阅相同的查询)消耗为一个? - How to consume two subscriber (subscribed to same query) to one? 仅一个连接允许接收订户 - Only one connection receive subscriber allowed 输入通道订户未使用Spring Cloud数据流中的自定义接收器 - Input Channel subscriber not up for Custom Sink in Spring Cloud Data flow Java:PubsubMessage 订阅者收到没有数据或属性的消息 - Java: PubsubMessage Subscriber receives message with no data or attribute 使用Java客户端使用张量流服务启动模型 - Consume tensor-flow serving inception model using java client 有没有办法在 Java 中只允许一个类型作为属性? - Is there a way to allow only one Type as Attribute in Java? Java 9反应流:一个订阅服务器是否属于一个发布服务器 - Java 9 reactive flows: does one Subscriber belong to one Publisher
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM