rsocket
This commit is contained in:
parent
03b8cfa579
commit
ba093e4c27
|
@ -53,9 +53,9 @@ public interface ChannelFactory {
|
||||||
String channelName) {
|
String channelName) {
|
||||||
var socketParameters = (RSocketParameters) channelsParameters;
|
var socketParameters = (RSocketParameters) channelsParameters;
|
||||||
if (socketParameters.isClient()) {
|
if (socketParameters.isClient()) {
|
||||||
return new RSocketConsumeAsClient<>(socketParameters.host(), channelCodec, channelName);
|
return new RSocketConsumeAsClient<>(socketParameters.channelHost(channelName), channelCodec, channelName);
|
||||||
} else {
|
} else {
|
||||||
return new RSocketConsumeAsServer<>(socketParameters.host(), channelCodec, channelName);
|
return new RSocketConsumeAsServer<>(socketParameters.channelHost(channelName), channelCodec, channelName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,9 +65,9 @@ public interface ChannelFactory {
|
||||||
String channelName) {
|
String channelName) {
|
||||||
var socketParameters = (RSocketParameters) channelsParameters;
|
var socketParameters = (RSocketParameters) channelsParameters;
|
||||||
if (socketParameters.isClient()) {
|
if (socketParameters.isClient()) {
|
||||||
return new RSocketProduceAsServer<>(socketParameters.host(), channelCodec, channelName);
|
return new RSocketProduceAsClient<>(socketParameters.channelHost(channelName), channelCodec, channelName);
|
||||||
} else {
|
} else {
|
||||||
return new RSocketProduceAsClient<>(socketParameters.host(), channelCodec, channelName);
|
return new RSocketProduceAsServer<>(socketParameters.channelHost(channelName), channelCodec, channelName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,11 +33,12 @@ public class ClientsSharedTdlib implements Closeable {
|
||||||
|
|
||||||
public ClientsSharedTdlib(TdlibChannelsClients tdClientsChannels) {
|
public ClientsSharedTdlib(TdlibChannelsClients tdClientsChannels) {
|
||||||
this.tdClientsChannels = tdClientsChannels;
|
this.tdClientsChannels = tdClientsChannels;
|
||||||
this.responses = tdClientsChannels.response().consumeMessages();
|
this.responses = tdClientsChannels.response().consumeMessages().repeat();
|
||||||
this.events = tdClientsChannels.events().entrySet().stream()
|
this.events = tdClientsChannels.events().entrySet().stream()
|
||||||
.collect(Collectors.toUnmodifiableMap(Entry::getKey, e -> e.getValue().consumeMessages()));
|
.collect(Collectors.toUnmodifiableMap(Entry::getKey, e -> e.getValue().consumeMessages().repeat()));
|
||||||
this.requestsSub = tdClientsChannels.request()
|
this.requestsSub = tdClientsChannels.request()
|
||||||
.sendMessages(requests.asFlux())
|
.sendMessages(requests.asFlux())
|
||||||
|
.repeat()
|
||||||
.subscribeOn(Schedulers.parallel())
|
.subscribeOn(Schedulers.parallel())
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,10 +31,35 @@ public final class RSocketParameters implements ChannelsParameters {
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HostAndPort host() {
|
public HostAndPort baseHost() {
|
||||||
return host;
|
return host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HostAndPort channelHost(String channelName) {
|
||||||
|
return switch (channelName) {
|
||||||
|
case "request" -> HostAndPort.fromParts(host.getHost(), host.getPort());
|
||||||
|
case "response" -> HostAndPort.fromParts(host.getHost(), host.getPort() + 1);
|
||||||
|
default -> {
|
||||||
|
if (channelName.startsWith("event-")) {
|
||||||
|
var lane = channelName.substring("event-".length());
|
||||||
|
int index;
|
||||||
|
if (lane.equals("main")) {
|
||||||
|
index = 0;
|
||||||
|
} else {
|
||||||
|
index = lanes.indexOf(lane);
|
||||||
|
if (index < 0) {
|
||||||
|
throw new IllegalArgumentException("Unknown lane: " + lane);
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
yield HostAndPort.fromParts(host.getHost(), host.getPort() + 2 + index);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("Unknown channel: " + channelName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj == this) {
|
if (obj == this) {
|
||||||
|
|
|
@ -29,9 +29,10 @@ public class TdlibChannelsSharedServer implements Closeable {
|
||||||
this.tdServersChannels = tdServersChannels;
|
this.tdServersChannels = tdServersChannels;
|
||||||
this.responsesSub = tdServersChannels.response()
|
this.responsesSub = tdServersChannels.response()
|
||||||
.sendMessages(responses.asFlux().log("responses", Level.FINEST, SignalType.ON_NEXT))
|
.sendMessages(responses.asFlux().log("responses", Level.FINEST, SignalType.ON_NEXT))
|
||||||
|
.repeat()
|
||||||
.subscribeOn(Schedulers.parallel())
|
.subscribeOn(Schedulers.parallel())
|
||||||
.subscribe();
|
.subscribe();
|
||||||
this.requests = tdServersChannels.request().consumeMessages();
|
this.requests = tdServersChannels.request().consumeMessages().repeat();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Flux<Timestamped<OnRequest<Object>>> requests() {
|
public Flux<Timestamped<OnRequest<Object>>> requests() {
|
||||||
|
@ -43,6 +44,7 @@ public class TdlibChannelsSharedServer implements Closeable {
|
||||||
public Disposable events(String lane, Flux<ClientBoundEvent> eventFlux) {
|
public Disposable events(String lane, Flux<ClientBoundEvent> eventFlux) {
|
||||||
return tdServersChannels.events(lane)
|
return tdServersChannels.events(lane)
|
||||||
.sendMessages(eventFlux)
|
.sendMessages(eventFlux)
|
||||||
|
.repeat()
|
||||||
.subscribeOn(Schedulers.parallel())
|
.subscribeOn(Schedulers.parallel())
|
||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,13 +13,17 @@ import it.tdlight.reactiveapi.ChannelCodec;
|
||||||
import it.tdlight.reactiveapi.EventConsumer;
|
import it.tdlight.reactiveapi.EventConsumer;
|
||||||
import it.tdlight.reactiveapi.RSocketParameters;
|
import it.tdlight.reactiveapi.RSocketParameters;
|
||||||
import it.tdlight.reactiveapi.Timestamped;
|
import it.tdlight.reactiveapi.Timestamped;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.apache.logging.log4j.LogManager;
|
import org.apache.logging.log4j.LogManager;
|
||||||
import org.apache.logging.log4j.Logger;
|
import org.apache.logging.log4j.Logger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
import reactor.core.publisher.SignalType;
|
||||||
import reactor.core.scheduler.Schedulers;
|
import reactor.core.scheduler.Schedulers;
|
||||||
|
import reactor.util.retry.Retry;
|
||||||
|
|
||||||
public class RSocketConsumeAsClient<T> implements EventConsumer<T> {
|
public class RSocketConsumeAsClient<T> implements EventConsumer<T> {
|
||||||
|
|
||||||
|
@ -52,9 +56,14 @@ public class RSocketConsumeAsClient<T> implements EventConsumer<T> {
|
||||||
var deserializer = channelCodec.getNewDeserializer();
|
var deserializer = channelCodec.getNewDeserializer();
|
||||||
return
|
return
|
||||||
RSocketConnector.create()
|
RSocketConnector.create()
|
||||||
.resume(new Resume())
|
//.resume(new Resume())
|
||||||
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
||||||
.connect(TcpClientTransport.create(host.getHost(), host.getPort()))
|
.connect(TcpClientTransport.create(host.getHost(), host.getPort()))
|
||||||
|
.retryWhen(Retry
|
||||||
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.maxBackoff(Duration.ofSeconds(16))
|
||||||
|
.jitter(1.0)
|
||||||
|
.doBeforeRetry(rs -> LOG.warn("Failed to bind, retrying. {}", rs)))
|
||||||
.flatMapMany(socket -> socket
|
.flatMapMany(socket -> socket
|
||||||
.requestStream(DefaultPayload.create("", "consume"))
|
.requestStream(DefaultPayload.create("", "consume"))
|
||||||
.map(payload -> {
|
.map(payload -> {
|
||||||
|
@ -64,12 +73,22 @@ public class RSocketConsumeAsClient<T> implements EventConsumer<T> {
|
||||||
//noinspection unchecked
|
//noinspection unchecked
|
||||||
return new Timestamped<T>(System.currentTimeMillis(), (T) deserializer.deserialize(null, data));
|
return new Timestamped<T>(System.currentTimeMillis(), (T) deserializer.deserialize(null, data));
|
||||||
})
|
})
|
||||||
.doFinally(signalType -> socket
|
.doFinally(signalType -> {
|
||||||
.fireAndForget(DefaultPayload.create("", "close"))
|
socket
|
||||||
.then(socket.onClose())
|
.fireAndForget(DefaultPayload.create("", "close"))
|
||||||
.doFinally(s -> socket.dispose())
|
.then(socket.onClose().timeout(Duration.ofSeconds(5), Mono.empty()))
|
||||||
.subscribeOn(Schedulers.parallel())
|
.doFinally(s -> socket.dispose())
|
||||||
.subscribe()))
|
.onErrorResume(ex -> Mono.empty())
|
||||||
|
.subscribeOn(Schedulers.parallel())
|
||||||
|
.subscribe();
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.retryWhen(Retry
|
||||||
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.filter(ex -> ex instanceof ClosedChannelException)
|
||||||
|
.maxBackoff(Duration.ofSeconds(16))
|
||||||
|
.jitter(1.0)
|
||||||
|
.doBeforeRetry(rs -> LOG.warn("Failed to communicate, retrying. {}", rs)))
|
||||||
.log("RSOCKET_CONSUMER_CLIENT", Level.FINE);
|
.log("RSOCKET_CONSUMER_CLIENT", Level.FINE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,7 @@ import it.tdlight.reactiveapi.ChannelCodec;
|
||||||
import it.tdlight.reactiveapi.EventConsumer;
|
import it.tdlight.reactiveapi.EventConsumer;
|
||||||
import it.tdlight.reactiveapi.RSocketParameters;
|
import it.tdlight.reactiveapi.RSocketParameters;
|
||||||
import it.tdlight.reactiveapi.Timestamped;
|
import it.tdlight.reactiveapi.Timestamped;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
|
@ -30,6 +31,7 @@ import org.apache.logging.log4j.Logger;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.reactivestreams.Subscription;
|
import org.reactivestreams.Subscription;
|
||||||
import reactor.core.CoreSubscriber;
|
import reactor.core.CoreSubscriber;
|
||||||
|
import reactor.core.Disposable;
|
||||||
import reactor.core.publisher.BaseSubscriber;
|
import reactor.core.publisher.BaseSubscriber;
|
||||||
import reactor.core.publisher.Flux;
|
import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
|
@ -73,7 +75,7 @@ public class RSocketConsumeAsServer<T> implements EventConsumer<T> {
|
||||||
return Mono
|
return Mono
|
||||||
.<Tuple3<CloseableChannel, RSocket, Flux<Timestamped<T>>>>create(sink -> {
|
.<Tuple3<CloseableChannel, RSocket, Flux<Timestamped<T>>>>create(sink -> {
|
||||||
AtomicReference<CloseableChannel> serverRef = new AtomicReference<>();
|
AtomicReference<CloseableChannel> serverRef = new AtomicReference<>();
|
||||||
var server = RSocketServer
|
var disposable = RSocketServer
|
||||||
.create((setup, in) -> {
|
.create((setup, in) -> {
|
||||||
var inRawFlux = in.requestStream(DefaultPayload.create("", "consume"));
|
var inRawFlux = in.requestStream(DefaultPayload.create("", "consume"));
|
||||||
var inFlux = inRawFlux.map(payload -> {
|
var inFlux = inRawFlux.map(payload -> {
|
||||||
|
@ -87,16 +89,30 @@ public class RSocketConsumeAsServer<T> implements EventConsumer<T> {
|
||||||
return Mono.just(new RSocket() {});
|
return Mono.just(new RSocket() {});
|
||||||
})
|
})
|
||||||
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
||||||
.resume(new Resume())
|
//.resume(new Resume())
|
||||||
.bindNow(TcpServerTransport.create(host.getHost(), host.getPort()));
|
.bind(TcpServerTransport.create(host.getHost(), host.getPort()))
|
||||||
serverRef.set(server);
|
.retryWhen(Retry
|
||||||
sink.onCancel(server);
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.maxBackoff(Duration.ofSeconds(16))
|
||||||
|
.jitter(1.0)
|
||||||
|
.doBeforeRetry(rs -> LOG.warn("Failed to bind, retrying. {}", rs)))
|
||||||
|
.subscribe(server -> {
|
||||||
|
serverRef.set(server);
|
||||||
|
sink.onCancel(server);
|
||||||
|
});
|
||||||
|
sink.onDispose(disposable);
|
||||||
})
|
})
|
||||||
.subscribeOn(Schedulers.boundedElastic())
|
.subscribeOn(Schedulers.boundedElastic())
|
||||||
.flatMapMany(t -> t.getT3().doFinally(s -> {
|
.flatMapMany(t -> t.getT3().doFinally(s -> {
|
||||||
t.getT2().dispose();
|
t.getT2().dispose();
|
||||||
t.getT1().dispose();
|
t.getT1().dispose();
|
||||||
}))
|
}))
|
||||||
|
.retryWhen(Retry
|
||||||
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.filter(ex -> ex instanceof ClosedChannelException)
|
||||||
|
.maxBackoff(Duration.ofSeconds(16))
|
||||||
|
.jitter(1.0)
|
||||||
|
.doBeforeRetry(rs -> LOG.warn("Failed to communicate, retrying. {}", rs)))
|
||||||
.log("RSOCKET_CONSUMER_SERVER", Level.FINE);
|
.log("RSOCKET_CONSUMER_SERVER", Level.FINE);
|
||||||
}
|
}
|
||||||
/*return Flux.defer(() -> {
|
/*return Flux.defer(() -> {
|
||||||
|
|
|
@ -16,6 +16,7 @@ import it.tdlight.reactiveapi.EventProducer;
|
||||||
import it.tdlight.reactiveapi.RSocketParameters;
|
import it.tdlight.reactiveapi.RSocketParameters;
|
||||||
import it.tdlight.reactiveapi.ReactorUtils;
|
import it.tdlight.reactiveapi.ReactorUtils;
|
||||||
import it.tdlight.reactiveapi.Timestamped;
|
import it.tdlight.reactiveapi.Timestamped;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
@ -66,9 +67,21 @@ public final class RSocketProduceAsClient<K> implements EventProducer<K> {
|
||||||
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
||||||
.setupPayload(DefaultPayload.create("", "connect"))
|
.setupPayload(DefaultPayload.create("", "connect"))
|
||||||
.acceptor(SocketAcceptor.forRequestStream(payload -> serializedEventsFlux))
|
.acceptor(SocketAcceptor.forRequestStream(payload -> serializedEventsFlux))
|
||||||
|
//.resume(new Resume())
|
||||||
.connect(TcpClientTransport.create(host.getHost(), host.getPort()))
|
.connect(TcpClientTransport.create(host.getHost(), host.getPort()))
|
||||||
|
.retryWhen(Retry
|
||||||
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.maxBackoff(Duration.ofSeconds(16))
|
||||||
|
.jitter(1.0)
|
||||||
|
.doBeforeRetry(rs -> LOG.warn("Failed to bind, retrying. {}", rs)))
|
||||||
.flatMap(rSocket -> rSocket.onClose()
|
.flatMap(rSocket -> rSocket.onClose()
|
||||||
.takeUntilOther(closeRequest.asMono().doFinally(s -> rSocket.dispose())))
|
.takeUntilOther(closeRequest.asMono().doFinally(s -> rSocket.dispose())))
|
||||||
|
.retryWhen(Retry
|
||||||
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.filter(ex -> ex instanceof ClosedChannelException)
|
||||||
|
.maxBackoff(Duration.ofSeconds(16))
|
||||||
|
.jitter(1.0)
|
||||||
|
.doBeforeRetry(rs -> LOG.warn("Failed to communicate, retrying. {}", rs)))
|
||||||
.log("RSOCKET_PRODUCER_CLIENT_Y", Level.FINE);
|
.log("RSOCKET_PRODUCER_CLIENT_Y", Level.FINE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,9 @@ import io.rsocket.util.DefaultPayload;
|
||||||
import it.tdlight.reactiveapi.ChannelCodec;
|
import it.tdlight.reactiveapi.ChannelCodec;
|
||||||
import it.tdlight.reactiveapi.EventProducer;
|
import it.tdlight.reactiveapi.EventProducer;
|
||||||
import it.tdlight.reactiveapi.ReactorUtils;
|
import it.tdlight.reactiveapi.ReactorUtils;
|
||||||
|
import java.nio.channels.ClosedChannelException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import org.apache.kafka.common.serialization.Serializer;
|
import org.apache.kafka.common.serialization.Serializer;
|
||||||
|
@ -26,6 +28,7 @@ import reactor.core.publisher.Flux;
|
||||||
import reactor.core.publisher.Mono;
|
import reactor.core.publisher.Mono;
|
||||||
import reactor.core.publisher.Sinks;
|
import reactor.core.publisher.Sinks;
|
||||||
import reactor.core.publisher.Sinks.Empty;
|
import reactor.core.publisher.Sinks.Empty;
|
||||||
|
import reactor.util.retry.Retry;
|
||||||
|
|
||||||
public final class RSocketProduceAsServer<K> implements EventProducer<K> {
|
public final class RSocketProduceAsServer<K> implements EventProducer<K> {
|
||||||
|
|
||||||
|
@ -84,12 +87,23 @@ public final class RSocketProduceAsServer<K> implements EventProducer<K> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.resume(new Resume())
|
//.resume(new Resume())
|
||||||
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
.payloadDecoder(PayloadDecoder.ZERO_COPY)
|
||||||
.bind(TcpServerTransport.create(host.getHost(), host.getPort()))
|
.bind(TcpServerTransport.create(host.getHost(), host.getPort()))
|
||||||
|
.retryWhen(Retry
|
||||||
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.maxBackoff(Duration.ofSeconds(16))
|
||||||
|
.jitter(1.0)
|
||||||
|
.doBeforeRetry(rs -> LOG.warn("Failed to bind, retrying. {}", rs)))
|
||||||
.doOnNext(serverRef::set)
|
.doOnNext(serverRef::set)
|
||||||
.flatMap(closeableChannel -> closeableChannel.onClose()
|
.flatMap(closeableChannel -> closeableChannel.onClose()
|
||||||
.takeUntilOther(closeRequest.asMono().doFinally(s -> closeableChannel.dispose())));
|
.takeUntilOther(closeRequest.asMono().doFinally(s -> closeableChannel.dispose())))
|
||||||
|
.retryWhen(Retry
|
||||||
|
.backoff(Long.MAX_VALUE, Duration.ofSeconds(1))
|
||||||
|
.filter(ex -> ex instanceof ClosedChannelException)
|
||||||
|
.maxBackoff(Duration.ofSeconds(16))
|
||||||
|
.jitter(1.0)
|
||||||
|
.doBeforeRetry(rs -> LOG.warn("Failed to communicate, retrying. {}", rs)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,7 +177,6 @@ public abstract class TestChannel {
|
||||||
.map(Integer::parseUnsignedInt)
|
.map(Integer::parseUnsignedInt)
|
||||||
.take(10, true)
|
.take(10, true)
|
||||||
.collect(Collectors.toCollection(IntArrayList::new));
|
.collect(Collectors.toCollection(IntArrayList::new));
|
||||||
var receiverWait = Flux.<IntArrayList>empty().delaySubscription(Duration.ofSeconds(4));
|
|
||||||
var receiver2 = consumer
|
var receiver2 = consumer
|
||||||
.consumeMessages()
|
.consumeMessages()
|
||||||
.limitRate(1)
|
.limitRate(1)
|
||||||
|
@ -187,7 +186,7 @@ public abstract class TestChannel {
|
||||||
Flux<IntArrayList> part1 = Flux
|
Flux<IntArrayList> part1 = Flux
|
||||||
.merge((isServerSender() ? sender : receiver1), isServerSender() ? receiver1 : sender);
|
.merge((isServerSender() ? sender : receiver1), isServerSender() ? receiver1 : sender);
|
||||||
Flux<IntArrayList> part2 = Flux
|
Flux<IntArrayList> part2 = Flux
|
||||||
.merge((isServerSender() ? sender : receiver2), receiverWait.then(isServerSender() ? receiver2 : sender));
|
.merge((isServerSender() ? sender : receiver2), isServerSender() ? receiver2 : sender);
|
||||||
var response = Flux.concat(part1, part2).reduce((a, b) -> {
|
var response = Flux.concat(part1, part2).reduce((a, b) -> {
|
||||||
a.addAll(b);
|
a.addAll(b);
|
||||||
return a;
|
return a;
|
||||||
|
|
Loading…
Reference in New Issue