-
Notifications
You must be signed in to change notification settings - Fork 226
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
240 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
Performance testing for pushd | ||
============================= | ||
|
||
Simple performance testing setup using HTTP push service: | ||
|
||
1. Configuration: | ||
- Enable http push server in settings.coffee | ||
- Configure the number and frequency of generated messages by editing settings() method pushgenerator.py | ||
1. Start the pushd server: `node server.js` | ||
1. Start test performance listener: `node statsserver.js` | ||
1. Start traffic generator: `python pushgenerator.py` | ||
|
||
Traffic generator | ||
----------------- | ||
|
||
*pushgenerator.py* sends push notifications at a configurable rate. | ||
|
||
The frequency of notifications and the number of subscribers can be | ||
configured by editing the function settings() in pushgenerator.py. | ||
|
||
pushd process is expected be running on localhost on port 5000 | ||
(configurable by PUSHD_SERVER variable in pushgenerator.py). | ||
|
||
By default, pushgenerator.py creates random HTTP POST subscribers as | ||
the receivers of the notifications. The HTTP push service must be | ||
enabled in pushd's settings.coffee. | ||
|
||
Statistics collector | ||
-------------------- | ||
|
||
*statsserver.js* is a node.js server that can receive HTTP POST | ||
notifications generated by pushgenerator.py and display some | ||
statistics of the received notifications. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
#!/usr/bin/python | ||
|
||
import time | ||
import json | ||
import urllib | ||
import urllib2 | ||
import base64 | ||
import random | ||
from multiprocessing import Process | ||
|
||
PUSHD_SERVER = 'http://admin:admin@localhost:5000' | ||
PUSHD_SERVER_WITHOUT_AUTH = 'http://localhost:5000' | ||
PUSHD_AUTHORIZATION = 'Basic %s' % base64.encodestring('admin:admin') | ||
TOKEN_HTTP = 'http://localhost:5001/log' | ||
|
||
class RepeatingMessage: | ||
def __init__(self, event, messagesPerMinute): | ||
self.event = event | ||
self.messagesPerMinute = messagesPerMinute | ||
self.pushCount = 0 | ||
|
||
def push(self): | ||
print 'Pushing message to ' + self.event | ||
self.pushCount += 1 | ||
msg = self.generate_message() | ||
urllib.urlopen(PUSHD_SERVER + '/event/' + self.event, msg).read() | ||
|
||
def generate_message(self): | ||
return 'title=performance test&msg=%s' % self.generate_body() | ||
|
||
def generate_body(self): | ||
t = time.time() | ||
readable = time.strftime('%Y-%m-%d %I:%M:%S', time.localtime(t)) | ||
message = {'timestamp': t, | ||
'readable_timestamp': readable, | ||
'event': self.event} | ||
return json.dumps(message) | ||
|
||
|
||
class Subscriber: | ||
def __init__(self, token, proto): | ||
self.token = token | ||
self.proto = proto | ||
self.subscriberId = None | ||
self.registerSubscriber() | ||
|
||
def registerSubscriber(self): | ||
print 'Registering subscriber %s' % self.token | ||
data = 'proto=%s&token=%s&lang=fi&badge=0' % (self.proto, self.token) | ||
response = urllib.urlopen(PUSHD_SERVER + '/subscribers', data).read() | ||
parsedResponse = json.loads(response) | ||
if 'id' not in parsedResponse: | ||
raise RuntimeError('No id in the reponse') | ||
self.subscriberId = parsedResponse['id'] | ||
|
||
def subscribe(self, event): | ||
print 'User (token %s) subscribing to %s' % (self.token, event) | ||
url = PUSHD_SERVER + '/subscriber/%s/subscriptions/%s' % \ | ||
(self.subscriberId, event) | ||
data = 'ignore_message=0' | ||
urllib.urlopen(url, data).read() | ||
|
||
def unregister(self): | ||
print 'Unregistering user %s' % self.token | ||
url = PUSHD_SERVER_WITHOUT_AUTH + '/subscriber/%s' % self.subscriberId | ||
request = urllib2.Request(url, data='') | ||
request.add_header('Authorization', PUSHD_AUTHORIZATION) | ||
request.get_method = lambda: 'DELETE' | ||
opener = urllib2.build_opener(urllib2.HTTPHandler) | ||
opener.open(request).read() | ||
|
||
|
||
def pusherProcessMain(repeatingMessage): | ||
try: | ||
while True: | ||
repeatingMessage.push() | ||
time.sleep(60./repeatingMessage.messagesPerMinute) | ||
except KeyboardInterrupt: | ||
pass | ||
|
||
print '%d messages pushed to %s' % \ | ||
(repeatingMessage.pushCount, repeatingMessage.event) | ||
|
||
def generateRandomHTTPSubscribers(event, count): | ||
subscribers = [] | ||
print 'Creating %d subscribers for %s' % (count, event) | ||
for i in xrange(count): | ||
subscriber = Subscriber(randomHTTPToken(), 'http') | ||
subscriber.subscribe(event) | ||
subscribers.append(subscriber) | ||
return subscribers | ||
|
||
def randomHTTPToken(): | ||
r = ''.join([random.choice('0123456789ABCDEF') for x in xrange(10)]) | ||
return TOKEN_HTTP + '/' + r | ||
|
||
def startPushProcesses(targets): | ||
print 'Starting %d push processes' % len(targets) | ||
processes = [] | ||
for message in targets: | ||
p = Process(target=pusherProcessMain, args=(message,)) | ||
p.daemon = True | ||
p.start() | ||
processes.append(p) | ||
print 'All processes started' | ||
return processes | ||
|
||
def settings(): | ||
# events and notification frequencies | ||
push_targets = [RepeatingMessage('performancetest1', 2), | ||
RepeatingMessage('performancetest2', 10)] | ||
subscribers = [generateRandomHTTPSubscribers(push_targets[0].event, 10), | ||
generateRandomHTTPSubscribers(push_targets[1].event, 5)] | ||
return push_targets, subscribers | ||
|
||
def main(): | ||
push_targets, subscribers = settings() | ||
|
||
processes = startPushProcesses(push_targets) | ||
|
||
try: | ||
while True: | ||
time.sleep(100) | ||
except KeyboardInterrupt: | ||
print 'Quiting...' | ||
|
||
for p in processes: | ||
p.terminate() | ||
p.join() | ||
|
||
print 'All processes joined' | ||
|
||
for subscribersForMessage in subscribers: | ||
for subscriber in subscribersForMessage: | ||
subscriber.unregister() | ||
|
||
if __name__ == '__main__': | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
express = require 'express' | ||
|
||
class TimeStatistics | ||
constructor: -> | ||
@count = 0 | ||
@sum = 0 | ||
@min = Infinity | ||
@max = 0 | ||
|
||
update: (sample) -> | ||
@count += 1 | ||
@sum += sample | ||
@min = Math.min(sample, @min) | ||
@max = Math.max(sample, @max) | ||
|
||
toString: -> | ||
avg = @sum/@count | ||
"#{@count} messages received, avg: #{avg.toFixed(1)} ms (min: #{@min.toFixed(1)}, max: #{@max.toFixed(1)})" | ||
|
||
timesPerEvent = {} | ||
|
||
app = express() | ||
app.use(express.bodyParser()) | ||
|
||
app.post /^\/log\/(\w+)$/, (req, res) -> | ||
#console.log 'Received message' | ||
#console.log req.body | ||
|
||
receivedTime = Date.now()/1000.0 | ||
|
||
if not req.body.message?.default? | ||
console.log 'No default message!' | ||
res.send 400 | ||
|
||
body = JSON.parse req.body.message.default | ||
if not body?.timestamp? | ||
console.log 'No timestamp in the body!' | ||
res.send 400 | ||
|
||
event = req.body.event | ||
|
||
sentTime = body.timestamp | ||
diff = (receivedTime-sentTime)*1000 | ||
if not timesPerEvent[event]? | ||
timesPerEvent[event] = new TimeStatistics() | ||
timesPerEvent[event].update(diff) | ||
|
||
console.log "#{event} " + timesPerEvent[event].toString() | ||
|
||
res.send 200 | ||
|
||
port = 5001 | ||
console.log "Listening on port #{port}" | ||
app.listen port |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/usr/bin/env node | ||
require('coffee-script'); | ||
require('./pushlistener'); |