package ru.yandex.market.graphouse.server; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Value; import ru.yandex.market.graphouse.Metric; import ru.yandex.market.graphouse.cacher.MetricCacher; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; /** * @author Dmitry Andreev <a href="mailto:AndreevDm@yandex-team.ru"></a> * @date 02/04/15 */ public class MetricServer implements InitializingBean { private static final Logger log = LogManager.getLogger(); @Value("${graphouse.cacher.port}") private int port; @Value("${graphouse.cacher.socket-timeout-millis}") private int socketTimeoutMillis; @Value("${graphouse.cacher.threads}") private int threadCount; @Value("${graphouse.cacher.read-batch-size}") private int readBatchSize; private ServerSocket serverSocket; private ExecutorService executorService; private final MetricCacher metricCacher; private final MetricFactory metricFactory; public MetricServer(MetricCacher metricCacher, MetricFactory metricFactory) { this.metricCacher = metricCacher; this.metricFactory = metricFactory; } @Override public void afterPropertiesSet() throws Exception { log.info("Starting metric server on port: " + port); serverSocket = new ServerSocket(port); log.info("Starting " + threadCount + " metric server threads"); executorService = Executors.newFixedThreadPool(threadCount); for (int i = 0; i < threadCount; i++) { executorService.submit(new MetricServerWorker()); } Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { @Override public void run() { log.info("Shutting down metric server"); executorService.shutdownNow(); try { serverSocket.close(); } catch (IOException ignored) { } log.info("Metric server stopped"); } })); log.info("Metric server started on port " + port); } private class MetricServerWorker implements Runnable { private final List<Metric> metrics = new ArrayList<>(readBatchSize); @Override public void run() { while (!Thread.interrupted() && !serverSocket.isClosed()) { try { read(); } catch (Exception e) { log.warn("Failed to read data", e); } } log.info("MetricServerWorker stopped"); } private void read() throws IOException { metrics.clear(); Socket socket = serverSocket.accept(); try { socket.setSoTimeout(socketTimeoutMillis); socket.setKeepAlive(false); BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while ((line = reader.readLine()) != null) { int updatedSeconds = (int) (TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())); Metric metric = metricFactory.createMetric(line, updatedSeconds); if (metric != null) { metrics.add(metric); if (metrics.size() >= readBatchSize) { metricCacher.submitMetrics(metrics); metrics.clear(); } } } } catch (SocketTimeoutException e) { log.warn("Socket timeout from " + socket.getRemoteSocketAddress().toString()); } finally { socket.close(); } metricCacher.submitMetrics(metrics); metrics.clear(); } } public void setSocketTimeoutMillis(int socketTimeoutMillis) { this.socketTimeoutMillis = socketTimeoutMillis; } public void setThreadCount(int threadCount) { this.threadCount = threadCount; } public void setPort(int port) { this.port = port; } }