From e4b2f0fcc2fe80692a00bd7e2876abfda71d37f9 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 5 Dec 2014 13:37:41 +0100 Subject: [PATCH] Speed up chunked framing Instead of allocating temporary byte arrays and ByteBufs, insert chunk headers explicitly and transfer bytes directly from input message to output. Change-Id: Id300cbb9bc39b36e9a07def3d50c948edda7f570 Signed-off-by: Robert Varga --- .../ChunkedFramingMechanismEncoder.java | 32 ++++++++----------- .../ChunkedFramingMechanismEncoderTest.java | 32 ++++++------------- .../messages/NetconfMessageConstants.java | 1 + 3 files changed, 24 insertions(+), 41 deletions(-) diff --git a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoder.java b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoder.java index c4cddb802e..2287a58602 100644 --- a/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoder.java +++ b/opendaylight/netconf/netconf-netty-util/src/main/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoder.java @@ -8,13 +8,12 @@ package org.opendaylight.controller.netconf.nettyutil.handler; +import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import org.opendaylight.controller.netconf.util.messages.NetconfMessageConstants; -import org.opendaylight.controller.netconf.util.messages.NetconfMessageHeader; public class ChunkedFramingMechanismEncoder extends MessageToByteEncoder { public static final int DEFAULT_CHUNK_SIZE = 8192; @@ -27,9 +26,8 @@ public class ChunkedFramingMechanismEncoder extends MessageToByteEncoder MIN_CHUNK_SIZE); - Preconditions.checkArgument(chunkSize < MAX_CHUNK_SIZE); + public ChunkedFramingMechanismEncoder(final int chunkSize) { + Preconditions.checkArgument(chunkSize >= MIN_CHUNK_SIZE && chunkSize <= MAX_CHUNK_SIZE, "Unsupported chunk size %s", chunkSize); this.chunkSize = chunkSize; } @@ -38,19 +36,17 @@ public class ChunkedFramingMechanismEncoder extends MessageToByteEncoder chunkSize) { - ByteBuf chunk = Unpooled.buffer(chunkSize); - chunk.writeBytes(createChunkHeader(chunkSize)); - chunk.writeBytes(msg.readBytes(chunkSize)); - ctx.write(chunk); - } - out.writeBytes(createChunkHeader(msg.readableBytes())); - out.writeBytes(msg.readBytes(msg.readableBytes())); - out.writeBytes(NetconfMessageConstants.END_OF_CHUNK); - } + protected void encode(final ChannelHandlerContext ctx, final ByteBuf msg, final ByteBuf out) { + do { + final int xfer = Math.min(chunkSize, msg.readableBytes()); + + out.writeBytes(NetconfMessageConstants.START_OF_CHUNK); + out.writeBytes(String.valueOf(xfer).getBytes(Charsets.US_ASCII)); + out.writeByte('\n'); - private ByteBuf createChunkHeader(int chunkSize) { - return Unpooled.wrappedBuffer(NetconfMessageHeader.toBytes(chunkSize)); + out.writeBytes(msg, xfer); + } while (msg.isReadable()); + + out.writeBytes(NetconfMessageConstants.END_OF_CHUNK); } } diff --git a/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoderTest.java b/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoderTest.java index 556bece43f..4488c5e1be 100644 --- a/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoderTest.java +++ b/opendaylight/netconf/netconf-netty-util/src/test/java/org/opendaylight/controller/netconf/nettyutil/handler/ChunkedFramingMechanismEncoderTest.java @@ -9,20 +9,16 @@ package org.opendaylight.controller.netconf.nettyutil.handler; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Mockito.doAnswer; -import com.google.common.collect.Lists; +import static org.junit.Assert.assertTrue; +import com.google.common.base.Charsets; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import java.util.List; +import java.nio.ByteBuffer; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; -import org.opendaylight.controller.netconf.util.messages.NetconfMessageConstants; public class ChunkedFramingMechanismEncoderTest { @@ -48,30 +44,20 @@ public class ChunkedFramingMechanismEncoderTest { @Test public void testEncode() throws Exception { - final List chunks = Lists.newArrayList(); - doAnswer(new Answer() { - @Override - public Object answer(final InvocationOnMock invocation) throws Throwable { - chunks.add((ByteBuf) invocation.getArguments()[0]); - return null; - } - }).when(ctx).write(anyObject()); - final ChunkedFramingMechanismEncoder encoder = new ChunkedFramingMechanismEncoder(chunkSize); final int lastChunkSize = 20; final ByteBuf src = Unpooled.wrappedBuffer(getByteArray(chunkSize * 4 + lastChunkSize)); final ByteBuf destination = Unpooled.buffer(); encoder.encode(ctx, src, destination); - assertEquals(4, chunks.size()); - final int framingSize = "#256\n".getBytes().length + 1/* new line at end */; + assertEquals(1077, destination.readableBytes()); - for (final ByteBuf chunk : chunks) { - assertEquals(chunkSize + framingSize, chunk.readableBytes()); - } + byte[] buf = new byte[destination.readableBytes()]; + destination.readBytes(buf); + String s = Charsets.US_ASCII.decode(ByteBuffer.wrap(buf)).toString(); - final int lastFramingSize = "#20\n".length() + NetconfMessageConstants.END_OF_CHUNK.length + 1/* new line at end */; - assertEquals(lastChunkSize + lastFramingSize, destination.readableBytes()); + assertTrue(s.startsWith("\n#256\na")); + assertTrue(s.endsWith("\n#20\naaaaaaaaaaaaaaaaaaaa\n##\n")); } private byte[] getByteArray(final int size) { diff --git a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageConstants.java b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageConstants.java index 5c2770a8c1..89285d18c0 100644 --- a/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageConstants.java +++ b/opendaylight/netconf/netconf-util/src/main/java/org/opendaylight/controller/netconf/util/messages/NetconfMessageConstants.java @@ -27,6 +27,7 @@ public final class NetconfMessageConstants { public static final int MAX_HEADER_LENGTH = 13; + public static final byte[] START_OF_CHUNK = "\n#".getBytes(Charsets.UTF_8); public static final byte[] END_OF_CHUNK = "\n##\n".getBytes(Charsets.UTF_8); } -- 2.36.6