Home

Awesome

Build Status

ChronoServer

A test server for sampling how long it takes mobile & web clients to make various types of requests to a server doing common request patterns. The idea is you can set this server up in various locations (think DCs) around the globe or country and test what the latency and experience for users will be as you vary the size and style of requests. You can also use the server to see how different CDNs might perform when sitting in front of your server and caching content.

The basic flow is as follows:

Getting Started

####Clone the repo & build

./gradlew clean shadowJar

####Setup the Cassandra DB

Options:

Execute the scheme file in db/scheme.cql on your setup.

####Configuration

The server takes the following config

{
    "port" : <port>,
    "default_test_base_url" : "<default http://localhost>",
    "data_retention_seconds" : <default 86400>,
    "cassandra": {
        "seeds": ["localhost"],
        "reconnect": {
            "name": "exponential",
            "base_delay": 1000,
            "max_delay": 10000
        }
    }
}

For example:

{
    "port" : 7345,
    "default_test_base_url" : "http://localhost",
    "data_retention_seconds" : 86400,
    "cassandra": {
        "seeds": ["localhost"],
        "reconnect": {
            "name": "exponential",
            "base_delay": 1000,
            "max_delay": 10000
        }
    }
}

Field breakdown:

####Running Server

#start the server
java -jar build/libs/ChronoServer-0.5.0-fat.jar -conf conf.json -instances 2

REST APIs

/api/v1/test_endpoints - GET

Gets a set of URLs for the client to execute.

Parameters:

Response:

See ConfigResponse.java

####/api/v1/report - POST

Allows a user to upload detailed information on the timings for all the calls it made.

Parameters:

See MetricReport.java

####/api/v1/timing - GET Sub APIs

Required Common Parameters:

Response:

{
  "data" : "<a bunch of random data matching your parameters>"
}

####/api/v1/timing - POST Sub APIs

Parameters:

See StorageRequest.java

####/healthcheck - GET

Standard health check for things like a LB to hit.

####/api/v1/timing_streaming - SockJS interface

See: streaming namespace and sample client below.

Examples

Piping curl output to something like jsonpp makes it much more readable.

###curl requests

#start the server
java -jar build/libs/ChronoServer-0.5.0-fat.jar -conf conf.json -instances 2

#grab a sample config package, take note of the api_key
curl -v "http://localhost:7345/api/v1/test_endpoints" | jsonpp

#grab static data with no caching
curl -v --header "X-API-Key: [api_key here]" "http://localhost:7345/api/v1/timing/static?unit=kb&size=1" | jsonpp

#grab static data with caching
curl -v --header "X-API-Key: [api_key here]" "http://localhost:7345/api/v1/timing/static_cached?unit=kb&size=1" | jsonpp

#grab data from the DB with no caching
curl -v --header "X-API-Key: [api_key here]" "http://localhost:7345/api/v1/timing/dynamic?unit=kb&size=1" | jsonpp

#grab data from the DB with caching
curl -v --header "X-API-Key: [api_key here]" "http://localhost:7345/api/v1/timing/dynamic_cached?unit=kb&size=1" | jsonpp

For testing POST calls via the store api Postman is great for this.

###sample SockJS client

<html>
    <script src="http:////cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
    <script src="vertxbus-3.0.0.js"></script>

    <!-- You're going to want to turn off security in your browser when running this probably -->
    <script>
        // this is acquired from the server
        var apiKey = ""
        var sock = new SockJS('http://localhost:7345/api/v1/timing_streaming');

        // download via socket
        var getData = function(address, unit, size) {
            sock.send(JSON.stringify({address: address, "api_key": apiKey,
                        body : {unit : unit, size: size}}));
        };

        // helper func for generating fake data
        var getKb = function(number) {
            var data = ['a', 'b', 'c', 'd', 'e', '1', '2', '3', '4', '5'];
            var str = "";

            var totalKb = 1024 * number;
            for(var i = 0; i < totalKb; i++) {
                str += data[Math.floor((Math.random() * 10))];
            }
            return str;
        };

        // upload via socket
        var putData = function(size) {
            sock.send(JSON.stringify({address: "streaming.store", "api_key": apiKey,
                        body : {test_batch: "some_batch", unit : "kb", size: size, data: getKb(size)}}));
        };

        // get the config, ie api key
        var getApiKey = function() {
            sock.send(JSON.stringify({address: "streaming.config", "api_key": "", body : {}}));
        }

        sock.onopen = function() {
            console.log('connected to server');
            getApiKey();
        };

        sock.onmessage = function(e) {
            console.log('message', e.data);

            var data = JSON.parse(e.data);

            // once we have the api key make some test calls
            if(data.type == "streaming.config") {
                apiKey = data.api_key;
                getData('streaming.static', "kb", 1);
                getData('streaming.dynamic', "kb", 5);
                putData(5);
            }
        };

        sock.onclose = function() {
            console.log('lost connection to server');
        };
    </script>
    <body>
    </body>
</html>