Search in sources :

Example 1 with StatusResponseHandler

use of com.metamx.http.client.response.StatusResponseHandler in project druid by druid-io.

the class DirectDruidClient method run.

@Override
public Sequence<T> run(final Query<T> query, final Map<String, Object> context) {
    QueryToolChest<T, Query<T>> toolChest = warehouse.getToolChest(query);
    boolean isBySegment = BaseQuery.getContextBySegment(query, false);
    Pair<JavaType, JavaType> types = typesMap.get(query.getClass());
    if (types == null) {
        final TypeFactory typeFactory = objectMapper.getTypeFactory();
        JavaType baseType = typeFactory.constructType(toolChest.getResultTypeReference());
        JavaType bySegmentType = typeFactory.constructParametricType(Result.class, typeFactory.constructParametricType(BySegmentResultValueClass.class, baseType));
        types = Pair.of(baseType, bySegmentType);
        typesMap.put(query.getClass(), types);
    }
    final JavaType typeRef;
    if (isBySegment) {
        typeRef = types.rhs;
    } else {
        typeRef = types.lhs;
    }
    final ListenableFuture<InputStream> future;
    final String url = String.format("http://%s/druid/v2/", host);
    final String cancelUrl = String.format("http://%s/druid/v2/%s", host, query.getId());
    try {
        log.debug("Querying queryId[%s] url[%s]", query.getId(), url);
        final long requestStartTime = System.currentTimeMillis();
        final ServiceMetricEvent.Builder builder = toolChest.makeMetricBuilder(query);
        builder.setDimension("server", host);
        final HttpResponseHandler<InputStream, InputStream> responseHandler = new HttpResponseHandler<InputStream, InputStream>() {

            private long responseStartTime;

            private final AtomicLong byteCount = new AtomicLong(0);

            private final BlockingQueue<InputStream> queue = new LinkedBlockingQueue<>();

            private final AtomicBoolean done = new AtomicBoolean(false);

            @Override
            public ClientResponse<InputStream> handleResponse(HttpResponse response) {
                log.debug("Initial response from url[%s] for queryId[%s]", url, query.getId());
                responseStartTime = System.currentTimeMillis();
                emitter.emit(builder.build("query/node/ttfb", responseStartTime - requestStartTime));
                try {
                    final String responseContext = response.headers().get("X-Druid-Response-Context");
                    // context may be null in case of error or query timeout
                    if (responseContext != null) {
                        context.putAll(objectMapper.<Map<String, Object>>readValue(responseContext, new TypeReference<Map<String, Object>>() {
                        }));
                    }
                    queue.put(new ChannelBufferInputStream(response.getContent()));
                } catch (final IOException e) {
                    log.error(e, "Error parsing response context from url [%s]", url);
                    return ClientResponse.<InputStream>finished(new InputStream() {

                        @Override
                        public int read() throws IOException {
                            throw e;
                        }
                    });
                } catch (InterruptedException e) {
                    log.error(e, "Queue appending interrupted");
                    Thread.currentThread().interrupt();
                    throw Throwables.propagate(e);
                }
                byteCount.addAndGet(response.getContent().readableBytes());
                return ClientResponse.<InputStream>finished(new SequenceInputStream(new Enumeration<InputStream>() {

                    @Override
                    public boolean hasMoreElements() {
                        // Then the stream should be spouting good InputStreams.
                        synchronized (done) {
                            return !done.get() || !queue.isEmpty();
                        }
                    }

                    @Override
                    public InputStream nextElement() {
                        try {
                            return queue.take();
                        } catch (InterruptedException e) {
                            Thread.currentThread().interrupt();
                            throw Throwables.propagate(e);
                        }
                    }
                }));
            }

            @Override
            public ClientResponse<InputStream> handleChunk(ClientResponse<InputStream> clientResponse, HttpChunk chunk) {
                final ChannelBuffer channelBuffer = chunk.getContent();
                final int bytes = channelBuffer.readableBytes();
                if (bytes > 0) {
                    try {
                        queue.put(new ChannelBufferInputStream(channelBuffer));
                    } catch (InterruptedException e) {
                        log.error(e, "Unable to put finalizing input stream into Sequence queue for url [%s]", url);
                        Thread.currentThread().interrupt();
                        throw Throwables.propagate(e);
                    }
                    byteCount.addAndGet(bytes);
                }
                return clientResponse;
            }

            @Override
            public ClientResponse<InputStream> done(ClientResponse<InputStream> clientResponse) {
                long stopTime = System.currentTimeMillis();
                log.debug("Completed queryId[%s] request to url[%s] with %,d bytes returned in %,d millis [%,f b/s].", query.getId(), url, byteCount.get(), stopTime - responseStartTime, byteCount.get() / (0.0001 * (stopTime - responseStartTime)));
                emitter.emit(builder.build("query/node/time", stopTime - requestStartTime));
                emitter.emit(builder.build("query/node/bytes", byteCount.get()));
                synchronized (done) {
                    try {
                        // An empty byte array is put at the end to give the SequenceInputStream.close() as something to close out
                        // after done is set to true, regardless of the rest of the stream's state.
                        queue.put(ByteSource.empty().openStream());
                    } catch (InterruptedException e) {
                        log.error(e, "Unable to put finalizing input stream into Sequence queue for url [%s]", url);
                        Thread.currentThread().interrupt();
                        throw Throwables.propagate(e);
                    } catch (IOException e) {
                        // This should never happen
                        throw Throwables.propagate(e);
                    } finally {
                        done.set(true);
                    }
                }
                return ClientResponse.<InputStream>finished(clientResponse.getObj());
            }

            @Override
            public void exceptionCaught(final ClientResponse<InputStream> clientResponse, final Throwable e) {
                // Don't wait for lock in case the lock had something to do with the error
                synchronized (done) {
                    done.set(true);
                    // Make a best effort to put a zero length buffer into the queue in case something is waiting on the take()
                    // If nothing is waiting on take(), this will be closed out anyways.
                    queue.offer(new InputStream() {

                        @Override
                        public int read() throws IOException {
                            throw new IOException(e);
                        }
                    });
                }
            }
        };
        future = httpClient.go(new Request(HttpMethod.POST, new URL(url)).setContent(objectMapper.writeValueAsBytes(query)).setHeader(HttpHeaders.Names.CONTENT_TYPE, isSmile ? SmileMediaTypes.APPLICATION_JACKSON_SMILE : MediaType.APPLICATION_JSON), responseHandler);
        queryWatcher.registerQuery(query, future);
        openConnections.getAndIncrement();
        Futures.addCallback(future, new FutureCallback<InputStream>() {

            @Override
            public void onSuccess(InputStream result) {
                openConnections.getAndDecrement();
            }

            @Override
            public void onFailure(Throwable t) {
                openConnections.getAndDecrement();
                if (future.isCancelled()) {
                    // forward the cancellation to underlying queriable node
                    try {
                        StatusResponseHolder res = httpClient.go(new Request(HttpMethod.DELETE, new URL(cancelUrl)).setContent(objectMapper.writeValueAsBytes(query)).setHeader(HttpHeaders.Names.CONTENT_TYPE, isSmile ? SmileMediaTypes.APPLICATION_JACKSON_SMILE : MediaType.APPLICATION_JSON), new StatusResponseHandler(Charsets.UTF_8)).get();
                        if (res.getStatus().getCode() >= 500) {
                            throw new RE("Error cancelling query[%s]: queriable node returned status[%d] [%s].", res.getStatus().getCode(), res.getStatus().getReasonPhrase());
                        }
                    } catch (IOException | ExecutionException | InterruptedException e) {
                        Throwables.propagate(e);
                    }
                }
            }
        });
    } catch (IOException e) {
        throw Throwables.propagate(e);
    }
    Sequence<T> retVal = new BaseSequence<>(new BaseSequence.IteratorMaker<T, JsonParserIterator<T>>() {

        @Override
        public JsonParserIterator<T> make() {
            return new JsonParserIterator<T>(typeRef, future, url);
        }

        @Override
        public void cleanup(JsonParserIterator<T> iterFromMake) {
            CloseQuietly.close(iterFromMake);
        }
    });
    // avoid the cost of de-serializing and then re-serializing again when adding to cache
    if (!isBySegment) {
        retVal = Sequences.map(retVal, toolChest.makePreComputeManipulatorFn(query, MetricManipulatorFns.deserializing()));
    }
    return retVal;
}
Also used : ClientResponse(com.metamx.http.client.response.ClientResponse) BaseQuery(io.druid.query.BaseQuery) Query(io.druid.query.Query) QueryInterruptedException(io.druid.query.QueryInterruptedException) URL(java.net.URL) ChannelBuffer(org.jboss.netty.buffer.ChannelBuffer) StatusResponseHolder(com.metamx.http.client.response.StatusResponseHolder) TypeReference(com.fasterxml.jackson.core.type.TypeReference) ChannelBufferInputStream(org.jboss.netty.buffer.ChannelBufferInputStream) BlockingQueue(java.util.concurrent.BlockingQueue) LinkedBlockingQueue(java.util.concurrent.LinkedBlockingQueue) Enumeration(java.util.Enumeration) ChannelBufferInputStream(org.jboss.netty.buffer.ChannelBufferInputStream) SequenceInputStream(java.io.SequenceInputStream) InputStream(java.io.InputStream) Request(com.metamx.http.client.Request) BySegmentResultValueClass(io.druid.query.BySegmentResultValueClass) HttpResponse(org.jboss.netty.handler.codec.http.HttpResponse) IOException(java.io.IOException) BaseSequence(io.druid.java.util.common.guava.BaseSequence) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) JavaType(com.fasterxml.jackson.databind.JavaType) AtomicLong(java.util.concurrent.atomic.AtomicLong) RE(io.druid.java.util.common.RE) SequenceInputStream(java.io.SequenceInputStream) ServiceMetricEvent(com.metamx.emitter.service.ServiceMetricEvent) StatusResponseHandler(com.metamx.http.client.response.StatusResponseHandler) TypeFactory(com.fasterxml.jackson.databind.type.TypeFactory) HttpResponseHandler(com.metamx.http.client.response.HttpResponseHandler) HttpChunk(org.jboss.netty.handler.codec.http.HttpChunk)

Example 2 with StatusResponseHandler

use of com.metamx.http.client.response.StatusResponseHandler in project druid by druid-io.

the class JettyQosTest method testQoS.

@Test(timeout = 60_000L)
public void testQoS() throws Exception {
    final int fastThreads = 20;
    final int slowThreads = 15;
    final int slowRequestsPerThread = 5;
    final int fastRequestsPerThread = 200;
    final HttpClient fastClient = new ClientHolder(fastThreads).getClient();
    final HttpClient slowClient = new ClientHolder(slowThreads).getClient();
    final ExecutorService fastPool = Execs.multiThreaded(fastThreads, "fast-%d");
    final ExecutorService slowPool = Execs.multiThreaded(slowThreads, "slow-%d");
    final CountDownLatch latch = new CountDownLatch(fastThreads * fastRequestsPerThread);
    final AtomicLong fastCount = new AtomicLong();
    final AtomicLong slowCount = new AtomicLong();
    final AtomicLong fastElapsed = new AtomicLong();
    final AtomicLong slowElapsed = new AtomicLong();
    for (int i = 0; i < slowThreads; i++) {
        slowPool.submit(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < slowRequestsPerThread; i++) {
                    long startTime = System.currentTimeMillis();
                    try {
                        ListenableFuture<StatusResponseHolder> go = slowClient.go(new Request(HttpMethod.GET, new URL("http://localhost:" + port + "/slow/hello")), new StatusResponseHandler(Charset.defaultCharset()));
                        go.get();
                        slowCount.incrementAndGet();
                        slowElapsed.addAndGet(System.currentTimeMillis() - startTime);
                    } catch (InterruptedException e) {
                    // BE COOL
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw Throwables.propagate(e);
                    }
                }
            }
        });
    }
    // wait for jetty server pool to completely fill up
    while (server.getThreadPool().getIdleThreads() != 0) {
        Thread.sleep(25);
    }
    for (int i = 0; i < fastThreads; i++) {
        fastPool.submit(new Runnable() {

            @Override
            public void run() {
                for (int i = 0; i < fastRequestsPerThread; i++) {
                    long startTime = System.currentTimeMillis();
                    try {
                        ListenableFuture<StatusResponseHolder> go = fastClient.go(new Request(HttpMethod.GET, new URL("http://localhost:" + port + "/default")), new StatusResponseHandler(Charset.defaultCharset()));
                        go.get();
                        fastCount.incrementAndGet();
                        fastElapsed.addAndGet(System.currentTimeMillis() - startTime);
                        latch.countDown();
                    } catch (InterruptedException e) {
                    // BE COOL
                    } catch (Exception e) {
                        e.printStackTrace();
                        throw Throwables.propagate(e);
                    }
                }
            }
        });
    }
    // Wait for all fast requests to be served
    latch.await();
    slowPool.shutdownNow();
    fastPool.shutdown();
    // check that fast requests finished quickly
    Assert.assertTrue(fastElapsed.get() / fastCount.get() < 500);
}
Also used : Request(com.metamx.http.client.Request) CountDownLatch(java.util.concurrent.CountDownLatch) URL(java.net.URL) AtomicLong(java.util.concurrent.atomic.AtomicLong) HttpClient(com.metamx.http.client.HttpClient) ExecutorService(java.util.concurrent.ExecutorService) ListenableFuture(com.google.common.util.concurrent.ListenableFuture) StatusResponseHandler(com.metamx.http.client.response.StatusResponseHandler) Test(org.junit.Test)

Example 3 with StatusResponseHandler

use of com.metamx.http.client.response.StatusResponseHandler in project druid by druid-io.

the class JettyTest method testTimeouts.

@Test
// this test will deadlock if it hits an issue, so ignored by default
@Ignore
public void testTimeouts() throws Exception {
    // test for request timeouts properly not locking up all threads
    final Executor executor = Executors.newFixedThreadPool(100);
    final AtomicLong count = new AtomicLong(0);
    final CountDownLatch latch = new CountDownLatch(1000);
    for (int i = 0; i < 10000; i++) {
        executor.execute(new Runnable() {

            @Override
            public void run() {
                executor.execute(new Runnable() {

                    @Override
                    public void run() {
                        long startTime = System.currentTimeMillis();
                        long startTime2 = 0;
                        try {
                            ListenableFuture<StatusResponseHolder> go = client.go(new Request(HttpMethod.GET, new URL("http://localhost:" + port + "/slow/hello")), new StatusResponseHandler(Charset.defaultCharset()));
                            startTime2 = System.currentTimeMillis();
                            go.get();
                        } catch (Exception e) {
                            e.printStackTrace();
                        } finally {
                            System.out.println("Response time client" + (System.currentTimeMillis() - startTime) + "time taken for getting future" + (System.currentTimeMillis() - startTime2) + "Counter " + count.incrementAndGet());
                            latch.countDown();
                        }
                    }
                });
            }
        });
    }
    latch.await();
}
Also used : Request(com.metamx.http.client.Request) CountDownLatch(java.util.concurrent.CountDownLatch) URL(java.net.URL) IOException(java.io.IOException) AtomicLong(java.util.concurrent.atomic.AtomicLong) Executor(java.util.concurrent.Executor) StatusResponseHolder(com.metamx.http.client.response.StatusResponseHolder) StatusResponseHandler(com.metamx.http.client.response.StatusResponseHandler) Ignore(org.junit.Ignore) Test(org.junit.Test)

Example 4 with StatusResponseHandler

use of com.metamx.http.client.response.StatusResponseHandler in project druid by druid-io.

the class RemoteTaskActionClient method submit.

@Override
public <RetType> RetType submit(TaskAction<RetType> taskAction) throws IOException {
    log.info("Performing action for task[%s]: %s", task.getId(), taskAction);
    byte[] dataToSend = jsonMapper.writeValueAsBytes(new TaskActionHolder(task, taskAction));
    final RetryPolicy retryPolicy = retryPolicyFactory.makeRetryPolicy();
    while (true) {
        try {
            final Server server;
            final URI serviceUri;
            try {
                server = getServiceInstance();
                serviceUri = makeServiceUri(server);
            } catch (Exception e) {
                // Want to retry, so throw an IOException.
                throw new IOException("Failed to locate service uri", e);
            }
            final StatusResponseHolder response;
            log.info("Submitting action for task[%s] to overlord[%s]: %s", task.getId(), serviceUri, taskAction);
            try {
                response = httpClient.go(new Request(HttpMethod.POST, serviceUri.toURL()).setContent(MediaType.APPLICATION_JSON, dataToSend), new StatusResponseHandler(Charsets.UTF_8)).get();
            } catch (Exception e) {
                Throwables.propagateIfInstanceOf(e.getCause(), IOException.class);
                Throwables.propagateIfInstanceOf(e.getCause(), ChannelException.class);
                throw Throwables.propagate(e);
            }
            if (response.getStatus().getCode() / 100 == 2) {
                final Map<String, Object> responseDict = jsonMapper.readValue(response.getContent(), new TypeReference<Map<String, Object>>() {
                });
                return jsonMapper.convertValue(responseDict.get("result"), taskAction.getReturnTypeReference());
            } else {
                // Want to retry, so throw an IOException.
                throw new IOException(String.format("Scary HTTP status returned: %s. Check your overlord[%s] logs for exceptions.", response.getStatus(), server.getHost()));
            }
        } catch (IOException | ChannelException e) {
            log.warn(e, "Exception submitting action for task[%s]", task.getId());
            final Duration delay = retryPolicy.getAndIncrementRetryDelay();
            if (delay == null) {
                throw e;
            } else {
                try {
                    final long sleepTime = jitter(delay.getMillis());
                    log.info("Will try again in [%s].", new Duration(sleepTime).toString());
                    Thread.sleep(sleepTime);
                } catch (InterruptedException e2) {
                    throw Throwables.propagate(e2);
                }
            }
        }
    }
}
Also used : Server(io.druid.client.selector.Server) Request(com.metamx.http.client.Request) Duration(org.joda.time.Duration) IOException(java.io.IOException) URI(java.net.URI) ChannelException(org.jboss.netty.channel.ChannelException) URISyntaxException(java.net.URISyntaxException) IOException(java.io.IOException) StatusResponseHolder(com.metamx.http.client.response.StatusResponseHolder) StatusResponseHandler(com.metamx.http.client.response.StatusResponseHandler) RetryPolicy(io.druid.indexing.common.RetryPolicy) Map(java.util.Map) ChannelException(org.jboss.netty.channel.ChannelException)

Aggregations

Request (com.metamx.http.client.Request)4 StatusResponseHandler (com.metamx.http.client.response.StatusResponseHandler)4 StatusResponseHolder (com.metamx.http.client.response.StatusResponseHolder)3 IOException (java.io.IOException)3 URL (java.net.URL)3 AtomicLong (java.util.concurrent.atomic.AtomicLong)3 CountDownLatch (java.util.concurrent.CountDownLatch)2 Test (org.junit.Test)2 TypeReference (com.fasterxml.jackson.core.type.TypeReference)1 JavaType (com.fasterxml.jackson.databind.JavaType)1 TypeFactory (com.fasterxml.jackson.databind.type.TypeFactory)1 ListenableFuture (com.google.common.util.concurrent.ListenableFuture)1 ServiceMetricEvent (com.metamx.emitter.service.ServiceMetricEvent)1 HttpClient (com.metamx.http.client.HttpClient)1 ClientResponse (com.metamx.http.client.response.ClientResponse)1 HttpResponseHandler (com.metamx.http.client.response.HttpResponseHandler)1 Server (io.druid.client.selector.Server)1 RetryPolicy (io.druid.indexing.common.RetryPolicy)1 RE (io.druid.java.util.common.RE)1 BaseSequence (io.druid.java.util.common.guava.BaseSequence)1