/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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 org.apache.avro.ipc.stats; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; 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.AvroRemoteException; import org.apache.avro.Protocol; import org.apache.avro.Protocol.Message; import org.apache.avro.generic.GenericData; import org.apache.avro.generic.GenericRecord; 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.apache.avro.ipc.generic.GenericRequestor; import org.apache.avro.ipc.generic.GenericResponder; import org.junit.Test; import org.mortbay.log.Log; public class TestStatsPluginAndServlet { Protocol protocol = Protocol.parse("" + "{\"protocol\": \"Minimal\", " + "\"messages\": { \"m\": {" + " \"request\": [{\"name\": \"x\", \"type\": \"int\"}], " + " \"response\": \"int\"} } }"); Message message = protocol.getMessages().get("m"); private static final long MS = 1000*1000L; /** Returns an HTML string. */ private String generateServletResponse(StatsPlugin statsPlugin) throws IOException { StatsServlet servlet; try { servlet = new StatsServlet(statsPlugin); } catch (UnavailableException e1) { throw new IOException(); } StringWriter w = new StringWriter(); try { servlet.writeStats(w); } catch (Exception e) { e.printStackTrace(); } String o = w.toString(); return o; } /** Expects 0 and returns 1. */ static class TestResponder extends GenericResponder { public TestResponder(Protocol local) { super(local); } @Override public Object respond(Message message, Object request) throws AvroRemoteException { assertEquals(0, ((GenericRecord) request).get("x")); return 1; } } private void makeRequest(Transceiver t) throws IOException { GenericRecord params = new GenericData.Record(protocol.getMessages().get( "m").getRequest()); params.put("x", 0); GenericRequestor r = new GenericRequestor(protocol, t); assertEquals(1, r.request("m", params)); } @Test public void testFullServerPath() throws IOException { Responder r = new TestResponder(protocol); StatsPlugin statsPlugin = new StatsPlugin(); r.addRPCPlugin(statsPlugin); Transceiver t = new LocalTransceiver(r); for (int i = 0; i < 10; ++i) { makeRequest(t); } String o = generateServletResponse(statsPlugin); assertTrue(o.contains("10 calls")); } @Test public void testMultipleRPCs() throws IOException { FakeTicks t = new FakeTicks(); StatsPlugin statsPlugin = new StatsPlugin(t, StatsPlugin.LATENCY_SEGMENTER, StatsPlugin.PAYLOAD_SEGMENTER); RPCContext context1 = makeContext(); RPCContext context2 = makeContext(); statsPlugin.serverReceiveRequest(context1); t.passTime(100*MS); // first takes 100ms statsPlugin.serverReceiveRequest(context2); String r = generateServletResponse(statsPlugin); // Check in progress RPCs assertTrue(r.contains("m: 0ms")); assertTrue(r.contains("m: 100ms")); statsPlugin.serverSendResponse(context1); t.passTime(900*MS); // second takes 900ms statsPlugin.serverSendResponse(context2); r = generateServletResponse(statsPlugin); 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); return context; } /** Sleeps as requested. */ private static class SleepyResponder extends GenericResponder { public SleepyResponder(Protocol local) { super(local); } @Override public Object respond(Message message, Object request) throws AvroRemoteException { try { Thread.sleep((Long)((GenericRecord)request).get("millis")); } catch (InterruptedException e) { throw new AvroRemoteException(e); } return null; } } /** * 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}' * </pre> * @param args * @throws Exception */ public static void main(String[] args) throws Exception { if (args.length == 0) { args = new String[] { "7002", "7003" }; } Protocol protocol = Protocol.parse("{\"protocol\": \"sleepy\", " + "\"messages\": { \"sleep\": {" + " \"request\": [{\"name\": \"millis\", \"type\": \"long\"}," + "{\"name\": \"data\", \"type\": \"bytes\"}], " + " \"response\": \"null\"} } }"); Log.info("Using protocol: " + protocol.toString()); Responder r = new SleepyResponder(protocol); StatsPlugin p = new StatsPlugin(); r.addRPCPlugin(p); // Start Avro server HttpServer avroServer = new HttpServer(r, Integer.parseInt(args[0])); avroServer.start(); 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); } } }