Search in sources :

Example 1 with Predicate

use of io.mantisrx.runtime.sink.predicate.Predicate 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)

Example 2 with Predicate

use of io.mantisrx.runtime.sink.predicate.Predicate in project mantis by Netflix.

the class SourceSink method call.

@Override
public void call(Context context, PortRequest portRequest, Observable<String> observable) {
    observable = observable.filter(t1 -> !t1.isEmpty());
    ServerSentEventsSink<String> sink = new ServerSentEventsSink.Builder<String>().withEncoder(data -> data).withPredicate(new Predicate<>("description", new EventFilter(clientId))).withRequestPreprocessor(preProcessor).withRequestPostprocessor(postProcessor).build();
    observable.subscribe();
    sink.call(context, portRequest, observable);
}
Also used : Predicate(io.mantisrx.runtime.sink.predicate.Predicate) List(java.util.List) Func2(rx.functions.Func2) Sink(io.mantisrx.runtime.sink.Sink) Map(java.util.Map) PortRequest(io.mantisrx.runtime.PortRequest) EventFilter(io.mantisrx.connector.publish.core.EventFilter) ServerSentEventsSink(io.mantisrx.runtime.sink.ServerSentEventsSink) Context(io.mantisrx.runtime.Context) Observable(rx.Observable) ServerSentEventsSink(io.mantisrx.runtime.sink.ServerSentEventsSink) EventFilter(io.mantisrx.connector.publish.core.EventFilter) Predicate(io.mantisrx.runtime.sink.predicate.Predicate)

Aggregations

Context (io.mantisrx.runtime.Context)2 Predicate (io.mantisrx.runtime.sink.predicate.Predicate)2 List (java.util.List)2 Map (java.util.Map)2 Observable (rx.Observable)2 Func2 (rx.functions.Func2)2 MantisSSEConstants (com.mantisrx.common.utils.MantisSSEConstants)1 BasicTag (com.netflix.spectator.api.BasicTag)1 Tag (com.netflix.spectator.api.Tag)1 CompressionUtils (io.mantisrx.common.compression.CompressionUtils)1 Counter (io.mantisrx.common.metrics.Counter)1 Metrics (io.mantisrx.common.metrics.Metrics)1 Endpoint (io.mantisrx.common.network.Endpoint)1 HashFunctions (io.mantisrx.common.network.HashFunctions)1 ServerSlotManager (io.mantisrx.common.network.ServerSlotManager)1 SlotAssignmentManager (io.mantisrx.common.network.ServerSlotManager.SlotAssignmentManager)1 WritableEndpoint (io.mantisrx.common.network.WritableEndpoint)1 EventFilter (io.mantisrx.connector.publish.core.EventFilter)1 PortRequest (io.mantisrx.runtime.PortRequest)1 ServerSentEventsSink (io.mantisrx.runtime.sink.ServerSentEventsSink)1