是否有相当于Project Reactor的Flux.create(),它适合rxjava-2中的推/拉模型?

时间:2018-03-01 22:07:20

标签: rx-java2 project-reactor

Project Reactor有这种工厂方法来创建推/拉Producer<T>

http://projectreactor.io/docs/core/release/reference/#_hybrid_push_pull_model

RxJava-2中有没有这样的东西?

如果没有,那么推荐的方法是什么(没有从头开始实际实现反应式规范接口)来创建可以处理推/拉模型的野兽?

编辑:根据要求,我将举例说明我正在尝试使用的API ...

private static class API
{
    CompletableFuture<Void> getT(Consumer<Object> consumer) {}
}

private static class Callback implements Consumer<Object>
{

    private API api;

    public Callback(API api) { this api = api; }

    @Override
    public void accept(Object o)
    {
        //do stuff with o
        //...
        //request for another o
        api.getT(this);
    }
}

public void example()
{

    API api = new API();
    api.getT(new Callback(api)).join();

}

所以它的回电基础,这将获得一个项目,你可以从内部请求另一个项目。可完成的未来不再标记项目。

2 个答案:

答案 0 :(得分:0)

以下是将此特定API转换为RxJava源的自定义Flowable的示例。但请注意,一般而言,通常可能无法使用单个反应桥设计捕获API特性:

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.*;
import java.util.function.*;

import org.reactivestreams.*;

import io.reactivex.Flowable;
import io.reactivex.internal.subscriptions.EmptySubscription;
import io.reactivex.internal.util.BackpressureHelper;

public final class SomeAsyncApiBridge<T> extends Flowable<T> {

    final Function<? super Consumer<? super T>, 
             ? extends CompletableFuture<Void>> apiInvoker;

    final AtomicBoolean once;

    public SomeAsyncApiBridge(Function<? super Consumer<? super T>, 
            ? extends CompletableFuture<Void>> apiInvoker) {
        this.apiInvoker = apiInvoker;
        this.once = new AtomicBoolean();
    }

    @Override
    protected void subscribeActual(Subscriber<? super T> s) {
        if (once.compareAndSet(false, true)) {
            SomeAsyncApiBridgeSubscription<T> parent = 
                new SomeAsyncApiBridgeSubscription<>(s, apiInvoker);
            s.onSubscribe(parent);
            parent.moveNext();
        } else {
            EmptySubscription.error(new IllegalStateException(
                "Only one Subscriber allowed"), s);
        }
    }

    static final class SomeAsyncApiBridgeSubscription<T>
    extends AtomicInteger
    implements Subscription, Consumer<T>, BiConsumer<Void, Throwable> {

        /** */
        private static final long serialVersionUID = 1270592169808316333L;

        final Subscriber<? super T> downstream;

        final Function<? super Consumer<? super T>,
            ? extends CompletableFuture<Void>> apiInvoker;

        final AtomicInteger wip;

        final AtomicLong requested;

        final AtomicReference<CompletableFuture<Void>> task;

        static final CompletableFuture<Void> TASK_CANCELLED =
                CompletableFuture.completedFuture(null);

        volatile T item;

        volatile boolean done;
        Throwable error;

        volatile boolean cancelled;

        long emitted;

        SomeAsyncApiBridgeSubscription(
                Subscriber<? super T> downstream,
                Function<? super Consumer<? super T>, 
                    ? extends CompletableFuture<Void>> apiInvoker) {
            this.downstream = downstream;
            this.apiInvoker = apiInvoker;
            this.requested = new AtomicLong();
            this.wip = new AtomicInteger();
            this.task = new AtomicReference<>();
        }

        @Override
        public void request(long n) {
            BackpressureHelper.add(requested, n);
            drain();
        }

        @Override
        public void cancel() {
            cancelled = true;
            CompletableFuture<Void> curr = task.getAndSet(TASK_CANCELLED);
            if (curr != null && curr != TASK_CANCELLED) {
                curr.cancel(true);
            }
            if (getAndIncrement() == 0) {
                item = null;
            }
        }

        void moveNext() {
            if (wip.getAndIncrement() == 0) {
                do {
                    CompletableFuture<Void> curr = task.get();

                    if (curr == TASK_CANCELLED) {
                        return;
                    }

                    CompletableFuture<Void> f = apiInvoker.apply(this);

                    if (task.compareAndSet(curr, f)) {
                        f.whenComplete(this);
                    } else {
                        curr = task.get();
                        if (curr == TASK_CANCELLED) {
                            f.cancel(true);
                            return;
                        }
                    }
                } while (wip.decrementAndGet() != 0);
            }
        }

        @Override
        public void accept(Void t, Throwable u) {
            if (u != null) {
                error = u;
                task.lazySet(TASK_CANCELLED);
            }
            done = true;
            drain();
        }

        @Override
        public void accept(T t) {
            item = t;
            drain();
        }

        void drain() {
            if (getAndIncrement() != 0) {
                return;
            }

            int missed = 1;
            long e = emitted;

            for (;;) {

                for (;;) {
                    if (cancelled) {
                        item = null;
                        return;
                    }

                    boolean d = done;
                    T v = item;
                    boolean empty = v == null;

                    if (d && empty) {
                        Throwable ex = error;
                        if (ex == null) {
                            downstream.onComplete();
                        } else {
                            downstream.onError(ex);
                        }
                        return;
                    }

                    if (empty || e == requested.get()) {
                        break;
                    }

                    item = null;

                    downstream.onNext(v);

                    e++;

                    moveNext();
                }

                emitted = e;
                missed = addAndGet(-missed);
                if (missed == 0) {
                    break;
                }
            }
        }
    }
}

测试和示例来源:

import java.util.concurrent.*;
import java.util.function.Consumer;

import org.junit.Test;

public class SomeAsyncApiBridgeTest {

    static final class AsyncRange {
        final int max;
        int index;

        public AsyncRange(int start, int count) {
            this.index = start;
            this.max = start + count;
        }

        public CompletableFuture<Void> next(Consumer<? super Integer> consumer) {
            int i = index;
            if (i == max) {
                return CompletableFuture.completedFuture(null);
            }
            index = i + 1;
            CompletableFuture<Void> cf = CompletableFuture
                 .runAsync(() -> consumer.accept(i));
            CompletableFuture<Void> cancel = new CompletableFuture<Void>() {
                @Override
                public boolean cancel(boolean mayInterruptIfRunning) {
                    cf.cancel(mayInterruptIfRunning);
                    return super.cancel(mayInterruptIfRunning);
                }
            };
            return cancel;
        }
    }

    @Test
    public void simple() {
        AsyncRange r = new AsyncRange(1, 10);

        new SomeAsyncApiBridge<Integer>(
                consumer -> r.next(consumer)
        )
        .test()
        .awaitDone(500, TimeUnit.SECONDS)
        .assertResult(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
    }
}

答案 1 :(得分:0)

使用Reactor Flux.create()这看起来很有用。我稍微更改了API。

public class FlowableGenerate4
{

    private static class API
    {
        private ExecutorService es = Executors.newFixedThreadPool(1);
        private CompletableFuture<Void> done = new CompletableFuture<>();

        private AtomicInteger stopCounter = new AtomicInteger(10);

        public boolean isDone()
        {
            return done.isDone();
        }

        public CompletableFuture<Void> getT(Consumer<Object> consumer)
        {

            es.submit(() -> {
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                }
                if (stopCounter.decrementAndGet() < 0)
                    done.complete(null);
                else
                    consumer.accept(new Object());
            });

            return done;
        }
    }

    private static class Callback implements Consumer<Object>
    {

        private API api;
        private FluxSink<Object> sink;

        public Callback(API api, FluxSink<Object> sink)
        {
            this.api = api;
            this.sink = sink;
        }

        @Override
        public void accept(Object o)
        {
            sink.next(o);
            if (sink.requestedFromDownstream() > 0 && !api.isDone())
                api.getT(this);
            else
                sink.currentContext().<AtomicBoolean>get("inProgress")
                    .set(false);
        }
    }

    private Publisher<Object> reactorPublisher()
    {

        API api = new API();

        return
            Flux.create(sink -> {

                sink.onRequest(n -> {

                    //if it's in progress already, do nothing
                    //I understand that onRequest() can be called asynchronously
                    //regardless if the previous call demand has been satisfied or not
                    if (!sink.currentContext().<AtomicBoolean>get("inProgress")
                        .compareAndSet(false, true))
                        return;

                    //else kick off calls to API
                    api.getT(new Callback(api, sink))
                       .whenComplete((o, t) -> {
                           if (t != null)
                               sink.error(t);
                           else
                               sink.complete();
                       });
                });
            }).subscriberContext(
                Context.empty().put("inProgress", new AtomicBoolean(false)));

    }

    @Test
    public void test()
    {
        Flowable.fromPublisher(reactorPublisher())
                .skip(5)
                .take(10)
                .blockingSubscribe(
                    i -> System.out.println("onNext()"),
                    Throwable::printStackTrace,
                    () -> System.out.println("onComplete()")
                );

    }

}