Search in sources :

Example 1 with DropOperator

use of io.reactivx.mantis.operators.DropOperator in project mantis by Netflix.

the class WorkerMetricHandler method start.

private void start() {
    final AtomicReference<List<Subscription>> ref = new AtomicReference<>(new ArrayList<>());
    masterClientApi.schedulingChanges(jobId).doOnNext(jobSchedulingInfo -> {
        final Map<Integer, WorkerAssignments> workerAssignments = jobSchedulingInfo.getWorkerAssignments();
        for (Map.Entry<Integer, WorkerAssignments> workerAssignmentsEntry : workerAssignments.entrySet()) {
            final WorkerAssignments workerAssignment = workerAssignmentsEntry.getValue();
            logger.debug("setting numWorkers={} for stage={}", workerAssignment.getNumWorkers(), workerAssignment.getStage());
            numWorkersByStage.put(workerAssignment.getStage(), workerAssignment.getNumWorkers());
            workerHostsByStage.put(workerAssignment.getStage(), new ArrayList<>(workerAssignment.getHosts().values()));
        }
    }).subscribe();
    logger.info("Starting worker metric handler with autoscale config {}", autoScaleMetricsConfig);
    metricDataSubject.groupBy(metricData -> metricData.getStage()).lift(new DropOperator<>(WorkerMetricHandler.class.getName())).doOnNext(go -> {
        final Integer stage = go.getKey();
        final Subscription s = go.lift(new StageMetricDataOperator(stage, lookupNumWorkersByStage, autoScaleMetricsConfig)).subscribe();
        logger.info("adding subscription for stage {} StageMetricDataOperator", stage);
        ref.get().add(s);
    }).doOnUnsubscribe(() -> {
        for (Subscription s : ref.get()) s.unsubscribe();
    }).subscribe();
}
Also used : MantisMasterClientApi(io.mantisrx.server.master.client.MantisMasterClientApi) LoggerFactory(org.slf4j.LoggerFactory) HashMap(java.util.HashMap) Cache(io.mantisrx.shaded.com.google.common.cache.Cache) Action1(rx.functions.Action1) AtomicReference(java.util.concurrent.atomic.AtomicReference) DropOperator(io.reactivx.mantis.operators.DropOperator) ArrayList(java.util.ArrayList) ConcurrentMap(java.util.concurrent.ConcurrentMap) Observable(rx.Observable) Func1(rx.functions.Func1) Matcher(java.util.regex.Matcher) Map(java.util.Map) Schedulers(rx.schedulers.Schedulers) CacheBuilder(io.mantisrx.shaded.com.google.common.cache.CacheBuilder) SerializedObserver(rx.observers.SerializedObserver) Logger(org.slf4j.Logger) Subscriber(rx.Subscriber) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) Set(java.util.Set) Observer(rx.Observer) Collectors(java.util.stream.Collectors) TimeUnit(java.util.concurrent.TimeUnit) List(java.util.List) io.mantisrx.server.core(io.mantisrx.server.core) Action0(rx.functions.Action0) MetricStringConstants(io.mantisrx.server.core.stats.MetricStringConstants) StageScalingPolicy(io.mantisrx.runtime.descriptor.StageScalingPolicy) Pattern(java.util.regex.Pattern) Subscription(rx.Subscription) PublishSubject(rx.subjects.PublishSubject) DropOperator(io.reactivx.mantis.operators.DropOperator) ArrayList(java.util.ArrayList) AtomicReference(java.util.concurrent.atomic.AtomicReference) ArrayList(java.util.ArrayList) List(java.util.List) Subscription(rx.Subscription) HashMap(java.util.HashMap) ConcurrentMap(java.util.concurrent.ConcurrentMap) Map(java.util.Map) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap)

Example 2 with DropOperator

use of io.reactivx.mantis.operators.DropOperator in project mantis by Netflix.

the class RemoteObservable method createTcpConnectionToServer.

private static <K, V> Observable<GroupedObservable<K, V>> createTcpConnectionToServer(final ConnectToGroupedObservable<K, V> params, final RemoteUnsubscribe remoteUnsubscribe, final RxMetrics metrics, final Action0 connectionDisconnectCallback, Observable<Integer> closeTrigger) {
    final Decoder<K> keyDecoder = params.getKeyDecoder();
    final Decoder<V> valueDecoder = params.getValueDecoder();
    loadFastProperties();
    return RxNetty.createTcpClient(params.getHost(), params.getPort(), new PipelineConfiguratorComposite<RemoteRxEvent, List<RemoteRxEvent>>(new PipelineConfigurator<RemoteRxEvent, List<RemoteRxEvent>>() {

        @Override
        public void configureNewPipeline(ChannelPipeline pipeline) {
            if (enableNettyLogging) {
                // uncomment to enable debug logging
                pipeline.addFirst(new LoggingHandler(LogLevel.ERROR));
            }
            if (enableHeartBeating) {
                pipeline.addLast("idleStateHandler", new IdleStateHandler(10, 2, 0));
                pipeline.addLast("heartbeat", new HeartbeatHandler());
            }
            if (enableCompression) {
                pipeline.addLast("gzipInflater", new JdkZlibEncoder(ZlibWrapper.GZIP));
                pipeline.addLast("gzipDeflater", new JdkZlibDecoder(ZlibWrapper.GZIP));
            }
            // 4 bytes to encode length
            pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
            // max frame = half MB
            pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(maxFrameLength, 0, 4, 0, 4));
        }
    }, new BatchedRxEventPipelineConfigurator())).connect().flatMap(new Func1<ObservableConnection<RemoteRxEvent, List<RemoteRxEvent>>, Observable<RemoteRxEvent>>() {

        @Override
        public Observable<RemoteRxEvent> call(final ObservableConnection<RemoteRxEvent, List<RemoteRxEvent>> connection) {
            // send subscribe event to server
            connection.writeAndFlush(RemoteRxEvent.subscribed(params.getName(), params.getSubscribeParameters()));
            remoteUnsubscribe.setConnection(connection);
            return connection.getInput().lift(new DropOperator<RemoteRxEvent>("incoming_" + RemoteObservable.class.getCanonicalName() + "_createTcpConnectionToServerGroups"));
        }
    }).doOnCompleted(new Action0() {

        @Override
        public void call() {
            // connection completed
            logger.warn("Detected connection completed when trying to connect to host: " + params.getHost() + " port: " + params.getPort());
            connectionDisconnectCallback.call();
        }
    }).onErrorResumeNext(new Func1<Throwable, Observable<RemoteRxEvent>>() {

        @Override
        public Observable<RemoteRxEvent> call(Throwable t1) {
            logger.warn("Detected connection error when trying to connect to host: " + params.getHost() + " port: " + params.getPort(), t1);
            connectionDisconnectCallback.call();
            // complete if error occurs
            return Observable.empty();
        }
    }).takeUntil(closeTrigger).map(new Func1<RemoteRxEvent, Notification<byte[]>>() {

        @Override
        public Notification<byte[]> call(RemoteRxEvent rxEvent) {
            if (rxEvent.getType() == RemoteRxEvent.Type.next) {
                metrics.incrementNextCount();
                return Notification.createOnNext(rxEvent.getData());
            } else if (rxEvent.getType() == RemoteRxEvent.Type.error) {
                metrics.incrementErrorCount();
                return Notification.createOnError(fromBytesToThrowable(rxEvent.getData()));
            } else if (rxEvent.getType() == RemoteRxEvent.Type.completed) {
                metrics.incrementCompletedCount();
                return Notification.createOnCompleted();
            } else {
                throw new RuntimeException("RemoteRxEvent of type:" + rxEvent.getType() + ", not supported.");
            }
        }
    }).<byte[]>dematerialize().groupBy(new Func1<byte[], K>() {

        @Override
        public K call(byte[] bytes) {
            ByteBuffer buff = ByteBuffer.wrap((byte[]) bytes);
            // ignore notification type in key selector
            buff.get();
            int keyLength = buff.getInt();
            byte[] key = new byte[keyLength];
            buff.get(key);
            return keyDecoder.decode(key);
        }
    }, new Func1<byte[], Notification<V>>() {

        @Override
        public Notification<V> call(byte[] bytes) {
            ByteBuffer buff = ByteBuffer.wrap((byte[]) bytes);
            byte notificationType = buff.get();
            if (notificationType == 1) {
                int keyLength = buff.getInt();
                int end = buff.limit();
                int dataLength = end - 4 - 1 - keyLength;
                byte[] valueBytes = new byte[dataLength];
                buff.position(4 + 1 + keyLength);
                buff.get(valueBytes, 0, dataLength);
                V value = valueDecoder.decode(valueBytes);
                return Notification.createOnNext(value);
            } else if (notificationType == 2) {
                return Notification.createOnCompleted();
            } else if (notificationType == 3) {
                int keyLength = buff.getInt();
                int end = buff.limit();
                int dataLength = end - 4 - 1 - keyLength;
                byte[] errorBytes = new byte[dataLength];
                buff.position(4 + 1 + keyLength);
                buff.get(errorBytes, 0, dataLength);
                return Notification.createOnError(fromBytesToThrowable(errorBytes));
            } else {
                throw new RuntimeException("Notification encoding not support: " + notificationType);
            }
        }
    }).map(new Func1<GroupedObservable<K, Notification<V>>, GroupedObservable<K, V>>() {

        @Override
        public GroupedObservable<K, V> call(GroupedObservable<K, Notification<V>> group) {
            return GroupedObservableUtils.createGroupedObservable(group.getKey(), group.<V>dematerialize());
        }
    }).doOnEach(new Observer<GroupedObservable<K, V>>() {

        @Override
        public void onCompleted() {
            logger.info("RemoteRxEvent, name: {} onCompleted()", params.getName());
        }

        @Override
        public void onError(Throwable e) {
            logger.error("RemoteRxEvent, name: {} onError()", params.getName(), e);
        }

        @Override
        public void onNext(GroupedObservable<K, V> group) {
            if (logger.isDebugEnabled()) {
                logger.debug("RemoteRxEvent, name: {} new key: {}", params.getName(), group.getKey());
            }
        }
    });
}
Also used : LoggingHandler(io.netty.handler.logging.LoggingHandler) DropOperator(io.reactivx.mantis.operators.DropOperator) LengthFieldPrepender(io.netty.handler.codec.LengthFieldPrepender) Notification(rx.Notification) List(java.util.List) JdkZlibEncoder(io.netty.handler.codec.compression.JdkZlibEncoder) Func1(rx.functions.Func1) LengthFieldBasedFrameDecoder(io.netty.handler.codec.LengthFieldBasedFrameDecoder) Action0(rx.functions.Action0) ObservableConnection(mantis.io.reactivex.netty.channel.ObservableConnection) ByteBuffer(java.nio.ByteBuffer) ChannelPipeline(io.netty.channel.ChannelPipeline) Observable(rx.Observable) GroupedObservable(rx.observables.GroupedObservable) PipelineConfigurator(mantis.io.reactivex.netty.pipeline.PipelineConfigurator) JdkZlibDecoder(io.netty.handler.codec.compression.JdkZlibDecoder) IdleStateHandler(io.netty.handler.timeout.IdleStateHandler) GroupedObservable(rx.observables.GroupedObservable)

Example 3 with DropOperator

use of io.reactivx.mantis.operators.DropOperator in project mantis by Netflix.

the class RemoteObservable method createTcpConnectionToGOServer.

private static <K, V> Observable<MantisGroup<K, V>> createTcpConnectionToGOServer(final ConnectToGroupedObservable<K, V> params, final RemoteUnsubscribe remoteUnsubscribe, final RxMetrics metrics, final Action0 connectionDisconnectCallback, Observable<Integer> closeTrigger, final SpscArrayQueue<MantisGroup<?, ?>> inputQueue) {
    final Decoder<K> keyDecoder = params.getKeyDecoder();
    final Decoder<V> valueDecoder = params.getValueDecoder();
    loadFastProperties();
    return RxNetty.createTcpClient(params.getHost(), params.getPort(), new PipelineConfiguratorComposite<RemoteRxEvent, List<RemoteRxEvent>>(new PipelineConfigurator<RemoteRxEvent, List<RemoteRxEvent>>() {

        @Override
        public void configureNewPipeline(ChannelPipeline pipeline) {
            if (enableNettyLogging) {
                // uncomment to enable debug logging
                pipeline.addFirst(new LoggingHandler(LogLevel.ERROR));
            }
            if (enableHeartBeating) {
                pipeline.addLast("idleStateHandler", new IdleStateHandler(10, 2, 0));
                pipeline.addLast("heartbeat", new HeartbeatHandler());
            }
            if (enableCompression) {
                pipeline.addLast("gzipInflater", new JdkZlibEncoder(ZlibWrapper.GZIP));
                pipeline.addLast("gzipDeflater", new JdkZlibDecoder(ZlibWrapper.GZIP));
            }
            // 4 bytes to encode length
            pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
            // max frame = half MB
            pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(maxFrameLength, 0, 4, 0, 4));
        }
    }, new BatchedRxEventPipelineConfigurator())).connect().flatMap(new Func1<ObservableConnection<RemoteRxEvent, List<RemoteRxEvent>>, Observable<RemoteRxEvent>>() {

        @Override
        public Observable<RemoteRxEvent> call(final ObservableConnection<RemoteRxEvent, List<RemoteRxEvent>> connection) {
            // send subscribe event to server
            connection.writeAndFlush(RemoteRxEvent.subscribed(params.getName(), params.getSubscribeParameters()));
            remoteUnsubscribe.setConnection(connection);
            return connection.getInput().lift(new DropOperator<RemoteRxEvent>("incoming_" + RemoteObservable.class.getCanonicalName() + "_createTcpConnectionToServerGroups"));
        }
    }).doOnCompleted(new Action0() {

        @Override
        public void call() {
            // connection completed
            logger.warn("Detected connection completed when trying to connect to host: " + params.getHost() + " port: " + params.getPort());
            connectionDisconnectCallback.call();
        }
    }).onErrorResumeNext(new Func1<Throwable, Observable<RemoteRxEvent>>() {

        @Override
        public Observable<RemoteRxEvent> call(Throwable t1) {
            logger.warn("Detected connection error when trying to connect to host: " + params.getHost() + " port: " + params.getPort(), t1);
            connectionDisconnectCallback.call();
            // complete if error occurs
            return Observable.empty();
        }
    }).takeUntil(closeTrigger).filter(new Func1<RemoteRxEvent, Boolean>() {

        @Override
        public Boolean call(RemoteRxEvent rxEvent) {
            return (rxEvent.getType() == RemoteRxEvent.Type.next);
        }
    }).map(new Func1<RemoteRxEvent, Notification<byte[]>>() {

        @Override
        public Notification<byte[]> call(RemoteRxEvent rxEvent) {
            metrics.incrementNextCount();
            return Notification.createOnNext(rxEvent.getData());
        }
    }).<byte[]>dematerialize().map(new Func1<byte[], MantisGroup<K, V>>() {

        @Override
        public MantisGroup<K, V> call(byte[] bytes) {
            ByteBuffer buff = ByteBuffer.wrap((byte[]) bytes);
            // ignore notification type in key selector
            buff.get();
            int keyLength = buff.getInt();
            byte[] key = new byte[keyLength];
            buff.get(key);
            K keyVal = keyDecoder.decode(key);
            V value = null;
            buff = ByteBuffer.wrap((byte[]) bytes);
            byte notificationType = buff.get();
            if (notificationType == 1) {
                // int keyLength = buff.getInt();
                int end = buff.limit();
                int dataLength = end - 4 - 1 - keyLength;
                byte[] valueBytes = new byte[dataLength];
                buff.position(4 + 1 + keyLength);
                buff.get(valueBytes, 0, dataLength);
                value = valueDecoder.decode(valueBytes);
            } else {
                throw new RuntimeException("Notification encoding not support: " + notificationType);
            }
            return new MantisGroup<K, V>(keyVal, value);
        }
    }).doOnEach(new Observer<MantisGroup<K, V>>() {

        @Override
        public void onCompleted() {
            logger.info("RemoteRxEvent, name: " + params.getName() + " onCompleted()");
        }

        @Override
        public void onError(Throwable e) {
            logger.error("RemoteRxEvent, name: " + params.getName() + " onError()", e);
        }

        @Override
        public void onNext(MantisGroup<K, V> group) {
            if (logger.isDebugEnabled()) {
                logger.debug("RemoteRxEvent, name: " + params.getName() + " new key: " + group.getKeyValue());
            }
        }
    });
}
Also used : LoggingHandler(io.netty.handler.logging.LoggingHandler) DropOperator(io.reactivx.mantis.operators.DropOperator) LengthFieldPrepender(io.netty.handler.codec.LengthFieldPrepender) List(java.util.List) JdkZlibEncoder(io.netty.handler.codec.compression.JdkZlibEncoder) Func1(rx.functions.Func1) LengthFieldBasedFrameDecoder(io.netty.handler.codec.LengthFieldBasedFrameDecoder) Action0(rx.functions.Action0) ObservableConnection(mantis.io.reactivex.netty.channel.ObservableConnection) MantisGroup(io.mantisrx.common.MantisGroup) ByteBuffer(java.nio.ByteBuffer) ChannelPipeline(io.netty.channel.ChannelPipeline) Observable(rx.Observable) GroupedObservable(rx.observables.GroupedObservable) PipelineConfigurator(mantis.io.reactivex.netty.pipeline.PipelineConfigurator) JdkZlibDecoder(io.netty.handler.codec.compression.JdkZlibDecoder) IdleStateHandler(io.netty.handler.timeout.IdleStateHandler)

Example 4 with DropOperator

use of io.reactivx.mantis.operators.DropOperator in project mantis by Netflix.

the class RemoteObservable method createTcpConnectionToServer.

private static <T> Observable<T> createTcpConnectionToServer(final ConnectToObservable<T> params, final RemoteUnsubscribe remoteUnsubscribe, final RxMetrics metrics, final Action0 connectionDisconnectCallback, Observable<Integer> closeTrigger) {
    final Decoder<T> decoder = params.getDecoder();
    loadFastProperties();
    return RxNetty.createTcpClient(params.getHost(), params.getPort(), new PipelineConfiguratorComposite<RemoteRxEvent, List<RemoteRxEvent>>(new PipelineConfigurator<RemoteRxEvent, List<RemoteRxEvent>>() {

        @Override
        public void configureNewPipeline(ChannelPipeline pipeline) {
            if (enableNettyLogging) {
                // uncomment to enable debug logging
                pipeline.addFirst(new LoggingHandler(LogLevel.ERROR));
            }
            if (enableHeartBeating) {
                pipeline.addLast("idleStateHandler", new IdleStateHandler(10, 2, 0));
                pipeline.addLast("heartbeat", new HeartbeatHandler());
            }
            if (enableCompression) {
                pipeline.addLast("gzipInflater", new JdkZlibEncoder(ZlibWrapper.GZIP));
                pipeline.addLast("gzipDeflater", new JdkZlibDecoder(ZlibWrapper.GZIP));
            }
            // 4 bytes to encode length
            pipeline.addLast("frameEncoder", new LengthFieldPrepender(4));
            // max frame = half MB
            pipeline.addLast("frameDecoder", new LengthFieldBasedFrameDecoder(maxFrameLength, 0, 4, 0, 4));
        }
    }, new BatchedRxEventPipelineConfigurator())).connect().flatMap(new Func1<ObservableConnection<RemoteRxEvent, List<RemoteRxEvent>>, Observable<RemoteRxEvent>>() {

        @Override
        public Observable<RemoteRxEvent> call(final ObservableConnection<RemoteRxEvent, List<RemoteRxEvent>> connection) {
            // send subscribe event to server
            connection.writeAndFlush(RemoteRxEvent.subscribed(params.getName(), params.getSubscribeParameters()));
            remoteUnsubscribe.setConnection(connection);
            return connection.getInput().lift(new DropOperator<RemoteRxEvent>("incoming_" + RemoteObservable.class.getCanonicalName() + "_createTcpConnectionToServer"));
        }
    }).doOnCompleted(new Action0() {

        @Override
        public void call() {
            // connection completed
            logger.warn("Detected connection completed when trying to connect to host: " + params.getHost() + " port: " + params.getPort());
            connectionDisconnectCallback.call();
        }
    }).onErrorResumeNext(new Func1<Throwable, Observable<RemoteRxEvent>>() {

        @Override
        public Observable<RemoteRxEvent> call(Throwable t1) {
            logger.warn("Detected connection error when trying to connect to host: " + params.getHost() + " port: " + params.getPort(), t1);
            connectionDisconnectCallback.call();
            // complete if error occurs
            return Observable.empty();
        }
    }).takeUntil(closeTrigger).map(new Func1<RemoteRxEvent, Notification<T>>() {

        @Override
        public Notification<T> call(RemoteRxEvent rxEvent) {
            if (rxEvent.getType() == RemoteRxEvent.Type.next) {
                metrics.incrementNextCount();
                return Notification.createOnNext(decoder.decode(rxEvent.getData()));
            } else if (rxEvent.getType() == RemoteRxEvent.Type.error) {
                metrics.incrementErrorCount();
                return Notification.createOnError(fromBytesToThrowable(rxEvent.getData()));
            } else if (rxEvent.getType() == RemoteRxEvent.Type.completed) {
                metrics.incrementCompletedCount();
                return Notification.createOnCompleted();
            } else {
                throw new RuntimeException("RemoteRxEvent of type: " + rxEvent.getType() + ", not supported.");
            }
        }
    }).<T>dematerialize().doOnEach(new Observer<T>() {

        @Override
        public void onCompleted() {
            logger.info("RemoteRxEvent: " + params.getName() + " onCompleted()");
        }

        @Override
        public void onError(Throwable e) {
            logger.error("RemoteRxEvent: " + params.getName() + " onError()", e);
        }

        @Override
        public void onNext(T t) {
            if (logger.isDebugEnabled()) {
                logger.debug("RemoteRxEvent: " + params.getName() + " onNext(): " + t);
            }
        }
    });
}
Also used : LoggingHandler(io.netty.handler.logging.LoggingHandler) DropOperator(io.reactivx.mantis.operators.DropOperator) LengthFieldPrepender(io.netty.handler.codec.LengthFieldPrepender) Notification(rx.Notification) List(java.util.List) JdkZlibEncoder(io.netty.handler.codec.compression.JdkZlibEncoder) LengthFieldBasedFrameDecoder(io.netty.handler.codec.LengthFieldBasedFrameDecoder) Action0(rx.functions.Action0) ObservableConnection(mantis.io.reactivex.netty.channel.ObservableConnection) ChannelPipeline(io.netty.channel.ChannelPipeline) Observable(rx.Observable) GroupedObservable(rx.observables.GroupedObservable) PipelineConfigurator(mantis.io.reactivex.netty.pipeline.PipelineConfigurator) JdkZlibDecoder(io.netty.handler.codec.compression.JdkZlibDecoder) IdleStateHandler(io.netty.handler.timeout.IdleStateHandler)

Example 5 with DropOperator

use of io.reactivx.mantis.operators.DropOperator in project mantis by Netflix.

the class ServerSentEventRequestHandler method handle.

@Override
public Observable<Void> handle(HttpServerRequest<ByteBuf> request, final HttpServerResponse<ServerSentEvent> response) {
    InetSocketAddress socketAddress = (InetSocketAddress) response.getChannel().remoteAddress();
    LOG.info("HTTP SSE connection received from " + socketAddress.getAddress() + ":" + socketAddress.getPort() + "  queryParams: " + request.getQueryParameters());
    final String socketAddrStr = socketAddress.getAddress().toString();
    final WritableEndpoint<String> sn = new WritableEndpoint<>(socketAddress.getHostString(), socketAddress.getPort(), Endpoint.uniqueHost(socketAddress.getHostString(), socketAddress.getPort(), null));
    final Map<String, List<String>> queryParameters = request.getQueryParameters();
    final SlotAssignmentManager<String> slotMgr = ssm.registerServer(sn, queryParameters);
    final AtomicLong lastResponseFlush = new AtomicLong();
    lastResponseFlush.set(-1);
    final AtomicLong lastResponseSent = new AtomicLong(-1);
    // copy reference, then apply request specific filters, sampling
    Observable<T> requestObservable = observableToServe;
    // decouple the observable on a separate thread and add backpressure handling
    // ServiceRegistry.INSTANCE.getPropertiesService().getStringValue("sse.decouple", "false");
    String decoupleSSE = "false";
    if ("true".equals(decoupleSSE)) {
        final BasicTag sockAddrTag = new BasicTag("sockAddr", Optional.ofNullable(socketAddrStr).orElse("none"));
        requestObservable = requestObservable.lift(new DropOperator<T>("outgoing_ServerSentEventRequestHandler", sockAddrTag)).observeOn(Schedulers.io());
    }
    response.getHeaders().set("Access-Control-Allow-Origin", "*");
    response.getHeaders().set("content-type", "text/event-stream");
    response.getHeaders().set("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate");
    response.getHeaders().set("Pragma", "no-cache");
    response.flush();
    String uniqueClientId = socketAddrStr;
    if (queryParameters != null && queryParameters.containsKey(CLIENT_ID_PARAM)) {
        // enablePings
        uniqueClientId = queryParameters.get(CLIENT_ID_PARAM).get(0);
    }
    if (queryParameters != null && queryParameters.containsKey(FORMAT_PARAM)) {
        format = queryParameters.get(FORMAT_PARAM).get(0);
    }
    if (queryParameters != null && requestPreprocessor != null) {
        requestPreprocessor.call(queryParameters, context);
    }
    // apply sampling, milli, then seconds
    if (queryParameters != null && queryParameters.containsKey(SAMPLE_PARAM_MSEC)) {
        // apply sampling rate
        int samplingRate = Integer.parseInt(queryParameters.get(SAMPLE_PARAM_MSEC).get(0));
        requestObservable = requestObservable.sample(samplingRate, TimeUnit.MILLISECONDS);
    }
    if (queryParameters != null && queryParameters.containsKey(SAMPLE_PARAM)) {
        // apply sampling rate
        int samplingRate = Integer.parseInt(queryParameters.get(SAMPLE_PARAM).get(0));
        requestObservable = requestObservable.sample(samplingRate, TimeUnit.SECONDS);
    }
    if (queryParameters != null && queryParameters.containsKey(ENABLE_PINGS_PARAM)) {
        // enablePings
        String enablePings = queryParameters.get(ENABLE_PINGS_PARAM).get(0);
        if ("true".equalsIgnoreCase(enablePings)) {
            pingsEnabled = true;
        } else {
            pingsEnabled = false;
        }
    }
    if (queryParameters != null && queryParameters.containsKey("delay")) {
        // apply flush
        try {
            int flushInterval = Integer.parseInt(queryParameters.get("delay").get(0));
            if (flushInterval >= 50) {
                flushIntervalMillis = flushInterval;
            } else {
                LOG.warn("delay parameter too small " + flushInterval + " min. is 100");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    final byte[] delimiter = queryParameters != null && queryParameters.containsKey(MantisSSEConstants.MANTIS_COMPRESSION_DELIMITER) && queryParameters.get(MantisSSEConstants.MANTIS_COMPRESSION_DELIMITER).get(0) != null ? queryParameters.get(MantisSSEConstants.MANTIS_COMPRESSION_DELIMITER).get(0).getBytes() : null;
    Tag[] tags = new Tag[2];
    final String clientId = Optional.ofNullable(uniqueClientId).orElse("none");
    final String sockAddr = Optional.ofNullable(socketAddrStr).orElse("none");
    tags[0] = new BasicTag("clientId", clientId);
    tags[1] = new BasicTag("sockAddr", sockAddr);
    Metrics sseSinkMetrics = new Metrics.Builder().id("ServerSentEventRequestHandler", tags).addCounter("processedCounter").addCounter("pingCounter").addCounter("errorCounter").addCounter("droppedCounter").addCounter("flushCounter").build();
    final Counter msgProcessedCounter = sseSinkMetrics.getCounter("processedCounter");
    final Counter pingCounter = sseSinkMetrics.getCounter("pingCounter");
    final Counter errorCounter = sseSinkMetrics.getCounter("errorCounter");
    final Counter droppedWrites = sseSinkMetrics.getCounter("droppedCounter");
    final Counter flushCounter = sseSinkMetrics.getCounter("flushCounter");
    // get predicate, defaults to return true for all T
    Func1<T, Boolean> filterFunction = new Func1<T, Boolean>() {

        @Override
        public Boolean call(T t1) {
            return true;
        }
    };
    if (queryParameters != null && predicate != null) {
        filterFunction = predicate.getPredicate().call(queryParameters);
    }
    final Subscription timerSubscription = Observable.interval(1, TimeUnit.SECONDS).doOnNext(new Action1<Long>() {

        @Override
        public void call(Long t1) {
            long currentTime = System.currentTimeMillis();
            if (pingsEnabled && (lastResponseSent.get() == -1 || currentTime > lastResponseSent.get() + PING_INTERVAL)) {
                pingCounter.increment();
                response.writeStringAndFlush(PING);
                lastResponseSent.set(currentTime);
            }
        }
    }).subscribe();
    return requestObservable.filter(filterFunction).map(encoder).lift(new DisableBackPressureOperator<String>()).buffer(flushIntervalMillis, TimeUnit.MILLISECONDS).flatMap(new Func1<List<String>, Observable<Void>>() {

        @Override
        public Observable<Void> call(List<String> valueList) {
            if (response.isCloseIssued() || !response.getChannel().isActive()) {
                LOG.info("Client closed detected, throwing closed channel exception");
                return Observable.error(new ClosedChannelException());
            }
            List<String> filteredList = valueList.stream().filter(e -> {
                return slotMgr.filter(sn, e.getBytes());
            }).collect(Collectors.toList());
            if (response.getChannel().isWritable()) {
                flushCounter.increment();
                if (format.equals(BINARY_FORMAT)) {
                    boolean useSnappy = true;
                    try {
                        String compressedList = delimiter == null ? CompressionUtils.compressAndBase64Encode(filteredList, useSnappy) : CompressionUtils.compressAndBase64Encode(filteredList, useSnappy, delimiter);
                        StringBuilder sb = new StringBuilder(3);
                        sb.append(SSE_DATA_PREFIX);
                        sb.append(compressedList);
                        sb.append(TWO_NEWLINES);
                        msgProcessedCounter.increment(valueList.size());
                        lastResponseSent.set(System.currentTimeMillis());
                        return response.writeStringAndFlush(sb.toString());
                    } catch (Exception e) {
                        LOG.warn("Could not compress data" + e.getMessage());
                        droppedWrites.increment(valueList.size());
                        return Observable.empty();
                    }
                } else {
                    int noOfMsgs = 0;
                    StringBuilder sb = new StringBuilder(valueList.size() * 3);
                    for (String s : filteredList) {
                        sb.append(SSE_DATA_PREFIX);
                        sb.append(s);
                        sb.append(TWO_NEWLINES);
                        noOfMsgs++;
                    }
                    msgProcessedCounter.increment(noOfMsgs);
                    lastResponseSent.set(System.currentTimeMillis());
                    return response.writeStringAndFlush(sb.toString());
                }
            } else {
                // 
                droppedWrites.increment(filteredList.size());
            }
            return Observable.empty();
        }
    }).onErrorResumeNext(new Func1<Throwable, Observable<? extends Void>>() {

        @Override
        public Observable<? extends Void> call(Throwable throwable) {
            Throwable cause = throwable.getCause();
            // ignore closed channel exceptions, this is
            // when the connection was closed on the client
            // side without informing the server
            errorCounter.increment();
            if (cause != null && !(cause instanceof ClosedChannelException)) {
                LOG.warn("Error detected in SSE sink", cause);
                if (errorEncoder != null) {
                    // write error out on connection
                    // response.writeAndFlush(errorEncoder.call(throwable));
                    ByteBuf errType = response.getAllocator().buffer().writeBytes("error: ".getBytes());
                    ByteBuf errRes = response.getAllocator().buffer().writeBytes((errorEncoder.call(throwable)).getBytes());
                    response.writeAndFlush(ServerSentEvent.withEventType(errType, errRes));
                }
                throwable.printStackTrace();
            }
            if (requestPostprocessor != null && queryParameters != null) {
                requestPostprocessor.call(queryParameters, context);
            }
            ssm.deregisterServer(sn, queryParameters);
            timerSubscription.unsubscribe();
            return Observable.error(throwable);
        }
    });
}
Also used : Predicate(io.mantisrx.runtime.sink.predicate.Predicate) DisableBackPressureOperator(io.reactivx.mantis.operators.DisableBackPressureOperator) ServerSentEvent(mantis.io.reactivex.netty.protocol.http.sse.ServerSentEvent) LoggerFactory(org.slf4j.LoggerFactory) Action1(rx.functions.Action1) DropOperator(io.reactivx.mantis.operators.DropOperator) HttpServerResponse(mantis.io.reactivex.netty.protocol.http.server.HttpServerResponse) Observable(rx.Observable) Func1(rx.functions.Func1) Func2(rx.functions.Func2) ByteBuf(io.netty.buffer.ByteBuf) Map(java.util.Map) Schedulers(rx.schedulers.Schedulers) RequestHandler(mantis.io.reactivex.netty.protocol.http.server.RequestHandler) BasicTag(com.netflix.spectator.api.BasicTag) Metrics(io.mantisrx.common.metrics.Metrics) Counter(io.mantisrx.common.metrics.Counter) Logger(org.slf4j.Logger) Tag(com.netflix.spectator.api.Tag) HashFunctions(io.mantisrx.common.network.HashFunctions) Endpoint(io.mantisrx.common.network.Endpoint) ClosedChannelException(java.nio.channels.ClosedChannelException) WritableEndpoint(io.mantisrx.common.network.WritableEndpoint) Context(io.mantisrx.runtime.Context) InetSocketAddress(java.net.InetSocketAddress) Collectors(java.util.stream.Collectors) TimeUnit(java.util.concurrent.TimeUnit) AtomicLong(java.util.concurrent.atomic.AtomicLong) List(java.util.List) CompressionUtils(io.mantisrx.common.compression.CompressionUtils) SlotAssignmentManager(io.mantisrx.common.network.ServerSlotManager.SlotAssignmentManager) Optional(java.util.Optional) ServerSlotManager(io.mantisrx.common.network.ServerSlotManager) MantisSSEConstants(com.mantisrx.common.utils.MantisSSEConstants) HttpServerRequest(mantis.io.reactivex.netty.protocol.http.server.HttpServerRequest) Subscription(rx.Subscription) InetSocketAddress(java.net.InetSocketAddress) ByteBuf(io.netty.buffer.ByteBuf) BasicTag(com.netflix.spectator.api.BasicTag) Metrics(io.mantisrx.common.metrics.Metrics) Counter(io.mantisrx.common.metrics.Counter) List(java.util.List) Func1(rx.functions.Func1) Subscription(rx.Subscription) ClosedChannelException(java.nio.channels.ClosedChannelException) WritableEndpoint(io.mantisrx.common.network.WritableEndpoint) DisableBackPressureOperator(io.reactivx.mantis.operators.DisableBackPressureOperator) Action1(rx.functions.Action1) Endpoint(io.mantisrx.common.network.Endpoint) WritableEndpoint(io.mantisrx.common.network.WritableEndpoint) ClosedChannelException(java.nio.channels.ClosedChannelException) Observable(rx.Observable) AtomicLong(java.util.concurrent.atomic.AtomicLong) AtomicLong(java.util.concurrent.atomic.AtomicLong) BasicTag(com.netflix.spectator.api.BasicTag) Tag(com.netflix.spectator.api.Tag)

Aggregations

DropOperator (io.reactivx.mantis.operators.DropOperator)5 List (java.util.List)5 Observable (rx.Observable)5 Action0 (rx.functions.Action0)4 Func1 (rx.functions.Func1)4 ChannelPipeline (io.netty.channel.ChannelPipeline)3 LengthFieldBasedFrameDecoder (io.netty.handler.codec.LengthFieldBasedFrameDecoder)3 LengthFieldPrepender (io.netty.handler.codec.LengthFieldPrepender)3 JdkZlibDecoder (io.netty.handler.codec.compression.JdkZlibDecoder)3 JdkZlibEncoder (io.netty.handler.codec.compression.JdkZlibEncoder)3 LoggingHandler (io.netty.handler.logging.LoggingHandler)3 IdleStateHandler (io.netty.handler.timeout.IdleStateHandler)3 ObservableConnection (mantis.io.reactivex.netty.channel.ObservableConnection)3 PipelineConfigurator (mantis.io.reactivex.netty.pipeline.PipelineConfigurator)3 GroupedObservable (rx.observables.GroupedObservable)3 ByteBuffer (java.nio.ByteBuffer)2 Map (java.util.Map)2 TimeUnit (java.util.concurrent.TimeUnit)2 Collectors (java.util.stream.Collectors)2 MantisSSEConstants (com.mantisrx.common.utils.MantisSSEConstants)1