Skip to content

Commit

Permalink
Merge pull request #450 from relayrides/benchmark_server
Browse files Browse the repository at this point in the history
Use a separate mock server for benchmarks
  • Loading branch information
jchambers committed May 3, 2017
2 parents 35586ba + 78ac95a commit 2e2ecc0
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 31 deletions.
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 {
}
}

0 comments on commit 2e2ecc0

Please sign in to comment.