Push notifications is a really great mechanism available to all mobile developers (and beyond). With notifications of this type, you can notify users of important events, so that they can respond as quickly as possible, even when the application is not running. Moreover, with this approach you can boost user awareness of your product. In this post, we are going to share our hands-on experience in implementing such functionality in our large-scale projects and provide a number of tips and tricks.
First of all, let’s note that today’s services are running on a variety of platforms, so be ready to support the following:
- Apple Push Notifications Service – APNS
- Google Cloud Messaging – GCM
- HTML5 WebSocket and Comet – for browser-based notification.
Deciding to use APNS and GCM, please consider the following technical specifics: no message delivery guaranteed, lack of easy-to-use logging and statistics for sent and received messages, and message size limits.
Our expertise shows that although there is no delivery guarantee, such services are very stable, have high performance and deliver messages almost instantly (in just seconds). So in most cases, APNS and GCM are exactly what you need. However, if we need messages to be delivered with a 100% certainty and a guaranteed speed of delivery, we’d better build our own message delivery infrastructure.
When a client device receives a push notification, the application can be run actively or in the background, or may not run at all. Message handling in this case is different. If the application is running, the application itself processes the message (a callback method is triggered). If the application is not running, the notification is handled by the operating system (notification name and ID counter are assigned); when the application starts, the accumulated notifications are sent for processing.
The process of integration with the message delivery services is quite simple and well-documented: here is the APNS documentation and GCM documentation. Next, we are going to tell you about our basic findings and provide some recommendations.
- Messages can be sent from multiple servers in multiple threads: this is very helpful when you need to deliver lots of notifications at once
- Very often, performance is affected by message preparation bottlenecks (recipient selection, message generation, etc.). It means that all the issues are on your side and you should not blame Apple or Google services for this.
- Sometimes user tokens invalidate, for example, because the user has removed your application. To remove such tokens from your database, you have to periodically request the feedback service for such tokens. If you fail to do it, your notifications will start processing slower or even fail to reach your recipients.
- Please feel free to post your questions to the Apple forum: they are open to communications and can check delivery issues with their logs (you have to provide a device token and notification sent timestamp)
- It is possible to send notifications to test versions of applications not uploaded to the App Store.
- For your sandbox environment, you need to generate separate certificates. In theory, user tokens may also vary for each application, certificate or environment. Please do not confuse different token certificates and do not mix tokens from various applications and environments with each other.
- As usual, notifications are sent asynchronously, without waiting for a response from the APNS server. In case a message is erroneous, the server simply disconnects. This is intended to speed up message delivery. But if you need to understand the error cause, you may use a special "error response" flag for your message. In this case, you can mark each message by a unique identifier and get a relevant error code.
Notification delivery to the server
If the service is intended to send lots of notifications to different devices (iOS, Android) and browsers, it is better to parallelize messaging anyway. Based on the experience, three stages of notification preparation and notification can be outlined. Each stage can be better or worse parallelizable, depending on the service logic.
- Select users to receive the next notification. For performance reasons, at this stage it is best to select the users who should be notified regardless of the platform they run. It would be nice if you could keep your user selection cached, rather then run database select for each notification. However, this is not always feasible. User selection is quite difficult to parallelize. For instance, you may run different threads on different user array ranges based on their primary ID.
- Prepare notifications in formats of specific delivery systems for user selection obtained in the previous paragraph. This stage is well-parallelizable, as user arrays can be processed independently.
- Interact with the external notification service. This stage is also well-parallelizable, as all push-notification services can receive notifications for the same application through multiple threads, even from the same IP address. If we organize delivery from multiple servers, or from one server providing several threads, we can achieve a very high performance of interaction with the external service. Our experience with APNS shows that for each application run on APNS there is a queue, so the maximum speed of notification delivery to APNS is reached at 10-15 messaging threads. Further increase of the number of delivery threads fails to increase the speed of notification delivery to devices.
Overview of off-the-shelf push-notification solutions
Please note that, all of the solutions discussed below enable interaction with an external service only. It means that introducing of push-notification to any project is quite a time consuming process, as you have to implement the first and second stages on your own.
Any professional notification solution should wrap the service, hiding all intricacies of service interaction from the developer, and, which is more important, providing for distributed messaging. Hence, if some hypothetical solution fails to meet these two criteria, it should not be seriously considered.
At the moment, there are lots of off-the-shelf services to send notifications to iOS devices, and virtually no solutions for GCM. However, this is compensated by the fact that GCM is much easier for the developer, as in case of GCM service interaction is implemented by POST requests only, while the APNS service requires a permanent socket.
The most fascinating PHP-based solution is the apns-php library. It provides object-oriented notification handling and contains a template for a multi-threaded messaging daemon. For a notification queue, a shared memory mechanism is used to enable multi-threaded delivery.
For python, there is the pyapns service. Unlike apns-php, pyapns is a service that runs as a twisted server, with all interaction with it run over the network. It is assumed that a lot of messaging services are behind a proxy balancer, which allows to provide for horizontally scalable messaging. Pyapns already includes clients to interact with it from python or ruby. As pyapns can run as a separate service, you can write a client in any language, including PHP.
However, GCM is far from having the same number of messaging solutions available. Basically, there are primitive wrappers for different languages, which to some extent conceal the services. However, as it has been mentioned above, the process of interaction with GCM is easier than with APNS, so writing your own parallel messaging system should not be a big deal.
To develop Web-based push-notification system, there are always two scenarios: using of a third-party service (http://www.webdistortion.com/2012/02/02/real-time-push/), or fully implement the system independently. If you opt for independent development, the main components of the system are:
- Socket server. Node.js can be a good choice, however if you have expertise in writing a socket server in your main project language (such as Python), it is better not to add another technology to your stack.
- The pubsub message queue (publish subscribe). To implement this task, Redis is a good choice.
Wishing you lots of recipients for your push notifications! 🙂