Batching multiple notifications

646 views
Skip to first unread message

mlunoe

unread,
Mar 2, 2012, 6:51:38 AM3/2/12
to Easy APNs
I am very excited about this library. Thank you so much for all the
work you put into this!

At first it seemed very capable of handling multiple notifications as
it can handle many applications. However, on further inspection I
found that it does not batch notifications as Apple encourages you to
do on their apns guide (https://developer.apple.com/library/mac/
#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/
CommunicatingWIthAPS/CommunicatingWIthAPS.html). For each message it
opens a stream, sends a notification and closes the stream, which can
be optimised. Here are the code that opens a stream, sends all the
queued messages (in a batch) and closes the stream:

////////////// BEGIN NEW ADDITION ////////////////

/**
* True if running in sandbox
*
* @var bool
* @access private
*/
private $isSandbox = true;

/**
* The limit of how many messages to send
* @var int
* @access private
*/
private $limit = 100;

private function _checkMessage($pid, $message, $token, $development) {
return strlen($pid)!=0 && strlen($message)!=0 && strlen($token)!=0 &&
strlen($development)!=0;
}

////////////// END NEW ADDITION ////////////////

////////////// BEGIN REPLACE EXISTING ////////////////

private function _fetchMessages(){
// only send one message per user... oldest message first
$sql = "SELECT
`apns_messages`.`pid`,
`apns_messages`.`message`,
`apns_devices`.`devicetoken`
FROM `apns_messages`
LEFT JOIN `apns_devices` ON (`apns_devices`.`pid` =
`apns_messages`.`fk_device` AND `apns_devices`.`clientid` =
`apns_messages`.`clientid`)
WHERE `apns_messages`.`status`='queued'
AND `apns_messages`.`delivery` <= NOW()
AND `apns_devices`.`status`='active'
AND `apns_devices`.`development`=".($this->isSandbox ?
"'sandbox'":"'production'").
" GROUP BY `apns_messages`.`fk_device`
ORDER BY `apns_messages`.`created` ASC
LIMIT ".$this->limit.";";
$development = $this->isSandbox ? 'sandbox': 'production';
if($result = $this->db->query($sql)){
if($result->num_rows){
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $this-
>apnsData[$development]['certificate']);
$fp = stream_socket_client($this->apnsData[$development]['ssl'],
$error, $errorString, 100, (STREAM_CLIENT_CONNECT|
STREAM_CLIENT_PERSISTENT), $ctx);

while($row = $result->fetch_array(MYSQLI_ASSOC)){
$pid = $this->db->prepare($row['pid']);
$message = stripslashes($this->db->prepare($row['message']));
$token = $this->db->prepare($row['devicetoken']);

if(!$fp){
$this->_pushFailed($pid);
$this->_triggerError("Failed to connect to APNS: {$error}
{$errorString}.");
}
else if(!$this->_checkMessage($pid, $message, $token,
$development)) {
$this->_pushFailed($pid);
$this->_triggerError("Invalid message for device pid: ".
$pid.".");
}
else {
$this->_pushMessage($fp, $pid, $message, $token, $development);

}
}
fclose($fp);

$this->_checkFeedback($development);
}
}
}

private function _flushMessages(){
// send all the messages for each device
$sql = "SELECT
`apns_messages`.`pid`,
`apns_messages`.`message`,
`apns_devices`.`devicetoken`
FROM `apns_messages`
LEFT JOIN `apns_devices` ON (`apns_devices`.`pid` =
`apns_messages`.`fk_device` AND `apns_devices`.`clientid` =
`apns_messages`.`clientid`)
WHERE `apns_messages`.`status`='queued'
AND `apns_messages`.`delivery` <= NOW()
AND `apns_devices`.`status`='active'
AND `apns_devices`.`development`=".($this->isSandbox ?
"'sandbox'":"'production'").
" ORDER BY `apns_messages`.`created` ASC
LIMIT ".$this->limit.";";
$development = $this->isSandbox ? 'sandbox': 'production';
if($result = $this->db->query($sql)){
//var_dump ($result);
if($result->num_rows){
$ctx = stream_context_create();
stream_context_set_option($ctx, 'ssl', 'local_cert', $this-
>apnsData[$development]['certificate']);
$fp = stream_socket_client($this->apnsData[$development]['ssl'],
$error, $errorString, 100, (STREAM_CLIENT_CONNECT|
STREAM_CLIENT_PERSISTENT), $ctx);
while($row = $result->fetch_array(MYSQLI_ASSOC)){
$pid = $this->db->prepare($row['pid']);
$message = stripslashes($this->db->prepare($row['message']));
$token = $this->db->prepare($row['devicetoken']);

if(!$fp){
$this->_pushFailed($pid);
$this->_triggerError("Failed to connect to APNS: {$error}
{$errorString}.");
}
else if(!$this->_checkMessage($pid, $message, $token,
$development)) {
$this->_pushFailed($pid);
$this->_triggerError("Invalid message for device pid: ".
$pid.".");
}
else {
$this->_pushMessage($fp, $pid, $message, $token, $development);

}
}
fclose($fp);

$this->_checkFeedback($development);
}
}
}

private function _pushMessage($fp, $pid, $message, $token,
$development){
$msg = chr(0).pack("n",32).pack('H*',
$token).pack("n",strlen($message)).$message;
$fwrite = fwrite($fp, $msg);
if(!$fwrite) {
$this->_pushFailed($pid);
$this->_triggerError("Failed writing to stream.", E_USER_ERROR);
}
else {
$this->_pushSuccess($pid);
}
}

////////////// END REPLACE EXISTING ////////////////

Hope you can use this for your own projects!

Regards Michael

Guillaume

unread,
Mar 14, 2012, 9:43:42 AM3/14/12
to Easy APNs
_flushMessages() and _fetchMessages() are same functions?

With theses modifications it seems it's possible to send push quickly.
But if I have 90,000 tokens, and so I put the $limit = 100000; the
$msg to send will be too large, no ?
Message has been deleted

yossi cohen

unread,
Mar 15, 2012, 5:32:33 AM3/15/12
to easy...@googlegroups.com
Hi Michael,

Thank you for the mail

I have APNS package on my server for few month now , but I never tested a real broadcast 
is I have 70,000 users and I don't want it to take hours to sending

I want to use your suggestion for modification for my first try for broadcast

- did you use it already ?, if yes do you have idea how much it will take to send 70,000 messages ?
- it is just to replace the current code with those you sent in your mail ?

Thanks!,
Spring



2012/3/2 mlunoe <michae...@gmail.com>

--
You received this message because you are subscribed to the Google
Groups "Easy APNS" group.
To post to this group, send email to easy...@googlegroups.com
To unsubscribe from this group, send email to
easyapns+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/easyapns?hl=en

Yossi Cohen

unread,
Mar 15, 2012, 5:14:54 AM3/15/12
to easy...@googlegroups.com
Dear Michael ,

Thank you for your mail, I'm also like much APNS packages if works for m

mlunoe

unread,
May 7, 2012, 5:08:37 AM5/7/12
to Easy APNs
No, there is actually no difference. In the php source of easy apns
they have both _fetchMessages() and _flushMessages(), where the first
takes 100 messages at a time and the latter takes $this->limit, so it
is adjustable. I ended up deleting one of them to use only the
adjustable as they where the same.

mlunoe

unread,
May 7, 2012, 5:11:29 AM5/7/12
to Easy APNs
They are the same. In apns php source they vary by _fetchMessages()
always takes $this->limit messages at a time, whereas _flushMessages()
takes all of them. I ended up only using _fetchMessages(), because I
can better control what happens.

On Mar 14, 3:43 pm, Guillaume <guilla...@gmail.com> wrote:

mlunoe

unread,
May 7, 2012, 5:30:54 AM5/7/12
to Easy APNs
Yes, I used it to build a campions league app to send out 10,000
messages each minute and it takes about ten seconds for it to finish.
I don't know Apples limit for batching, so to avoid your server
getting banned from using apn, I would search the web for a limit. And
please post what you find here, so we can all learn! :)

Yes, it should be possible to just replace with my code and add the
"new addition" for it to work. No, errors with it have been reported
so far, but please keep me updated!

br. Michael

On Mar 15, 11:32 am, yossi cohen <yossi.c...@gmail.com> wrote:
> Hi Michael,
>
> Thank you for the mail
>
> I have APNS package on my server for few month now , but I never tested a
> real broadcast
> is I have 70,000 users and I don't want it to take hours to sending
>
> I want to use your suggestion for modification for my first try for
> broadcast
>
> - did you use it already ?, if yes do you have idea how much it will take
> to send 70,000 messages ?
> - it is just to replace the current code with those you sent in your mail ?
>
> Thanks!,
> Spring
>
> 2012/3/2 mlunoe <michael.lu...@gmail.com>
Reply all
Reply to author
Forward
Message has been deleted
0 new messages