/* * Copyright 2010-2011 Ning, Inc. * * Ning licenses this file to you under the Apache License, version 2.0 * (the "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. */ package com.ning.metrics.eventtracker; import com.ning.http.client.AsyncCompletionHandler; import com.ning.http.client.Response; import com.ning.metrics.serialization.writer.CallbackHandler; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.Timer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; public class HttpSender implements EventSender { private static final Logger log = LoggerFactory.getLogger(HttpSender.class); private final AtomicLong activeRequests = new AtomicLong(0); private final LocalQueueAndWorkers workers; private final ThreadSafeAsyncHttpClient client; private final long httpMaxWaitTimeInMillis; private final Timer sendTimer; public HttpSender(final String collectorHost, final int collectorPort, final EventType eventType, final long httpMaxWaitTimeInMillis, final long httpMaxKeepAliveInMillis, final int httpWorkersPoolSize) { this(new ThreadSafeAsyncHttpClient(collectorHost, collectorPort, eventType, httpMaxKeepAliveInMillis), httpMaxWaitTimeInMillis, Metrics.newTimer(HttpSender.class, collectorHost.replace(":", "_"), TimeUnit.MILLISECONDS, TimeUnit.SECONDS), httpWorkersPoolSize); } // For testing HttpSender(final ThreadSafeAsyncHttpClient client, final long httpMaxWaitTimeInMillis, final Timer sendTimer, final int httpWorkersPoolSize) { this.client = client; this.httpMaxWaitTimeInMillis = httpMaxWaitTimeInMillis; this.sendTimer = sendTimer; this.workers = new LocalQueueAndWorkers(httpWorkersPoolSize); } /** * Send a file full of events to the collector. This does zero-bytes-copy by default (the async-http-client does * it for us). * * @param file File to send * @param handler callback handler for the serialization-writer library */ @Override public void send(final File file, final CallbackHandler handler) { log.info("Sending local file to collector: {}", file.getAbsolutePath()); final long startTime = System.nanoTime(); final AsyncCompletionHandler<Response> asyncCompletionHandler = new AsyncCompletionHandler<Response>() { @Override public Response onCompleted(final Response response) { activeRequests.decrementAndGet(); if (response.getStatusCode() == 202) { handler.onSuccess(file); } else { handler.onError(new IOException(String.format("Received response %d: %s", response.getStatusCode(), response.getStatusText())), file); } sendTimer.update(System.nanoTime() - startTime, TimeUnit.NANOSECONDS); return response; // never read } @Override public void onThrowable(final Throwable t) { activeRequests.decrementAndGet(); handler.onError(t, file); } }; final HttpJob job = new HttpJob(client, file, asyncCompletionHandler); workers.offer(job); } @Override public synchronized void close() { client.close(); try { if (activeRequests.get() > 0) { log.info(String.format("%d HTTP request(s) in progress, giving them some time to finish...", activeRequests.get())); } long sleptInMillins = httpMaxWaitTimeInMillis; while (activeRequests.get() > 0 && sleptInMillins >= 0) { Thread.sleep(200); sleptInMillins -= 200; } if (activeRequests.get() > 0) { log.warn("Giving up on pending HTTP requests, shutting down NOW!"); } workers.close(); } catch (InterruptedException e) { log.warn("Interrupted while waiting for active queries to finish"); Thread.currentThread().interrupt(); } } }