/* * Copyright 2015 the original author or authors. * * Licensed 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 org.springframework.xd.throughput; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.Lifecycle; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHandler; import org.springframework.messaging.MessagingException; /** * A simple handler that will count messages and log witnessed throughput at some interval. * * @author Eric Bottard * @author Marius Bogoevici */ public class ThroughputMessageHandler implements MessageHandler, Lifecycle { private Logger logger; private final AtomicLong counter = new AtomicLong(); private final AtomicLong start = new AtomicLong(-1); private final AtomicLong bytes = new AtomicLong(-1); private final AtomicLong intermediateCounter = new AtomicLong(); private final AtomicLong intermediateBytes = new AtomicLong(); private long reportEveryMs; private TimeUnit timeUnit = TimeUnit.s; private final Clock clock = new Clock(); private volatile boolean running; private ExecutorService executorService; private boolean reportBytes = false; public ThroughputMessageHandler() { } @Override public void start() { if (executorService == null) { executorService = Executors.newFixedThreadPool(1); } this.running = true; } @Override public void stop() { this.running = false; } @Override public boolean isRunning() { return running; } @Override public void handleMessage(Message<?> message) throws MessagingException { if (start.get() == -1L) { synchronized (start) { if (start.get() == -1L) { // assume a homogeneous message structure - this is intended for perf tests so we can safely assume // that the messages are similar, Therefore we'll do our reporting based on the first message Object payload = message.getPayload(); if (payload instanceof byte[] || payload instanceof String) { reportBytes = true; } start.set(clock.now()); executorService.execute(new ReportStats()); } } } intermediateCounter.incrementAndGet(); if (reportBytes) { Object payload = message.getPayload(); if (payload instanceof byte[]) { intermediateBytes.addAndGet(((byte[]) payload).length); } else if (payload instanceof String) { intermediateBytes.addAndGet((((String) payload).getBytes()).length); } } } /** * As a strategy class to ease unit testing. */ public static class Clock { public long now() { return System.currentTimeMillis(); } } public void setReportEveryMs(long reportEveryMs) { this.reportEveryMs = reportEveryMs; } public void setTimeUnit(TimeUnit timeUnit) { this.timeUnit = timeUnit; } public void setLogger(String name) { this.logger = LoggerFactory.getLogger("xd.sink.throughput." + name); } private class ReportStats implements Runnable { @Override public void run() { while (isRunning()) { long intervalStart = clock.now(); try { Thread.sleep(reportEveryMs); long timeNow = clock.now(); long currentCounter = intermediateCounter.getAndSet(0L); long currentBytes = intermediateBytes.getAndSet(0L); long totalCounter = counter.addAndGet(currentCounter); long totalBytes = bytes.addAndGet(currentBytes); logger.info( String.format("Messages: %10d in %5.2f%s = %11.2f/s", currentCounter, (timeNow - intervalStart)/ 1000.0, timeUnit, ((double) currentCounter * 1000 / reportEveryMs))); logger.info( String.format("Messages: %10d in %5.2f%s = %11.2f/s", totalCounter, (timeNow - start.get()) / 1000.0, timeUnit, ((double) totalCounter * 1000 / (timeNow - start.get())))); if (reportBytes) { logger.info( String.format("Throughput: %12d in %5.2f%s = %11.2fMB/s, ", currentBytes, (timeNow - intervalStart)/ 1000.0, timeUnit, ((currentBytes / (1024.0 * 1024)) * 1000 / reportEveryMs))); logger.info( String.format("Throughput: %12d in %5.2f%s = %11.2fMB/s", totalBytes, (timeNow - start.get()) / 1000.0, timeUnit, ((totalBytes / (1024.0 * 1024)) * 1000 / (timeNow - start.get())))); } } catch (InterruptedException e) { if (!isRunning()) { Thread.currentThread().interrupt(); } } } } } }