package com.netflix.discovery.converters; import javax.ws.rs.core.MediaType; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import com.netflix.appinfo.InstanceInfo; import com.netflix.discovery.converters.jackson.AbstractEurekaJacksonCodec; import com.netflix.discovery.converters.jackson.EurekaJsonJacksonCodec; import com.netflix.discovery.converters.jackson.EurekaXmlJacksonCodec; import com.netflix.discovery.shared.Application; import com.netflix.discovery.shared.Applications; import com.netflix.discovery.util.InstanceInfoGenerator; /** * @author Tomasz Bak */ public class CodecLoadTester { private final List<InstanceInfo> instanceInfoList = new ArrayList<InstanceInfo>(); private final List<Application> applicationList = new ArrayList<Application>(); private final Applications applications; private final EntityBodyConverter xstreamCodec = new EntityBodyConverter(); private final EurekaJacksonCodec legacyJacksonCodec = new EurekaJacksonCodec(); private final EurekaJsonJacksonCodec jsonCodecNG = new EurekaJsonJacksonCodec(); private final EurekaJsonJacksonCodec jsonCodecNgCompact = new EurekaJsonJacksonCodec(KeyFormatter.defaultKeyFormatter(), true); private final EurekaXmlJacksonCodec xmlCodecNG = new EurekaXmlJacksonCodec(); private final EurekaXmlJacksonCodec xmlCodecNgCompact = new EurekaXmlJacksonCodec(KeyFormatter.defaultKeyFormatter(), true); static class FirstHolder { Applications value; } static class SecondHolder { Applications value; } private FirstHolder firstHolder = new FirstHolder(); private SecondHolder secondHolder = new SecondHolder(); public CodecLoadTester(int instanceCount, int appCount) { Iterator<InstanceInfo> instanceIt = InstanceInfoGenerator.newBuilder(instanceCount, appCount) .withMetaData(true).build().serviceIterator(); applications = new Applications(); int appIdx = 0; while (instanceIt.hasNext()) { InstanceInfo next = instanceIt.next(); instanceInfoList.add(next); if (applicationList.size() <= appIdx) { applicationList.add(new Application(next.getAppName())); } applicationList.get(appIdx).addInstance(next); appIdx = (appIdx + 1) % appCount; } for (Application app : applicationList) { applications.addApplication(app); } applications.setAppsHashCode(applications.getReconcileHashCode()); firstHolder.value = applications; } public CodecLoadTester(String[] args) throws Exception { if (args.length != 1) { System.err.println("ERROR: too many command line arguments; file name expected only"); throw new IllegalArgumentException(); } String fileName = args[0]; Applications applications; try { System.out.println("Attempting to load " + fileName + " in XML format..."); applications = loadWithCodec(fileName, MediaType.APPLICATION_XML_TYPE); } catch (Exception e) { System.out.println("Attempting to load " + fileName + " in JSON format..."); applications = loadWithCodec(fileName, MediaType.APPLICATION_JSON_TYPE); } this.applications = applications; long totalInstances = 0; for (Application a : applications.getRegisteredApplications()) { totalInstances += a.getInstances().size(); } System.out.printf("Loaded %d applications with %d instances\n", applications.getRegisteredApplications().size(), totalInstances); firstHolder.value = applications; } private Applications loadWithCodec(String fileName, MediaType mediaType) throws IOException { FileInputStream fis = new FileInputStream(fileName); BufferedInputStream bis = new BufferedInputStream(fis); return (Applications) xstreamCodec.read(bis, Applications.class, mediaType); } public void runApplicationsLoadTest(int loops, Func0<Applications> action) { long size = 0; for (int i = 0; i < loops; i++) { size += action.call(applications); } System.out.println("Average applications object size=" + formatSize(size / loops)); } public void runApplicationLoadTest(int loops, Func0<Application> action) { for (int i = 0; i < loops; i++) { action.call(applicationList.get(i % applicationList.size())); } } public void runInstanceInfoLoadTest(int loops, Func0<InstanceInfo> action) { for (int i = 0; i < loops; i++) { action.call(instanceInfoList.get(i % instanceInfoList.size())); } } public void runInstanceInfoIntervalTest(int batch, int intervalMs, long durationSec, Func0 action) { long startTime = System.currentTimeMillis(); long endTime = startTime + durationSec * 1000; long now; do { now = System.currentTimeMillis(); runInstanceInfoLoadTest(batch, action); long waiting = intervalMs - (System.currentTimeMillis() - now); System.out.println("Waiting " + waiting + "ms"); if (waiting > 0) { try { Thread.sleep(waiting); } catch (InterruptedException e) { // IGNORE } } } while (now < endTime); } public void runApplicationIntervalTest(int batch, int intervalMs, long durationSec, Func0 action) { long startTime = System.currentTimeMillis(); long endTime = startTime + durationSec * 1000; long now; do { now = System.currentTimeMillis(); runApplicationLoadTest(batch, action); long waiting = intervalMs - (System.currentTimeMillis() - now); System.out.println("Waiting " + waiting + "ms"); if (waiting > 0) { try { Thread.sleep(waiting); } catch (InterruptedException e) { // IGNORE } } } while (now < endTime); } private static String formatSize(long size) { if (size < 1000) { return String.format("%d [bytes]", size); } if (size < 1024 * 1024) { return String.format("%.2f [KB]", size / 1024f); } return String.format("%.2f [MB]", size / (1024f * 1024f)); } interface Func0<T> { int call(T data); } Func0 legacyJacksonAction = new Func0<Object>() { @Override public int call(Object object) { ByteArrayOutputStream captureStream = new ByteArrayOutputStream(); try { legacyJacksonCodec.writeTo(object, captureStream); byte[] bytes = captureStream.toByteArray(); InputStream source = new ByteArrayInputStream(bytes); legacyJacksonCodec.readValue(object.getClass(), source); return bytes.length; } catch (IOException e) { throw new RuntimeException("unexpected", e); } } }; Func0 xstreamJsonAction = new Func0<Object>() { @Override public int call(Object object) { ByteArrayOutputStream captureStream = new ByteArrayOutputStream(); try { xstreamCodec.write(object, captureStream, MediaType.APPLICATION_JSON_TYPE); byte[] bytes = captureStream.toByteArray(); InputStream source = new ByteArrayInputStream(bytes); xstreamCodec.read(source, InstanceInfo.class, MediaType.APPLICATION_JSON_TYPE); return bytes.length; } catch (IOException e) { throw new RuntimeException("unexpected", e); } } }; Func0 xstreamXmlAction = new Func0<Object>() { @Override public int call(Object object) { ByteArrayOutputStream captureStream = new ByteArrayOutputStream(); try { xstreamCodec.write(object, captureStream, MediaType.APPLICATION_XML_TYPE); byte[] bytes = captureStream.toByteArray(); InputStream source = new ByteArrayInputStream(bytes); xstreamCodec.read(source, InstanceInfo.class, MediaType.APPLICATION_XML_TYPE); return bytes.length; } catch (IOException e) { throw new RuntimeException("unexpected", e); } } }; Func0 createJacksonNgAction(final MediaType mediaType, final boolean compact) { return new Func0<Object>() { @Override public int call(Object object) { AbstractEurekaJacksonCodec codec; if (mediaType.equals(MediaType.APPLICATION_JSON_TYPE)) { codec = compact ? jsonCodecNgCompact : jsonCodecNG; } else { codec = compact ? xmlCodecNgCompact : xmlCodecNG; } ByteArrayOutputStream captureStream = new ByteArrayOutputStream(); try { codec.writeTo(object, captureStream); byte[] bytes = captureStream.toByteArray(); InputStream source = new ByteArrayInputStream(bytes); Applications readValue = codec.getObjectMapper(object.getClass()).readValue(source, Applications.class); secondHolder.value = readValue; return bytes.length; } catch (IOException e) { throw new RuntimeException("unexpected", e); } } }; } public void runFullSpeed() { int loop = 5; System.gc(); long start = System.currentTimeMillis(); // runInstanceInfoLoadTest(loop, legacyJacksonAction); // runInstanceInfoLoadTest(loop, xstreamAction); // runApplicationLoadTest(loop, legacyJacksonAction); // runApplicationLoadTest(loop, xstreamAction); // ---------------------------------------------------------------- // Applications // runApplicationsLoadTest(loop, xstreamJsonAction); // runApplicationsLoadTest(loop, xstreamXmlAction); // runApplicationsLoadTest(loop, legacyJacksonAction); runApplicationsLoadTest(loop, createJacksonNgAction(MediaType.APPLICATION_JSON_TYPE, false)); long executionTime = System.currentTimeMillis() - start; System.out.printf("Execution time: %d[ms]\n", executionTime); } public void runIntervals() { int batch = 1500; int intervalMs = 1000; long durationSec = 600; // runInstanceInfoIntervalTest(batch, intervalMs, durationSec, legacyJacksonAction); runInstanceInfoIntervalTest(batch, intervalMs, durationSec, xstreamJsonAction); // runApplicationIntervalTest(batch, intervalMs, durationSec, legacyJacksonAction); // runApplicationIntervalTest(batch, intervalMs, durationSec, xstreamAction); } public static void main(String[] args) throws Exception { CodecLoadTester loadTester; if (args.length == 0) { loadTester = new CodecLoadTester(2000, 40); } else { loadTester = new CodecLoadTester(args); } loadTester.runFullSpeed(); Thread.sleep(100000); } }