You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@avro.apache.org by ph...@apache.org on 2010/07/21 20:15:09 UTC
svn commit: r966342 - in /avro/trunk: ./ lang/java/
lang/java/src/java/org/apache/avro/ipc/stats/
lang/java/src/test/java/org/apache/avro/ipc/stats/ share/
Author: philz
Date: Wed Jul 21 18:15:08 2010
New Revision: 966342
URL: http://svn.apache.org/viewvc?rev=966342&view=rev
Log:
AVRO-587. Add Charts and Templating to Stats View
(Contributed by Patrick Wendell)
Modified:
avro/trunk/CHANGES.txt
avro/trunk/lang/java/build.xml
avro/trunk/lang/java/ivy.xml
avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java
avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java
avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java
avro/trunk/share/rat-excludes.txt
Modified: avro/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/avro/trunk/CHANGES.txt?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/CHANGES.txt (original)
+++ avro/trunk/CHANGES.txt Wed Jul 21 18:15:08 2010
@@ -33,6 +33,9 @@ Avro 1.4.0 (unreleased)
implementation. (Harry Wang via cutting)
IMPROVEMENTS
+ AVRO-587. Add Charts and Templating to Stats View
+ (Patrick Wendell via philz)
+
AVRO-584. Update Histogram for Stats Plugin
(Patrick Wendell via philz)
Modified: avro/trunk/lang/java/build.xml
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/build.xml?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/build.xml (original)
+++ avro/trunk/lang/java/build.xml Wed Jul 21 18:15:08 2010
@@ -41,11 +41,16 @@
<property name="java.src.dir" value="${src.dir}/java"/>
<property name="build.dir" value="${basedir}/build"/>
<property name="lib.dir" value="${basedir}/lib"/>
+ <property name="template.dir" value="${src.dir}/java/org/apache/avro/ipc/stats/templates"/>
+ <property name="static.dir" value="${src.dir}/java/org/apache/avro/ipc/stats/static"/>
+
<property name="build.classes" value="${build.dir}/classes"/>
<property name="build.doc" value="${build.dir}/doc"/>
<property name="build.javadoc" value="${build.doc}/api/java"/>
<property name="build.javadoc.log" value="${build.dir}/javadoc.log"/>
+ <property name="build.template.dir" value="${build.classes}/org/apache/avro/ipc/stats/templates"/>
+ <property name="build.static.dir" value="${build.classes}/org/apache/avro/ipc/stats/static"/>
<property name="test.count" value="100"/>
<property name="test.junit.output.format" value="plain"/>
@@ -169,6 +174,12 @@
<fileset file="${basedir}/../../NOTICE.txt"/>
<fileset file="${basedir}/../../share/VERSION.txt"/>
</copy>
+ <copy todir="${build.template.dir}">
+ <fileset dir="${template.dir}" includes="**/**"/>
+ </copy>
+ <copy todir="${build.static.dir}">
+ <fileset dir="${static.dir}" includes="**/**"/>
+ </copy>
</target>
<target name="ivy-download" unless="ivy.jar.exists" depends="init">
Modified: avro/trunk/lang/java/ivy.xml
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/ivy.xml?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/ivy.xml (original)
+++ avro/trunk/lang/java/ivy.xml Wed Jul 21 18:15:08 2010
@@ -45,6 +45,7 @@
rev="2.2"/>
<dependency org="org.mortbay.jetty" name="jetty"
rev="6.1.22"/>
+ <dependency org="org.apache.velocity" name="velocity" rev="1.6.4"/>
<dependency org="junit" name="junit" rev="4.8.1" conf="test->default"/>
<dependency org="checkstyle" name="checkstyle" rev="5.0"
conf="test->default"/>
Modified: avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java (original)
+++ avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsPlugin.java Wed Jul 21 18:15:08 2010
@@ -17,8 +17,11 @@
*/
package org.apache.avro.ipc.stats;
+import java.nio.ByteBuffer;
import java.util.Arrays;
+import java.util.Date;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;
@@ -32,14 +35,15 @@ import org.apache.avro.ipc.stats.Stopwat
/**
* Collects count and latency statistics about RPC calls. Keeps
- * data for every method.
+ * data for every method. Can be added to a Requestor (client)
+ * or Responder (server).
*
* This uses milliseconds as the standard unit of measure
* throughout the class, stored in floats.
*/
public class StatsPlugin extends RPCPlugin {
/** Static declaration of histogram buckets. */
- static final Segmenter<String, Float> DEFAULT_SEGMENTER =
+ static final Segmenter<String, Float> LATENCY_SEGMENTER =
new Histogram.TreeMapSegmenter<Float>(new TreeSet<Float>(Arrays.asList(
0f,
25f,
@@ -57,28 +61,74 @@ public class StatsPlugin extends RPCPlug
60000f, // 1 minute
600000f)));
+ static final Segmenter<String, Integer> PAYLOAD_SEGMENTER =
+ new Histogram.TreeMapSegmenter<Integer>(new TreeSet<Integer>(Arrays.asList(
+ 0,
+ 25,
+ 50,
+ 75,
+ 100,
+ 200,
+ 300,
+ 500,
+ 750,
+ 1000, // 1 k
+ 2000,
+ 5000,
+ 10000,
+ 50000,
+ 100000)));
+
/** Per-method histograms.
- * Must be accessed while holding a lock on methodTimings. */
+ * Must be accessed while holding a lock. */
Map<Message, FloatHistogram<?>> methodTimings =
new HashMap<Message, FloatHistogram<?>>();
+ Map<Message, IntegerHistogram<?>> sendPayloads =
+ new HashMap<Message, IntegerHistogram<?>>();
+
+ Map<Message, IntegerHistogram<?>> receivePayloads =
+ new HashMap<Message, IntegerHistogram<?>>();
+
/** RPCs in flight. */
ConcurrentMap<RPCContext, Stopwatch> activeRpcs =
new ConcurrentHashMap<RPCContext, Stopwatch>();
private Ticks ticks;
- private Segmenter<?, Float> segmenter;
+ /** How long I've been alive */
+ public Date startupTime = new Date();
+
+ private Segmenter<?, Float> floatSegmenter;
+ private Segmenter<?, Integer> integerSegmenter;
/** Construct a plugin with custom Ticks and Segmenter implementations. */
- StatsPlugin(Ticks ticks, Segmenter<?, Float> segmenter) {
- this.segmenter = segmenter;
+ StatsPlugin(Ticks ticks, Segmenter<?, Float> floatSegmenter,
+ Segmenter<?, Integer> integerSegmenter) {
+ this.floatSegmenter = floatSegmenter;
+ this.integerSegmenter = integerSegmenter;
this.ticks = ticks;
}
/** Construct a plugin with default (system) ticks, and default
* histogram segmentation. */
public StatsPlugin() {
- this(Stopwatch.SYSTEM_TICKS, DEFAULT_SEGMENTER);
+ this(Stopwatch.SYSTEM_TICKS, LATENCY_SEGMENTER, PAYLOAD_SEGMENTER);
+ }
+
+ /**
+ * Helper to get the size of an RPC payload.
+ */
+ private int getPayloadSize(List<ByteBuffer> payload) {
+ if (payload == null) {
+ return 0;
+ }
+
+ int size = 0;
+ for (ByteBuffer bb: payload) {
+ size = size + bb.limit();
+ }
+
+ return size;
}
@Override
@@ -86,15 +136,65 @@ public class StatsPlugin extends RPCPlug
Stopwatch t = new Stopwatch(ticks);
t.start();
this.activeRpcs.put(context, t);
+
+ synchronized(receivePayloads) {
+ IntegerHistogram<?> h = receivePayloads.get(context.getMessage());
+ if (h == null) {
+ h = createNewIntegerHistogram();
+ receivePayloads.put(context.getMessage(), h);
+ }
+ h.add(getPayloadSize(context.getRequestPayload()));
+ }
}
-
+
@Override
public void serverSendResponse(RPCContext context) {
Stopwatch t = this.activeRpcs.remove(context);
t.stop();
publish(context, t);
+
+ synchronized(sendPayloads) {
+ IntegerHistogram<?> h = sendPayloads.get(context.getMessage());
+ if (h == null) {
+ h = createNewIntegerHistogram();
+ sendPayloads.put(context.getMessage(), h);
+ }
+ h.add(getPayloadSize(context.getResponsePayload()));
+ }
}
-
+
+ @Override
+ public void clientSendRequest(RPCContext context) {
+ Stopwatch t = new Stopwatch(ticks);
+ t.start();
+ this.activeRpcs.put(context, t);
+
+ synchronized(sendPayloads) {
+ IntegerHistogram<?> h = sendPayloads.get(context.getMessage());
+ if (h == null) {
+ h = createNewIntegerHistogram();
+ sendPayloads.put(context.getMessage(), h);
+ }
+ h.add(getPayloadSize(context.getRequestPayload()));
+ }
+ }
+
+ @Override
+ public void clientReceiveResponse(RPCContext context) {
+ Stopwatch t = this.activeRpcs.remove(context);
+ t.stop();
+ publish(context, t);
+
+ synchronized(receivePayloads) {
+ IntegerHistogram<?> h = receivePayloads.get(context.getMessage());
+ if (h == null) {
+ h = createNewIntegerHistogram();
+ receivePayloads.put(context.getMessage(), h);
+ }
+ h.add(getPayloadSize(context.getRequestPayload()));
+ }
+ }
+
/** Adds timing to the histograms. */
private void publish(RPCContext context, Stopwatch t) {
Message message = context.getMessage();
@@ -102,7 +202,7 @@ public class StatsPlugin extends RPCPlug
synchronized(methodTimings) {
FloatHistogram<?> h = methodTimings.get(context.getMessage());
if (h == null) {
- h = createNewHistogram();
+ h = createNewFloatHistogram();
methodTimings.put(context.getMessage(), h);
}
h.add(nanosToMillis(t.elapsedNanos()));
@@ -110,10 +210,15 @@ public class StatsPlugin extends RPCPlug
}
@SuppressWarnings("unchecked")
- private FloatHistogram<?> createNewHistogram() {
- return new FloatHistogram(segmenter);
+ private FloatHistogram<?> createNewFloatHistogram() {
+ return new FloatHistogram(floatSegmenter);
}
+ @SuppressWarnings("unchecked")
+ private IntegerHistogram<?> createNewIntegerHistogram() {
+ return new IntegerHistogram(integerSegmenter);
+ }
+
/** Converts nanoseconds to milliseconds. */
static float nanosToMillis(long elapsedNanos) {
return elapsedNanos / 1000000.0f;
Modified: avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java (original)
+++ avro/trunk/lang/java/src/java/org/apache/avro/ipc/stats/StatsServlet.java Wed Jul 21 18:15:08 2010
@@ -19,15 +19,33 @@ package org.apache.avro.ipc.stats;
import java.io.IOException;
import java.io.Writer;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
import java.util.Map.Entry;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
+import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.avro.Protocol.Message;
import org.apache.avro.ipc.RPCContext;
+import org.mortbay.jetty.servlet.DefaultServlet;
+import org.mortbay.resource.Resource;
/**
* Exposes information provided by a StatsPlugin as
@@ -36,71 +54,217 @@ import org.apache.avro.ipc.RPCContext;
* This class follows the same synchronization conventions
* as StatsPlugin, to avoid requiring StatsPlugin to serve
* a copy of the data.
- */
+ */
public class StatsServlet extends HttpServlet {
private final StatsPlugin statsPlugin;
+ private VelocityEngine velocityEngine;
+ private static final SimpleDateFormat FORMATTER =
+ new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
- public StatsServlet(StatsPlugin statsPlugin) {
+ public StatsServlet(StatsPlugin statsPlugin) throws UnavailableException {
this.statsPlugin = statsPlugin;
+ this.velocityEngine = new VelocityEngine();
+
+ // These two properties tell Velocity to use its own classpath-based loader
+ velocityEngine.addProperty("resource.loader", "class");
+ velocityEngine.addProperty("class.resource.loader.class",
+ "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
+ }
+
+ /* Helper class to store per-message data which is passed to templates.
+ *
+ * The template expects a list of charts, each of which is parameterized by
+ * map key-value string attributes. */
+ public class RenderableMessage { // Velocity brakes if not public
+ public String name;
+ public int numCalls;
+ public ArrayList<HashMap<String, String>> charts;
+
+ public RenderableMessage(String name) {
+ this.name = name;
+ this.charts = new ArrayList<HashMap<String, String>>();
+ }
+
+ public ArrayList<HashMap<String, String>> getCharts() {
+ return this.charts;
+ }
+
+ public String getname() {
+ return this.name;
+ }
+
+ public int getNumCalls() {
+ return this.numCalls;
+ }
}
+ /* Surround each string in an array with
+ * quotation marks and escape existing quotes.
+ *
+ * This is useful when we have an array of strings that we want to turn into
+ * a javascript array declaration.
+ */
+ protected static List<String> escapeStringArray(List<String> input) {
+ for (int i = 0; i < input.size(); i++) {
+ input.set(i, "\"" + input.get(i).replace("\"", "\\\"") + "\"");
+ }
+ return input;
+ }
+
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
- writeStats(resp.getWriter());
+ String url = req.getRequestURL().toString();
+ String[] parts = url.split("//")[1].split("/");
+
+ try {
+ writeStats(resp.getWriter());
+ }
+ catch (Exception e) {
+ e.printStackTrace();
+ }
}
void writeStats(Writer w) throws IOException {
- w.append("<html><head><title>Avro RPC Stats</title></head>");
- w.append("<body><h1>Avro RPC Stats</h1>");
-
- w.append("<h2>Active RPCs</h2>");
- w.append("<ol>");
- for (Entry<RPCContext, Stopwatch> rpc : this.statsPlugin.activeRpcs.entrySet()) {
- writeActiveRpc(w, rpc.getKey(), rpc.getValue());
+ VelocityContext context = new VelocityContext();
+ context.put("title", "Avro RPC Stats");
+
+ ArrayList<String> rpcs = new ArrayList<String>(); // in flight rpcs
+
+ ArrayList<RenderableMessage> messages =
+ new ArrayList<RenderableMessage>();
+
+ for (Entry<RPCContext, Stopwatch> rpc :
+ this.statsPlugin.activeRpcs.entrySet()) {
+ rpcs.add(renderActiveRpc(rpc.getKey(), rpc.getValue()));
}
- w.append("</ol>");
-
- w.append("<h2>Per-method Timing</h2>");
+
+ // Get set of all seen messages
+ Set<Message> keys = null;
synchronized(this.statsPlugin.methodTimings) {
- for (Entry<Message, FloatHistogram<?>> e :
- this.statsPlugin.methodTimings.entrySet()) {
- writeMethod(w, e.getKey(), e.getValue());
+ keys = this.statsPlugin.methodTimings.keySet();
+
+ for (Message m: keys) {
+ messages.add(renderMethod(m));
}
}
- w.append("</body></html>");
+
+ context.put("inFlightRpcs", rpcs);
+ context.put("messages", messages);
+
+ context.put("currTime", FORMATTER.format(new Date()));
+ context.put("startupTime", FORMATTER.format(statsPlugin.startupTime));
+
+ Template t;
+ try {
+ t = velocityEngine.getTemplate(
+ "org/apache/avro/ipc/stats/templates/statsview.vm");
+ } catch (ResourceNotFoundException e) {
+ throw new IOException();
+ } catch (ParseErrorException e) {
+ throw new IOException();
+ } catch (Exception e) {
+ throw new IOException();
+ }
+ t.merge(context, w);
}
- private void writeActiveRpc(Writer w, RPCContext rpc, Stopwatch stopwatch) throws IOException {
- w.append("<li>").append(rpc.getMessage().getName()).append(": ");
- w.append(formatMillis(StatsPlugin.nanosToMillis(stopwatch.elapsedNanos())));
- w.append("</li>");
- }
-
- private void writeMethod(Writer w, Message message, FloatHistogram<?> hist) throws IOException {
- w.append("<h3>").append(message.getName()).append("</h3>");
- w.append("<p>Number of calls: ");
- w.append(Integer.toString(hist.getCount()));
- w.append("</p><p>Average Duration: ");
- w.append(formatMillis(hist.getMean()));
- w.append("</p>");
- w.append("</p><p>Std Dev: ");
- w.append(formatMillis(hist.getUnbiasedStdDev()));
- w.append("</p>");
-
- w.append("<dl>");
-
- for (Histogram.Entry<?> e : hist.entries()) {
- w.append("<dt>");
- w.append(e.bucket.toString());
- w.append("</dt>");
- w.append("<dd>").append(Integer.toString(e.count)).append("</dd>");
- w.append("</dt>");
- }
- w.append("</dl>");
+ private String renderActiveRpc(RPCContext rpc, Stopwatch stopwatch)
+ throws IOException {
+ String out = new String();
+ out += rpc.getMessage().getName() + ": " +
+ formatMillis(StatsPlugin.nanosToMillis(stopwatch.elapsedNanos()));
+ return out;
}
+
+ private RenderableMessage renderMethod(Message message) {
+ RenderableMessage out = new RenderableMessage(message.getName());
+
+ synchronized(this.statsPlugin.methodTimings) {
+ FloatHistogram<?> hist = this.statsPlugin.methodTimings.get(message);
+ out.numCalls = hist.getCount();
+
+ HashMap<String, String> latencyBar = new HashMap<String, String>();
+ // Fill in chart attributes for velocity
+ latencyBar.put("type", "bar");
+ latencyBar.put("title", "All-Time Latency");
+ latencyBar.put("units", "ms");
+ latencyBar.put("numCalls", Integer.toString(hist.getCount()));
+ latencyBar.put("avg", Float.toString(hist.getMean()));
+ latencyBar.put("stdDev", Float.toString(hist.getUnbiasedStdDev()));
+ latencyBar.put("labelStr",
+ Arrays.toString(hist.getSegmenter().getBoundaryLabels().toArray()));
+ latencyBar.put("boundaryStr",
+ Arrays.toString(escapeStringArray(hist.getSegmenter().
+ getBucketLabels()).toArray()));
+ latencyBar.put("dataStr", Arrays.toString(hist.getHistogram()));
+ out.charts.add(latencyBar);
+
+ HashMap<String, String> latencyDot = new HashMap<String, String>();
+ latencyDot.put("title", "Latency");
+ latencyDot.put("type", "dot");
+ latencyDot.put("dataStr",
+ Arrays.toString(hist.getRecentAdditions().toArray()));
+ out.charts.add(latencyDot);
+ }
+
+ synchronized(this.statsPlugin.sendPayloads) {
+ IntegerHistogram<?> hist = this.statsPlugin.sendPayloads.get(message);
+ HashMap<String, String> latencyBar = new HashMap<String, String>();
+ // Fill in chart attributes for velocity
+ latencyBar.put("type", "bar");
+ latencyBar.put("title", "All-Time Send Payload");
+ latencyBar.put("units", "ms");
+ latencyBar.put("numCalls", Integer.toString(hist.getCount()));
+ latencyBar.put("avg", Float.toString(hist.getMean()));
+ latencyBar.put("stdDev", Float.toString(hist.getUnbiasedStdDev()));
+ latencyBar.put("labelStr",
+ Arrays.toString(hist.getSegmenter().getBoundaryLabels().toArray()));
+ latencyBar.put("boundaryStr",
+ Arrays.toString(escapeStringArray(hist.getSegmenter().
+ getBucketLabels()).toArray()));
+ latencyBar.put("dataStr", Arrays.toString(hist.getHistogram()));
+ out.charts.add(latencyBar);
+
+ HashMap<String, String> latencyDot = new HashMap<String, String>();
+ latencyDot.put("title", "Send Payload");
+ latencyDot.put("type", "dot");
+ latencyDot.put("dataStr",
+ Arrays.toString(hist.getRecentAdditions().toArray()));
+ out.charts.add(latencyDot);
+ }
+
+ synchronized(this.statsPlugin.receivePayloads) {
+ IntegerHistogram<?> hist = this.statsPlugin.receivePayloads.get(message);
+ HashMap<String, String> latencyBar = new HashMap<String, String>();
+ // Fill in chart attributes for velocity
+ latencyBar.put("type", "bar");
+ latencyBar.put("title", "All-Time Receive Payload");
+ latencyBar.put("units", "ms");
+ latencyBar.put("numCalls", Integer.toString(hist.getCount()));
+ latencyBar.put("avg", Float.toString(hist.getMean()));
+ latencyBar.put("stdDev", Float.toString(hist.getUnbiasedStdDev()));
+ latencyBar.put("labelStr",
+ Arrays.toString(hist.getSegmenter().getBoundaryLabels().toArray()));
+ latencyBar.put("boundaryStr",
+ Arrays.toString(escapeStringArray(hist.getSegmenter().
+ getBucketLabels()).toArray()));
+ latencyBar.put("dataStr", Arrays.toString(hist.getHistogram()));
+ out.charts.add(latencyBar);
+
+ HashMap<String, String> latencyDot = new HashMap<String, String>();
+ latencyDot.put("title", "Recv Payload");
+ latencyDot.put("type", "dot");
+ latencyDot.put("dataStr",
+ Arrays.toString(hist.getRecentAdditions().toArray()));
+ out.charts.add(latencyDot);
+ }
+
+ return out;
+ }
+
private CharSequence formatMillis(float millis) {
return String.format("%.0fms", millis);
}
Modified: avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java
URL: http://svn.apache.org/viewvc/avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java (original)
+++ avro/trunk/lang/java/src/test/java/org/apache/avro/ipc/stats/TestStatsPluginAndServlet.java Wed Jul 21 18:15:08 2010
@@ -22,6 +22,11 @@ import static org.junit.Assert.assertTru
import java.io.IOException;
import java.io.StringWriter;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.util.Random;
+
+import javax.servlet.UnavailableException;
import org.apache.avro.Protocol;
import org.apache.avro.Protocol.Message;
@@ -31,14 +36,12 @@ import org.apache.avro.generic.GenericRe
import org.apache.avro.generic.GenericResponder;
import org.apache.avro.ipc.AvroRemoteException;
import org.apache.avro.ipc.HttpServer;
+import org.apache.avro.ipc.HttpTransceiver;
import org.apache.avro.ipc.LocalTransceiver;
import org.apache.avro.ipc.RPCContext;
import org.apache.avro.ipc.Responder;
import org.apache.avro.ipc.Transceiver;
import org.junit.Test;
-import org.mortbay.jetty.Server;
-import org.mortbay.jetty.servlet.Context;
-import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.log.Log;
public class TestStatsPluginAndServlet {
@@ -53,9 +56,18 @@ public class TestStatsPluginAndServlet {
/** Returns an HTML string. */
private String generateServletResponse(StatsPlugin statsPlugin)
throws IOException {
- StatsServlet servlet = new StatsServlet(statsPlugin);
+ StatsServlet servlet;
+ try {
+ servlet = new StatsServlet(statsPlugin);
+ } catch (UnavailableException e1) {
+ throw new IOException();
+ }
StringWriter w = new StringWriter();
- servlet.writeStats(w);
+ try {
+ servlet.writeStats(w);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
String o = w.toString();
return o;
}
@@ -95,13 +107,14 @@ public class TestStatsPluginAndServlet {
}
String o = generateServletResponse(statsPlugin);
- assertTrue(o.contains("Number of calls: 10"));
+ assertTrue(o.contains("10 calls"));
}
@Test
public void testMultipleRPCs() throws IOException {
FakeTicks t = new FakeTicks();
- StatsPlugin statsPlugin = new StatsPlugin(t, StatsPlugin.DEFAULT_SEGMENTER);
+ StatsPlugin statsPlugin = new StatsPlugin(t, StatsPlugin.LATENCY_SEGMENTER,
+ StatsPlugin.PAYLOAD_SEGMENTER);
RPCContext context1 = makeContext();
RPCContext context2 = makeContext();
statsPlugin.serverReceiveRequest(context1);
@@ -114,11 +127,23 @@ public class TestStatsPluginAndServlet {
statsPlugin.serverSendResponse(context1);
t.passTime(900*MS); // second takes 900ms
statsPlugin.serverSendResponse(context2);
-
r = generateServletResponse(statsPlugin);
- assertTrue(r.contains("Average Duration: 500ms"));
+ assertTrue(r.contains("Average: 500.0ms"));
}
+ @Test
+ public void testPayloadSize() throws IOException {
+ Responder r = new TestResponder(protocol);
+ StatsPlugin statsPlugin = new StatsPlugin();
+ r.addRPCPlugin(statsPlugin);
+ Transceiver t = new LocalTransceiver(r);
+ makeRequest(t);
+
+ String resp = generateServletResponse(statsPlugin);
+ assertTrue(resp.contains("Average: 2.0"));
+
+ }
+
private RPCContext makeContext() {
RPCContext context = new RPCContext();
context.setMessage(message);
@@ -144,7 +169,8 @@ public class TestStatsPluginAndServlet {
}
/**
- * Demo program for using RPC stats. Tool can be used (as below)
+ * Demo program for using RPC stats. This automatically generates
+ * client RPC requests. Alternatively a can be used (as below)
* to trigger RPCs.
* <pre>
* java -jar build/avro-tools-*.jar rpcsend '{"protocol":"sleepy","namespace":null,"types":[],"messages":{"sleep":{"request":[{"name":"millis","type":"long"}],"response":"null"}}}' sleep localhost 7002 '{"millis": 20000}'
@@ -158,7 +184,8 @@ public class TestStatsPluginAndServlet {
}
Protocol protocol = Protocol.parse("{\"protocol\": \"sleepy\", "
+ "\"messages\": { \"sleep\": {"
- + " \"request\": [{\"name\": \"millis\", \"type\": \"long\"}], "
+ + " \"request\": [{\"name\": \"millis\", \"type\": \"long\"}," +
+ "{\"name\": \"data\", \"type\": \"bytes\"}], "
+ " \"response\": \"null\"} } }");
Log.info("Using protocol: " + protocol.toString());
Responder r = new SleepyResponder(protocol);
@@ -169,11 +196,24 @@ public class TestStatsPluginAndServlet {
HttpServer avroServer = new HttpServer(r, Integer.parseInt(args[0]));
avroServer.start();
- // Ideally we could use the same Jetty server
- Server httpServer = new Server(Integer.parseInt(args[1]));
- new Context(httpServer, "/").addServlet(
- new ServletHolder(new StatsServlet(p)), "/*");
- httpServer.start();
- httpServer.join();
+ StatsServer ss = new StatsServer(p, 8080);
+
+ HttpTransceiver trans = new HttpTransceiver(
+ new URL("http://localhost:" + Integer.parseInt(args[0])));
+ GenericRequestor req = new GenericRequestor(protocol, trans);
+
+
+ while(true) {
+ Thread.sleep(1000);
+ GenericRecord params = new GenericData.Record(protocol.getMessages().get(
+ "sleep").getRequest());
+ Random rand = new Random();
+ params.put("millis", Math.abs(rand.nextLong()) % 1000);
+ int payloadSize = Math.abs(rand.nextInt()) % 10000;
+ byte[] payload = new byte[payloadSize];
+ rand.nextBytes(payload);
+ params.put("data", ByteBuffer.wrap(payload));
+ req.request("sleep", params);
+ }
}
}
Modified: avro/trunk/share/rat-excludes.txt
URL: http://svn.apache.org/viewvc/avro/trunk/share/rat-excludes.txt?rev=966342&r1=966341&r2=966342&view=diff
==============================================================================
--- avro/trunk/share/rat-excludes.txt (original)
+++ avro/trunk/share/rat-excludes.txt Wed Jul 21 18:15:08 2010
@@ -30,3 +30,5 @@ lang/c/jansson/**
lang/c/src/queue.h
lang/c/src/st.h
lang/c/src/st.c
+lang/java/src/java/org/apache/avro/ipc/stats/static/*.js
+lang/java/src/java/org/apache/avro/ipc/stats/static/*.css