I'm designing a push notification server (for pushing notification via APNS and Google servers) and have come up with the following design for a Java based web application server. I would really like to hear anyone feedback from people who have experience with anything relating to this. This is a fairly high level design so I might be missing some minor implementation details.
- Push notifications will either go via APNS (for iOS) or GCM (for Android)
- To push to iOS, per Apple's advice, connections to APNS (the Apple push notification service) have to remain open at all times
- The APNS connection is via a binary TCP socket - if there is a problem with the APNS servers or a problem with a request, the APNS server can terminate your connection. If there are no problems there is no response. To avoid losing messages, after sending a message there will be a waiting period of ~1 sec to ensure the connection is not closed.
- To push to Android, can just make HTTP requests to GCM.
- Various other components can push to mobile devices via this server so I will expose an (HTTP) API via a servlet
- No individual components should be able to deny service to other components by submitting many push notification requests to be fulfilled
- Any errors when sending the push notifications via Google or Apple should be persisted to a database
The Servlet will expose a HTTP interface that will accept a push request. The request contains all information required to make the push with APNS or GCM, as well as which platform the push should go via (either APNS or GCM). The request also contains information about the component that sent the request. When the servlet receives a request, it will place it in a JMS queue and then immediately return success (ie HTTP 200 response) if placing it in this queue was successful.
The Servlet will have a single background daemon that starts on the
ServletContextEvent. This daemon will be the 'push manager' and upon initialisation will spawn a number (~10) of persistent worker threads. Half the threads will be for pushing to Android and half to iOS.
The background daemon will block on a JMS queue, and when it receives a message on the queue, place it into its own "round robin" queue that is based upon the component that the message is from. So that no single component can dominate this server, one message from each component will be processed at a time.
The background daemon will then give the message off to a worker thread that is free for the platform that the message is for. If there are no free worker threads, the daemon will wait until one becomes free. Before giving a message to the worker thread, the daemon first checks if there were any problems with the previous message and if there were, persists these to a database.
On initialisation, the iOS threads will immediately open a connection to APNS.
Both types of workers threads will infinitely loop, blocking on waiting for a message from the daemon. On receiving a message, the thread will send the message via APNS or GCM, check for any errors (by waiting for the response for GCM or waiting for a set period for APNS) and then block again on waiting for a message, returning the error information to the daemon once the next message comes through.
Is this a feasible plan? Is there any better ways to go about this? Am I reinventing any wheels?