SslHandler benchmark and SslEngine multiple packets benchmark

Motivation:
We currently don't have a benchmark which includes SslHandler. The SslEngine benchmarks also always include a single TLS packet when encoding/decoding. In practice when reading data from the network there may be multiple TLS packets present and we should expand the benchmarks to understand this use case.

Modifications:
- SslEngine benchmarks should include wrapping/unwrapping of multiple TLS packets
- Introduce SslHandler benchmarks which can also account for wrapping/unwrapping of multiple TLS packets

Result:
SslHandler and SslEngine benchmarks are more comprehensive.
This commit is contained in:
Scott Mitchell 2017-02-25 19:12:54 -08:00
parent 53fc693901
commit 743d2d374c
11 changed files with 797 additions and 298 deletions

View File

@ -0,0 +1,308 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project 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 io.netty.microbench.channel;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelProgressivePromise;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.concurrent.EventExecutor;
import java.net.SocketAddress;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
public abstract class EmbeddedChannelHandlerContext implements ChannelHandlerContext {
private static final String HANDLER_NAME = "microbench-delegator-ctx";
private final EventLoop eventLoop;
private final Channel channel;
private final ByteBufAllocator alloc;
private final ChannelHandler handler;
private SocketAddress localAddress;
protected EmbeddedChannelHandlerContext(ByteBufAllocator alloc, ChannelHandler handler, EmbeddedChannel channel) {
this.alloc = checkNotNull(alloc, "alloc");
this.channel = checkNotNull(channel, "channel");
this.handler = checkNotNull(handler, "handler");
eventLoop = checkNotNull(channel.eventLoop(), "eventLoop");
}
protected abstract void handleException(Throwable t);
@Override
public final <T> Attribute<T> attr(AttributeKey<T> key) {
return null;
}
@Override
public final <T> boolean hasAttr(AttributeKey<T> key) {
return false;
}
@Override
public final Channel channel() {
return channel;
}
@Override
public final EventExecutor executor() {
return eventLoop;
}
@Override
public final String name() {
return HANDLER_NAME;
}
@Override
public final ChannelHandler handler() {
return handler;
}
@Override
public final boolean isRemoved() {
return false;
}
@Override
public final ChannelHandlerContext fireChannelRegistered() {
return this;
}
@Override
public final ChannelHandlerContext fireChannelUnregistered() {
return this;
}
@Override
public final ChannelHandlerContext fireChannelActive() {
return this;
}
@Override
public final ChannelHandlerContext fireChannelInactive() {
return this;
}
@Override
public final ChannelHandlerContext fireExceptionCaught(Throwable cause) {
try {
handler().exceptionCaught(this, cause);
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public final ChannelHandlerContext fireUserEventTriggered(Object event) {
ReferenceCountUtil.release(event);
return this;
}
@Override
public final ChannelHandlerContext fireChannelRead(Object msg) {
ReferenceCountUtil.release(msg);
return this;
}
@Override
public final ChannelHandlerContext fireChannelReadComplete() {
return this;
}
@Override
public final ChannelHandlerContext fireChannelWritabilityChanged() {
return this;
}
@Override
public final ChannelFuture bind(SocketAddress localAddress) {
return bind(localAddress, newPromise());
}
@Override
public final ChannelFuture connect(SocketAddress remoteAddress) {
return connect(remoteAddress, newPromise());
}
@Override
public final ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return connect(remoteAddress, localAddress, newPromise());
}
@Override
public final ChannelFuture disconnect() {
return disconnect(newPromise());
}
@Override
public final ChannelFuture close() {
return close(newPromise());
}
@Override
public final ChannelFuture deregister() {
return deregister(newPromise());
}
@Override
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
try {
channel().bind(localAddress, promise);
this.localAddress = localAddress;
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
try {
channel().connect(remoteAddress, localAddress, promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) {
try {
channel().connect(remoteAddress, localAddress, promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelFuture disconnect(ChannelPromise promise) {
try {
channel().disconnect(promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelFuture close(ChannelPromise promise) {
try {
channel().close(promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelFuture deregister(ChannelPromise promise) {
try {
channel().deregister(promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelHandlerContext read() {
try {
channel().read();
} catch (Exception e) {
handleException(e);
}
return this;
}
@Override
public ChannelFuture write(Object msg) {
return channel().write(msg);
}
@Override
public ChannelFuture write(Object msg, ChannelPromise promise) {
return channel().write(msg, promise);
}
@Override
public final ChannelHandlerContext flush() {
channel().flush();
return this;
}
@Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
return channel().writeAndFlush(msg, promise);
}
@Override
public ChannelFuture writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise());
}
@Override
public final ChannelPipeline pipeline() {
return channel().pipeline();
}
@Override
public final ByteBufAllocator alloc() {
return alloc;
}
@Override
public final ChannelPromise newPromise() {
return channel().newPromise();
}
@Override
public final ChannelProgressivePromise newProgressivePromise() {
return channel().newProgressivePromise();
}
@Override
public final ChannelFuture newSucceededFuture() {
return channel().newSucceededFuture();
}
@Override
public final ChannelFuture newFailedFuture(Throwable cause) {
return channel().newFailedFuture(cause);
}
@Override
public final ChannelPromise voidPromise() {
return channel().voidPromise();
}
}

View File

@ -0,0 +1,103 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project 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 io.netty.microbench.channel;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelPromise;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.internal.ObjectUtil;
public abstract class EmbeddedChannelWriteAccumulatingHandlerContext extends EmbeddedChannelHandlerContext {
private ByteBuf cumulation;
private ByteToMessageDecoder.Cumulator cumulator;
protected EmbeddedChannelWriteAccumulatingHandlerContext(ByteBufAllocator alloc, ChannelHandler handler,
ByteToMessageDecoder.Cumulator writeCumulator) {
this(alloc, handler, writeCumulator, new EmbeddedChannel());
}
protected EmbeddedChannelWriteAccumulatingHandlerContext(ByteBufAllocator alloc, ChannelHandler handler,
ByteToMessageDecoder.Cumulator writeCumulator,
EmbeddedChannel channel) {
super(alloc, handler, channel);
this.cumulator = ObjectUtil.checkNotNull(writeCumulator, "writeCumulator");
}
public final ByteBuf cumulation() {
return cumulation;
}
public final void releaseCumulation() {
if (cumulation != null) {
cumulation.release();
cumulation = null;
}
}
@Override
public final ChannelFuture write(Object msg) {
return write(msg, newPromise());
}
@Override
public final ChannelFuture write(Object msg, ChannelPromise promise) {
try {
if (msg instanceof ByteBuf) {
if (cumulation == null) {
cumulation = (ByteBuf) msg;
} else {
cumulation = cumulator.cumulate(alloc(), cumulation, (ByteBuf) msg);
}
promise.setSuccess();
} else {
channel().write(msg, promise);
}
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
try {
if (msg instanceof ByteBuf) {
ByteBuf buf = (ByteBuf) msg;
if (cumulation == null) {
cumulation = buf;
} else {
cumulation = cumulator.cumulate(alloc(), cumulation, buf);
}
promise.setSuccess();
} else {
channel().writeAndFlush(msg, promise);
}
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public final ChannelFuture writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise());
}
}

View File

@ -14,246 +14,32 @@
*/
package io.netty.microbench.channel;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelProgressivePromise;
import io.netty.channel.ChannelPromise;
import io.netty.channel.EventLoop;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.Attribute;
import io.netty.util.AttributeKey;
import io.netty.util.ReferenceCounted;
import io.netty.util.concurrent.EventExecutor;
import java.net.SocketAddress;
public abstract class EmbeddedChannelWriteReleaseHandlerContext implements ChannelHandlerContext {
private static final String HANDLER_NAME = "microbench-delegator-ctx";
private final EventLoop eventLoop;
private final Channel channel;
private final ByteBufAllocator alloc;
private final ChannelHandler handler;
private SocketAddress localAddress;
public EmbeddedChannelWriteReleaseHandlerContext(ByteBufAllocator alloc, ChannelHandler handler) {
public abstract class EmbeddedChannelWriteReleaseHandlerContext extends EmbeddedChannelHandlerContext {
protected EmbeddedChannelWriteReleaseHandlerContext(ByteBufAllocator alloc, ChannelHandler handler) {
this(alloc, handler, new EmbeddedChannel());
}
public EmbeddedChannelWriteReleaseHandlerContext(ByteBufAllocator alloc, ChannelHandler handler,
protected EmbeddedChannelWriteReleaseHandlerContext(ByteBufAllocator alloc, ChannelHandler handler,
EmbeddedChannel channel) {
this.alloc = checkNotNull(alloc, "alloc");
this.channel = checkNotNull(channel, "channel");
this.handler = checkNotNull(handler, "handler");
eventLoop = checkNotNull(channel.eventLoop(), "eventLoop");
super(alloc, handler, channel);
}
protected abstract void handleException(Throwable t);
@Override
public <T> Attribute<T> attr(AttributeKey<T> key) {
return null;
}
@Override
public <T> boolean hasAttr(AttributeKey<T> key) {
return false;
}
@Override
public Channel channel() {
return channel;
}
@Override
public EventExecutor executor() {
return eventLoop;
}
@Override
public String name() {
return HANDLER_NAME;
}
@Override
public ChannelHandler handler() {
return handler;
}
@Override
public boolean isRemoved() {
return false;
}
@Override
public ChannelHandlerContext fireChannelRegistered() {
return this;
}
@Override
public ChannelHandlerContext fireChannelUnregistered() {
return this;
}
@Override
public ChannelHandlerContext fireChannelActive() {
return this;
}
@Override
public ChannelHandlerContext fireChannelInactive() {
return this;
}
@Override
public ChannelHandlerContext fireExceptionCaught(Throwable cause) {
try {
handler().exceptionCaught(this, cause);
} catch (Exception e) {
handleException(e);
}
return null;
}
@Override
public ChannelHandlerContext fireUserEventTriggered(Object event) {
return this;
}
@Override
public ChannelHandlerContext fireChannelRead(Object msg) {
return this;
}
@Override
public ChannelHandlerContext fireChannelReadComplete() {
return this;
}
@Override
public ChannelHandlerContext fireChannelWritabilityChanged() {
return this;
}
@Override
public ChannelFuture bind(SocketAddress localAddress) {
return bind(localAddress, newPromise());
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress) {
return connect(remoteAddress, newPromise());
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
return connect(remoteAddress, localAddress, newPromise());
}
@Override
public ChannelFuture disconnect() {
return disconnect(newPromise());
}
@Override
public ChannelFuture close() {
return close(newPromise());
}
@Override
public ChannelFuture deregister() {
return deregister(newPromise());
}
@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
try {
channel().bind(localAddress, promise);
this.localAddress = localAddress;
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
try {
channel().connect(remoteAddress, localAddress, promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) {
try {
channel().connect(remoteAddress, localAddress, promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public ChannelFuture disconnect(ChannelPromise promise) {
try {
channel().disconnect(promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public ChannelFuture close(ChannelPromise promise) {
try {
channel().close(promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public ChannelFuture deregister(ChannelPromise promise) {
try {
channel().deregister(promise);
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public ChannelHandlerContext read() {
try {
channel().read();
} catch (Exception e) {
handleException(e);
}
return this;
}
@Override
public ChannelFuture write(Object msg) {
public final ChannelFuture write(Object msg) {
return write(msg, newPromise());
}
@Override
public ChannelFuture write(Object msg, ChannelPromise promise) {
public final ChannelFuture write(Object msg, ChannelPromise promise) {
try {
if (msg instanceof ReferenceCounted) {
((ReferenceCounted) msg).release();
@ -269,53 +55,23 @@ public abstract class EmbeddedChannelWriteReleaseHandlerContext implements Chann
}
@Override
public ChannelHandlerContext flush() {
channel().flush();
return this;
public final ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
try {
if (msg instanceof ReferenceCounted) {
((ReferenceCounted) msg).release();
promise.setSuccess();
} else {
channel().writeAndFlush(msg, promise);
}
} catch (Exception e) {
promise.setFailure(e);
handleException(e);
}
return promise;
}
@Override
public ChannelFuture writeAndFlush(Object msg, ChannelPromise promise) {
return channel().writeAndFlush(msg, promise);
}
@Override
public ChannelFuture writeAndFlush(Object msg) {
public final ChannelFuture writeAndFlush(Object msg) {
return writeAndFlush(msg, newPromise());
}
@Override
public ChannelPipeline pipeline() {
return channel().pipeline();
}
@Override
public ByteBufAllocator alloc() {
return alloc;
}
@Override
public ChannelPromise newPromise() {
return channel().newPromise();
}
@Override
public ChannelProgressivePromise newProgressivePromise() {
return channel().newProgressivePromise();
}
@Override
public ChannelFuture newSucceededFuture() {
return channel().newSucceededFuture();
}
@Override
public ChannelFuture newFailedFuture(Throwable cause) {
return channel().newFailedFuture(cause);
}
@Override
public ChannelPromise voidPromise() {
return channel().voidPromise();
}
}

View File

@ -15,7 +15,7 @@
*/
package io.netty.microbench.handler.ssl;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
@ -25,11 +25,11 @@ import io.netty.util.ReferenceCountUtil;
import io.netty.util.internal.PlatformDependent;
import org.openjdk.jmh.annotations.Param;
import java.io.File;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import java.io.File;
import java.nio.ByteBuffer;
public class AbstractSslEngineBenchmark extends AbstractMicrobenchmark {
@ -82,12 +82,12 @@ public class AbstractSslEngineBenchmark extends AbstractMicrobenchmark {
}
}
SSLEngine newClientEngine(String cipher) {
return configureEngine(clientContext.newEngine(PooledByteBufAllocator.DEFAULT), cipher);
SSLEngine newClientEngine(ByteBufAllocator allocator, String cipher) {
return configureEngine(clientContext.newHandler(allocator).engine(), cipher);
}
SSLEngine newServerEngine(String cipher) {
return configureEngine(serverContext.newEngine(PooledByteBufAllocator.DEFAULT), cipher);
SSLEngine newServerEngine(ByteBufAllocator allocator, String cipher) {
return configureEngine(serverContext.newHandler(allocator).engine(), cipher);
}
abstract SslProvider sslProvider();
@ -142,9 +142,9 @@ public class AbstractSslEngineBenchmark extends AbstractMicrobenchmark {
private ByteBuffer clientAppReadBuffer;
private ByteBuffer empty;
protected final void initEngines() {
clientEngine = newClientEngine();
serverEngine = newServerEngine();
protected final void initEngines(ByteBufAllocator allocator) {
clientEngine = newClientEngine(allocator);
serverEngine = newServerEngine(allocator);
}
protected final void destroyEngines() {
@ -241,15 +241,15 @@ public class AbstractSslEngineBenchmark extends AbstractMicrobenchmark {
serverResult.getStatus() == SSLEngineResult.Status.OK;
}
protected final SSLEngine newClientEngine() {
return sslProvider.newClientEngine(cipher);
protected final SSLEngine newClientEngine(ByteBufAllocator allocator) {
return sslProvider.newClientEngine(allocator, cipher);
}
protected final SSLEngine newServerEngine() {
return sslProvider.newServerEngine(cipher);
protected final SSLEngine newServerEngine(ByteBufAllocator allocator) {
return sslProvider.newServerEngine(allocator, cipher);
}
protected static boolean checkSslEngineResult(SSLEngineResult result, ByteBuffer src, ByteBuffer dst) {
static boolean checkSslEngineResult(SSLEngineResult result, ByteBuffer src, ByteBuffer dst) {
return result.getStatus() == SSLEngineResult.Status.OK && !src.hasRemaining() && dst.hasRemaining();
}

View File

@ -15,15 +15,17 @@
*/
package io.netty.microbench.handler.ssl;
import io.netty.util.internal.ThreadLocalRandom;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.util.internal.PlatformDependent;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.TearDown;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import java.nio.ByteBuffer;
public abstract class AbstractSslEngineThroughputBenchmark extends AbstractSslEngineBenchmark {
@ -35,14 +37,15 @@ public abstract class AbstractSslEngineThroughputBenchmark extends AbstractSslEn
@Setup(Level.Iteration)
public final void setup() throws Exception {
initEngines();
ByteBufAllocator allocator = new PooledByteBufAllocator(true);
initEngines(allocator);
initHandshakeBuffers();
wrapDstBuffer = allocateBuffer(clientEngine.getSession().getPacketBufferSize());
wrapDstBuffer = allocateBuffer(clientEngine.getSession().getPacketBufferSize() << 2);
wrapSrcBuffer = allocateBuffer(messageSize);
byte[] bytes = new byte[messageSize];
ThreadLocalRandom.current().nextBytes(bytes);
PlatformDependent.threadLocalRandom().nextBytes(bytes);
wrapSrcBuffer.put(bytes);
wrapSrcBuffer.flip();
@ -66,14 +69,16 @@ public abstract class AbstractSslEngineThroughputBenchmark extends AbstractSslEn
protected void doTearDown() throws Exception { }
protected final ByteBuffer doWrap() throws SSLException {
wrapSrcBuffer.position(0).limit(messageSize);
protected final ByteBuffer doWrap(int numWraps) throws SSLException {
wrapDstBuffer.clear();
SSLEngineResult wrapResult = clientEngine.wrap(wrapSrcBuffer, wrapDstBuffer);
for (int i = 0; i < numWraps; ++i) {
wrapSrcBuffer.position(0).limit(messageSize);
assert checkSslEngineResult(wrapResult, wrapSrcBuffer, wrapDstBuffer);
SSLEngineResult wrapResult = clientEngine.wrap(wrapSrcBuffer, wrapDstBuffer);
assert checkSslEngineResult(wrapResult, wrapSrcBuffer, wrapDstBuffer);
}
return wrapDstBuffer;
}
}

View File

@ -0,0 +1,181 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project 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 io.netty.microbench.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.channel.ChannelHandler;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslHandler;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.microbench.channel.EmbeddedChannelWriteAccumulatingHandlerContext;
import io.netty.microbench.util.AbstractMicrobenchmark;
import io.netty.util.ReferenceCountUtil;
import org.openjdk.jmh.annotations.Param;
import java.io.File;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import static io.netty.handler.codec.ByteToMessageDecoder.COMPOSITE_CUMULATOR;
import static org.junit.Assert.assertNull;
public class AbstractSslHandlerBenchmark extends AbstractMicrobenchmark {
private static final String PROTOCOL_TLS_V1_2 = "TLSv1.2";
public enum SslEngineProvider {
JDK {
@Override
SslProvider sslProvider() {
return SslProvider.JDK;
}
},
OPENSSL {
@Override
SslProvider sslProvider() {
return SslProvider.OPENSSL;
}
},
OPENSSL_REFCNT {
@Override
SslProvider sslProvider() {
return SslProvider.OPENSSL_REFCNT;
}
};
private final SslContext clientContext = newClientContext();
private final SslContext serverContext = newServerContext();
private SslContext newClientContext() {
try {
return SslContextBuilder.forClient()
.sslProvider(sslProvider())
.trustManager(InsecureTrustManagerFactory.INSTANCE)
.build();
} catch (SSLException e) {
throw new IllegalStateException(e);
}
}
private SslContext newServerContext() {
try {
File keyFile = new File(getClass().getResource("test_unencrypted.pem").getFile());
File crtFile = new File(getClass().getResource("test.crt").getFile());
return SslContextBuilder.forServer(crtFile, keyFile)
.sslProvider(sslProvider())
.build();
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
SslHandler newClientHandler(ByteBufAllocator allocator, String cipher) {
SslHandler handler = clientContext.newHandler(allocator);
configureEngine(handler.engine(), cipher);
return handler;
}
SslHandler newServerHandler(ByteBufAllocator allocator, String cipher) {
SslHandler handler = serverContext.newHandler(allocator);
configureEngine(handler.engine(), cipher);
return handler;
}
abstract SslProvider sslProvider();
static SSLEngine configureEngine(SSLEngine engine, String cipher) {
engine.setEnabledProtocols(new String[]{ PROTOCOL_TLS_V1_2 });
engine.setEnabledCipherSuites(new String[]{ cipher });
return engine;
}
}
@Param
public SslEngineProvider sslProvider;
// Includes cipher required by HTTP/2
@Param({ "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256", "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" })
public String cipher;
protected SslHandler clientSslHandler;
protected SslHandler serverSslHandler;
protected EmbeddedChannelWriteAccumulatingHandlerContext clientCtx;
protected EmbeddedChannelWriteAccumulatingHandlerContext serverCtx;
protected final void initSslHandlers(ByteBufAllocator allocator) {
clientSslHandler = newClientHandler(allocator);
serverSslHandler = newServerHandler(allocator);
clientCtx = new SslThroughputBenchmarkHandlerContext(allocator, clientSslHandler, COMPOSITE_CUMULATOR);
serverCtx = new SslThroughputBenchmarkHandlerContext(allocator, serverSslHandler, COMPOSITE_CUMULATOR);
}
protected final void destroySslHandlers() {
try {
if (clientSslHandler != null) {
ReferenceCountUtil.release(clientSslHandler.engine());
}
} finally {
if (serverSslHandler != null) {
ReferenceCountUtil.release(serverSslHandler.engine());
}
}
}
protected final void doHandshake() throws Exception {
serverSslHandler.handlerAdded(serverCtx);
clientSslHandler.handlerAdded(clientCtx);
do {
ByteBuf clientCumulation = clientCtx.cumulation();
if (clientCumulation != null) {
serverSslHandler.channelRead(serverCtx, clientCumulation.retain());
clientCtx.releaseCumulation();
}
ByteBuf serverCumulation = serverCtx.cumulation();
if (serverCumulation != null) {
clientSslHandler.channelRead(clientCtx, serverCumulation.retain());
serverCtx.releaseCumulation();
}
} while (!clientSslHandler.handshakeFuture().isDone() || !serverSslHandler.handshakeFuture().isDone());
}
protected final SslHandler newClientHandler(ByteBufAllocator allocator) {
return sslProvider.newClientHandler(allocator, cipher);
}
protected final SslHandler newServerHandler(ByteBufAllocator allocator) {
return sslProvider.newServerHandler(allocator, cipher);
}
private static final class SslThroughputBenchmarkHandlerContext extends
EmbeddedChannelWriteAccumulatingHandlerContext {
SslThroughputBenchmarkHandlerContext(ByteBufAllocator alloc, ChannelHandler handler,
ByteToMessageDecoder.Cumulator writeCumulator) {
super(alloc, handler, writeCumulator);
}
@Override
protected void handleException(Throwable t) {
handleUnexpectedException(t);
}
}
public static void handleUnexpectedException(Throwable t) {
assertNull(t);
}
}

View File

@ -0,0 +1,94 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project 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 io.netty.microbench.handler.ssl;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.internal.PlatformDependent;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.TearDown;
public abstract class AbstractSslHandlerThroughputBenchmark extends AbstractSslHandlerBenchmark {
@Param({ "64", "128", "512", "1024", "4096" })
public int messageSize;
@Param
public BufferType bufferType;
public enum BufferType {
HEAP {
@Override
ByteBuf newBuffer(ByteBufAllocator allocator, int size) {
return allocator.heapBuffer(size);
}
},
DIRECT {
@Override
ByteBuf newBuffer(ByteBufAllocator allocator, int size) {
return allocator.directBuffer(size);
}
};
abstract ByteBuf newBuffer(ByteBufAllocator allocator, int size);
}
protected ByteBuf wrapSrcBuffer;
protected EmbeddedChannel channel;
private ByteBufAllocator allocator;
@Setup(Level.Iteration)
public final void setup() throws Exception {
allocator = new PooledByteBufAllocator(true);
initSslHandlers(allocator);
wrapSrcBuffer = allocateBuffer(messageSize);
byte[] bytes = new byte[messageSize];
PlatformDependent.threadLocalRandom().nextBytes(bytes);
wrapSrcBuffer.writeBytes(bytes);
// Complete the initial TLS handshake.
doHandshake();
}
@TearDown(Level.Iteration)
public final void tearDown() throws Exception {
destroySslHandlers();
wrapSrcBuffer.release();
clientCtx.releaseCumulation();
serverCtx.releaseCumulation();
}
protected final ByteBuf allocateBuffer(int size) {
return bufferType.newBuffer(allocator, size);
}
protected final ByteBuf doWrite(int numWrites) throws Exception {
clientCtx.releaseCumulation();
for (int i = 0; i < numWrites; ++i) {
ByteBuf wrapSrcBuffer = this.wrapSrcBuffer.retainedSlice();
clientSslHandler.write(clientCtx, wrapSrcBuffer, clientCtx.voidPromise());
}
clientSslHandler.flush(clientCtx);
return clientCtx.cumulation().retainedSlice();
}
}

View File

@ -16,23 +16,26 @@
package io.netty.microbench.handler.ssl;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLEngineResult;
import javax.net.ssl.SSLException;
import java.nio.ByteBuffer;
@State(Scope.Benchmark)
@Threads(1)
public class SslEngineEchoBenchmark extends AbstractSslEngineThroughputBenchmark {
@Param({ "1", "2", "5", "10" })
public int numWraps;
private ByteBuffer unwrapDstBuffer;
@Override
protected void doSetup() {
unwrapDstBuffer = allocateBuffer(serverEngine.getSession().getApplicationBufferSize());
unwrapDstBuffer = allocateBuffer(serverEngine.getSession().getApplicationBufferSize() << 2);
}
@Override
@ -42,12 +45,15 @@ public class SslEngineEchoBenchmark extends AbstractSslEngineThroughputBenchmark
@Benchmark
public ByteBuffer wrapUnwrap() throws SSLException {
ByteBuffer src = doWrap();
ByteBuffer src = doWrap(numWraps);
src.flip();
unwrapDstBuffer.clear();
SSLEngineResult unwrapResult = serverEngine.unwrap(src, unwrapDstBuffer);
SSLEngineResult unwrapResult;
do {
unwrapResult = serverEngine.unwrap(src, unwrapDstBuffer);
} while (unwrapResult.getStatus() == SSLEngineResult.Status.OK && src.hasRemaining());
assert checkSslEngineResult(unwrapResult, src, unwrapDstBuffer);
return unwrapDstBuffer;

View File

@ -15,6 +15,8 @@
*/
package io.netty.microbench.handler.ssl;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.PooledByteBufAllocator;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
@ -32,10 +34,13 @@ import java.util.concurrent.TimeUnit;
@Threads(1)
public class SslEngineHandshakeBenchmark extends AbstractSslEngineBenchmark {
private ByteBufAllocator allocator;
@Setup(Level.Iteration)
public void setup() {
allocator = new PooledByteBufAllocator(true);
// Init an engine one time and create the buffers needed for an handshake so we can use them in the benchmark
initEngines();
initEngines(allocator);
initHandshakeBuffers();
destroyEngines();
}
@ -49,7 +54,7 @@ public class SslEngineHandshakeBenchmark extends AbstractSslEngineBenchmark {
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public boolean handshake() throws Exception {
initEngines();
initEngines(allocator);
boolean ok = doHandshake();
destroyEngines();
assert ok;

View File

@ -15,21 +15,24 @@
*/
package io.netty.microbench.handler.ssl;
import javax.net.ssl.SSLException;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Threads;
import java.nio.ByteBuffer;
import javax.net.ssl.SSLException;
@State(Scope.Benchmark)
@Threads(1)
public class SslEngineWrapBenchmark extends AbstractSslEngineThroughputBenchmark {
@Param({ "1", "2", "5", "10" })
public int numWraps;
@Benchmark
public ByteBuffer wrap() throws SSLException {
return doWrap();
return doWrap(numWraps);
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2017 The Netty Project
*
* The Netty Project 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 io.netty.microbench.handler.ssl;
import io.netty.buffer.ByteBuf;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Param;
public class SslHandlerEchoBenchmark extends AbstractSslHandlerThroughputBenchmark {
@Param({ "1", "2", "5", "10" })
public int numWrites;
@Benchmark
public ByteBuf wrapUnwrap() throws Exception {
ByteBuf src = doWrite(numWrites);
do {
serverSslHandler.channelRead(serverCtx, src);
} while (src.isReadable());
assert !src.isReadable() && src.refCnt() == 1 : "src: " + src + " src.refCnt(): " + src.refCnt();
return src;
}
}