Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use a separate mock server for benchmarks #450

Merged
merged 1 commit into from
May 3, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,43 +1,29 @@
package com.relayrides.pushy.apns;

import com.relayrides.pushy.apns.util.ApnsPayloadBuilder;
import com.relayrides.pushy.apns.util.SimpleApnsPushNotification;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import org.apache.commons.lang3.RandomStringUtils;
import org.openjdk.jmh.annotations.*;

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import java.security.interfaces.ECPrivateKey;
import java.security.interfaces.ECPublicKey;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

import org.apache.commons.lang3.RandomStringUtils;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.TearDown;
import org.openjdk.jmh.annotations.Threads;
import org.openjdk.jmh.annotations.Warmup;

import com.relayrides.pushy.apns.util.ApnsPayloadBuilder;
import com.relayrides.pushy.apns.util.SimpleApnsPushNotification;

import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;

@State(Scope.Thread)
public class ApnsClientBenchmark {

private EventLoopGroup eventLoopGroup;
private NioEventLoopGroup eventLoopGroup;

private ApnsClient client;
private MockApnsServer server;
private BenchmarkApnsServer server;

private List<SimpleApnsPushNotification> pushNotifications;

Expand Down Expand Up @@ -65,12 +51,10 @@ public void setUp() throws Exception {
.setTrustedServerCertificateChain(ApnsClientBenchmark.class.getResourceAsStream(CA_CERTIFICATE_FILENAME))
.setEventLoopGroup(this.eventLoopGroup);

final MockApnsServerBuilder serverBuilder = new MockApnsServerBuilder()
.setServerCredentials(ApnsClientBenchmark.class.getResourceAsStream(SERVER_CERTIFICATES_FILENAME), ApnsClientBenchmark.class.getResourceAsStream(SERVER_KEY_FILENAME), null)
.setEventLoopGroup(this.eventLoopGroup);

this.client = clientBuilder.build();
this.server = serverBuilder.build();
this.server = new BenchmarkApnsServer(ApnsClientBenchmark.class.getResourceAsStream(SERVER_CERTIFICATES_FILENAME),
ApnsClientBenchmark.class.getResourceAsStream(SERVER_KEY_FILENAME),
this.eventLoopGroup);

final KeyPair keyPair;
{
Expand All @@ -83,10 +67,8 @@ public void setUp() throws Exception {
}

this.client.registerSigningKey((ECPrivateKey) keyPair.getPrivate(), TEAM_ID, KEY_ID, TOPIC);
this.server.registerPublicKey((ECPublicKey) keyPair.getPublic(), TEAM_ID, KEY_ID, TOPIC);

final String token = generateRandomToken();
this.server.registerDeviceTokenForTopic(TOPIC, token, null);

this.pushNotifications = new ArrayList<>(this.notificationCount);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package com.relayrides.pushy.apns;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http2.Http2SecurityUtil;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.ssl.*;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GlobalEventExecutor;
import io.netty.util.concurrent.SucceededFuture;

import javax.net.ssl.SSLException;
import java.io.InputStream;

/**
* A simple benchmark server that sends a "looks good!" response to any and all requests.
*
* @author <a href="https://github.com/jchambers">Jon Chambers</a>
*/
class BenchmarkApnsServer {

private final ServerBootstrap bootstrap;
private ChannelGroup allChannels;

private static final int MAX_CONCURRENT_STREAMS = 500;

public BenchmarkApnsServer(final InputStream certificateChainInputStream, final InputStream privateKeyPkcs8InputStream, final NioEventLoopGroup eventLoopGroup) throws SSLException {
final SslContext sslContext;
{
final SslProvider sslProvider;

if (OpenSsl.isAvailable()) {
if (OpenSsl.isAlpnSupported()) {
sslProvider = SslProvider.OPENSSL;
} else {
sslProvider = SslProvider.JDK;
}
} else {
sslProvider = SslProvider.JDK;
}

sslContext = SslContextBuilder.forServer(certificateChainInputStream, privateKeyPkcs8InputStream, null)
.sslProvider(sslProvider)
.ciphers(Http2SecurityUtil.CIPHERS, SupportedCipherSuiteFilter.INSTANCE)
.clientAuth(ClientAuth.OPTIONAL)
.applicationProtocolConfig(new ApplicationProtocolConfig(
ApplicationProtocolConfig.Protocol.ALPN,
ApplicationProtocolConfig.SelectorFailureBehavior.NO_ADVERTISE,
ApplicationProtocolConfig.SelectedListenerFailureBehavior.ACCEPT,
ApplicationProtocolNames.HTTP_2))
.build();
}

this.bootstrap = new ServerBootstrap();
this.bootstrap.group(eventLoopGroup);

this.bootstrap.channel(NioServerSocketChannel.class);
this.bootstrap.childHandler(new ChannelInitializer<SocketChannel>() {

@Override
protected void initChannel(final SocketChannel channel) throws Exception {
final SslHandler sslHandler = sslContext.newHandler(channel.alloc());
channel.pipeline().addLast(sslHandler);
channel.pipeline().addLast(new ApplicationProtocolNegotiationHandler(ApplicationProtocolNames.HTTP_1_1) {

@Override
protected void configurePipeline(final ChannelHandlerContext context, final String protocol) throws Exception {
if (ApplicationProtocolNames.HTTP_2.equals(protocol)) {
context.pipeline().addLast(new BenchmarkApnsServerHandler.BenchmarkApnsServerHandlerBuilder()
.initialSettings(new Http2Settings().maxConcurrentStreams(MAX_CONCURRENT_STREAMS))
.build());

BenchmarkApnsServer.this.allChannels.add(context.channel());
} else {
throw new IllegalStateException("Unexpected protocol: " + protocol);
}
}
});
}
});
}

public Future<Void> start(final int port) {
final ChannelFuture channelFuture = this.bootstrap.bind(port);

this.allChannels = new DefaultChannelGroup(channelFuture.channel().eventLoop(), true);
this.allChannels.add(channelFuture.channel());

return channelFuture;
}

public Future<Void> shutdown() {
return (this.allChannels != null) ? this.allChannels.close() : new SucceededFuture<Void>(GlobalEventExecutor.INSTANCE, null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package com.relayrides.pushy.apns;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http2.*;

class BenchmarkApnsServerHandler extends Http2ConnectionHandler implements Http2FrameListener {

private static final Http2Headers SUCCESS_HEADERS = new DefaultHttp2Headers()
.status(HttpResponseStatus.OK.codeAsText());

public static final class BenchmarkApnsServerHandlerBuilder extends AbstractHttp2ConnectionHandlerBuilder<BenchmarkApnsServerHandler, BenchmarkApnsServerHandlerBuilder> {
@Override
public BenchmarkApnsServerHandlerBuilder initialSettings(final Http2Settings initialSettings) {
return super.initialSettings(initialSettings);
}

@Override
public BenchmarkApnsServerHandler build(final Http2ConnectionDecoder decoder, final Http2ConnectionEncoder encoder, final Http2Settings initialSettings) {
final BenchmarkApnsServerHandler handler = new BenchmarkApnsServerHandler(decoder, encoder, initialSettings);
this.frameListener(handler);
return handler;
}

@Override
public BenchmarkApnsServerHandler build() {
return super.build();
}
}

protected BenchmarkApnsServerHandler(final Http2ConnectionDecoder decoder, final Http2ConnectionEncoder encoder, final Http2Settings initialSettings) {
super(decoder, encoder, initialSettings);
}

@Override
public int onDataRead(final ChannelHandlerContext context, final int streamId, final ByteBuf data, final int padding, final boolean endOfStream) throws Http2Exception {
if (endOfStream) {
this.encoder().writeHeaders(context, streamId, SUCCESS_HEADERS, 0, true, context.channel().newPromise());
}

return data.readableBytes() + padding;
}

@Override
public void onHeadersRead(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers, final int padding, final boolean endOfStream) throws Http2Exception {
}

@Override
public void onHeadersRead(final ChannelHandlerContext ctx, final int streamId, final Http2Headers headers, final int streamDependency, final short weight, final boolean exclusive, final int padding, final boolean endOfStream) throws Http2Exception {
}

@Override
public void onPriorityRead(final ChannelHandlerContext ctx, final int streamId, final int streamDependency, final short weight, final boolean exclusive) throws Http2Exception {
}

@Override
public void onRstStreamRead(final ChannelHandlerContext ctx, final int streamId, final long errorCode) throws Http2Exception {
}

@Override
public void onSettingsAckRead(final ChannelHandlerContext ctx) throws Http2Exception {
}

@Override
public void onSettingsRead(final ChannelHandlerContext ctx, final Http2Settings settings) throws Http2Exception {
}

@Override
public void onPingRead(final ChannelHandlerContext ctx, final ByteBuf data) throws Http2Exception {
}

@Override
public void onPingAckRead(final ChannelHandlerContext ctx, final ByteBuf data) throws Http2Exception {
}

@Override
public void onPushPromiseRead(final ChannelHandlerContext ctx, final int streamId, final int promisedStreamId, final Http2Headers headers, final int padding) throws Http2Exception {
}

@Override
public void onGoAwayRead(final ChannelHandlerContext ctx, final int lastStreamId, final long errorCode, final ByteBuf debugData) throws Http2Exception {
}

@Override
public void onWindowUpdateRead(final ChannelHandlerContext ctx, final int streamId, final int windowSizeIncrement) throws Http2Exception {
}

@Override
public void onUnknownFrame(final ChannelHandlerContext ctx, final byte frameType, final int streamId, final Http2Flags flags, final ByteBuf payload) throws Http2Exception {
}
}