Jan 31 2012

A fake Apple APNS for Performance Testing

Recently we had to conduct a performance test on our Push Notification Server software to measure how long it would take to deliver a large number of Push Notifications to the Apple APNS.

Bombarding the Apple APNS with 500,000+ messages might not go down well with Apple, and we didn’t want to risk getting our server IP blocked for any reason.

We created a fake APNS on a separate server that we could point our Push Notification Server to. This would accept the request but do nothing with it. It’s not a 100% accurate test (the Apple APNS would be slightly slower since it processes the data), but it’s good enough to find a benchmark.

We implemented this using Python, which you can find the code for below. To use this, run the script on the server you want to be the fake APNS. Then in your code that connects to the socket, replace the apple url with the one to this fake server.
e.g “ssl://gateway.push.apple.com:2195″ will become “tcp://yourserver.com:2195″

From this test and other tests we performed, we found that we could send between 1,000 to 3,800 Push Notifications a second to the APNS. Not too shabby I think!

import SocketServer
 
HOST = "0.0.0.0"
PORT = 2195
 
# this server uses ThreadingMixIn - one thread per connection
# replace with ForkMixIn to spawn a new process per connection
 
class EchoServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer):
    # no need to override anything - default behavior is just fine
    pass
 
class EchoRequestHandler(SocketServer.StreamRequestHandler):
    """
    Handles one connection to the client.
    """
    def handle(self):
        print "connection from %s" % self.client_address[0]
        while True:
            line = self.rfile.readline()
            if not line: break
            print "%s wrote: %s" % (self.client_address[0], line.rstrip())
            #self.wfile.write(line)
        print "%s disconnected" % self.client_address[0]
 
 
# Create the server
server = EchoServer((HOST, PORT), EchoRequestHandler)
 
# Activate the server; this will keep running until you
# interrupt the program with Ctrl-C
print "server listening on %s:%s" % server.server_address
server.serve_forever()