com.relayrides.pushy.apns.BenchmarkApp.java Source code

Java tutorial

Introduction

Here is the source code for com.relayrides.pushy.apns.BenchmarkApp.java

Source

/* Copyright (c) 2014 RelayRides
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.relayrides.pushy.apns;

import io.netty.channel.nio.NioEventLoopGroup;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

import com.relayrides.pushy.apns.ApnsEnvironment;
import com.relayrides.pushy.apns.PushManager;
import com.relayrides.pushy.apns.PushManagerFactory;
import com.relayrides.pushy.apns.RejectedNotificationListener;
import com.relayrides.pushy.apns.util.ApnsPayloadBuilder;
import com.relayrides.pushy.apns.util.SimpleApnsPushNotification;

public class BenchmarkApp {

    private static final int NOTIFICATIONS_PER_TEST = 100000;

    private static final int GATEWAY_PORT = 2195;
    private static final int FEEDPACK_PORT = 2196;

    private final PushManagerFactory<SimpleApnsPushNotification> pushManagerFactory;

    private NioEventLoopGroup serverEventLoopGroup;
    private MockApnsServer server;

    private final ArrayList<SimpleApnsPushNotification> notifications = new ArrayList<SimpleApnsPushNotification>(
            NOTIFICATIONS_PER_TEST);

    private class BenchmarkErrorListener implements RejectedNotificationListener<SimpleApnsPushNotification>,
            FailedConnectionListener<SimpleApnsPushNotification> {

        public void handleFailedConnection(final PushManager<? extends SimpleApnsPushNotification> pushManager,
                final Throwable cause) {
            System.err.println("Connection failed.");
            cause.printStackTrace(System.err);
        }

        public void handleRejectedNotification(final PushManager<? extends SimpleApnsPushNotification> pushManager,
                final SimpleApnsPushNotification notification, final RejectedNotificationReason rejectionReason) {
            System.err.format("%s rejected: %s\n", notification, rejectionReason);
        }
    }

    public BenchmarkApp() throws Exception {
        this.pushManagerFactory = new PushManagerFactory<SimpleApnsPushNotification>(
                new ApnsEnvironment("127.0.0.1", GATEWAY_PORT, "localhost", FEEDPACK_PORT),
                SSLTestUtil.createSSLContextForTestClient());

        final ApnsPayloadBuilder builder = new ApnsPayloadBuilder();
        final Random random = new Random();

        for (int i = 0; i < NOTIFICATIONS_PER_TEST; i++) {
            final byte[] token = new byte[32];
            random.nextBytes(token);

            builder.setAlertBody(new BigInteger(1024, new Random()).toString(16));

            this.notifications.add(new SimpleApnsPushNotification(token, builder.buildWithDefaultMaximumLength()));
        }
    }

    public void runAllBenchmarks() throws InterruptedException {
        this.serverEventLoopGroup = new NioEventLoopGroup(2);
        this.server = new MockApnsServer(GATEWAY_PORT, serverEventLoopGroup);

        // We want to do a dummy run first to let the JVM warm up
        final NioEventLoopGroup warmupGroup = new NioEventLoopGroup(1);
        this.runBenchmark(warmupGroup, 1, this.notifications);
        warmupGroup.shutdownGracefully().await();

        System.out.println("threads, connections, throughput [k/sec]");

        for (int eventLoopGroupThreadCount : new int[] { 1, 2, 4, 8, 16 }) {
            final NioEventLoopGroup eventLoopGroup = new NioEventLoopGroup(eventLoopGroupThreadCount);

            for (int concurrentConnectionCount : new int[] { 1, 2, 4, 8, 16, 32 }) {
                if (concurrentConnectionCount >= eventLoopGroupThreadCount) {
                    System.gc();

                    double throughput = this.runBenchmark(eventLoopGroup, concurrentConnectionCount,
                            this.notifications);

                    System.out.format("%d, %d, %.1f\n", eventLoopGroupThreadCount, concurrentConnectionCount,
                            throughput / 1000.0);
                }
            }

            eventLoopGroup.shutdownGracefully().await();
        }

        this.serverEventLoopGroup.shutdownGracefully().await();
    }

    private double runBenchmark(final NioEventLoopGroup eventLoopGroup, final int concurrentConnectionCount,
            final List<SimpleApnsPushNotification> notifications) throws InterruptedException {
        this.pushManagerFactory.setEventLoopGroup(eventLoopGroup);
        this.pushManagerFactory.setConcurrentConnectionCount(concurrentConnectionCount);

        final PushManager<SimpleApnsPushNotification> pushManager = this.pushManagerFactory.buildPushManager();

        final BenchmarkErrorListener errorListener = new BenchmarkErrorListener();

        pushManager.registerFailedConnectionListener(errorListener);
        pushManager.registerRejectedNotificationListener(errorListener);
        pushManager.getQueue().addAll(notifications);

        final CountDownLatch firstMessageLatch = this.server.getAcceptedNotificationCountDownLatch(1);
        final CountDownLatch lastMessageLatch = this.server
                .getAcceptedNotificationCountDownLatch(notifications.size());

        this.server.start();
        pushManager.start();

        if (!firstMessageLatch.await(10, TimeUnit.SECONDS)) {
            System.err.println("Timed out waiting for first message.");
        }

        long start = System.currentTimeMillis();

        if (!lastMessageLatch.await(10, TimeUnit.SECONDS)) {
            System.err
                    .println("Timed out waiting for last message. Remaining count: " + lastMessageLatch.getCount());
        }

        long end = System.currentTimeMillis();

        pushManager.shutdown();
        this.server.shutdown();

        return 1000.0 * ((double) notifications.size() / (double) (end - start));
    }

    public static void main(String[] args) throws Exception {
        final BenchmarkApp benchmarkApp = new BenchmarkApp();
        benchmarkApp.runAllBenchmarks();
    }
}