Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Related projects #53

Open
nathany opened this issue May 22, 2015 · 10 comments
Open

Related projects #53

nathany opened this issue May 22, 2015 · 10 comments

Comments

@nathany
Copy link
Contributor

nathany commented May 22, 2015

For a little inspiration, I'm taking a look at other Go projects for APNS, starting with the one @finkel mentioned in #47 and then the one @themartorana mentioned (#38). @willfaught mentioned anachronistic in #26 (comment).

apns by Pranav Raja @pranavraja https://github.com/pranavraja/apns

  • Exposes a queue (a slice) for sending multiple notifications at once. Timehop has an internal circular buffer rather than exposing a queue. Currently neither implementation batches multiple notifications into a single frame.
  • It uses the synchronous approach of sending a batch, then reading with a timeout. Timehop does the read on a separate goroutine. Based on Apple's documentation I believe the separate goroutine to be more responsive, though reading with a timeout does make for a nice and simple synchronous API.
  • Rather than resend failed messages internally, it reports what was unsent and reslices the queue for resending (less magic).
  • There is a timeout parameter for sending. 👍
  • Exposes a lower-level API to read errors from the socket and decode the Apple format. This is an interesting alternative to passing those errors over a channel. Timehop does a blocking read in a goroutine and sends errors over FailedNotifs, but a blocking read in a goroutine could be part of the client code or a high-level API.
  • Offers a lower-level interface where you can make a notification before sending it.
  • Notification is a separate package. 👍
  • MakeNotification takes a string rather than defining the structs for JSON serialization.

SendOne, MakeNotification, and ReadInvalid make for a nice lower-level API that other things can be built on. I'm not sure how I feel about the queue. If I was fanning-out the same/similar message to a number of devices or using it as a mechanism for batch & send, maybe it could work, but managing multiple queues at once (requeuing on errors while sending more messages) could become cumbersome for the client.

go-libapns by Karl Kirch @joekarl https://github.com/joekarl/go-libapns

  • Batches push notifications up to 64K (TCP packet size) and flushes every 10ms. This is accomplished with an internal inFlightFrameByteBuffer.
  • Doesn't use Nagel Using Nagle's algorithm? #32 in order to have a shorter timeout length (REAMDE includes explanation)
  • Provides two functions for creating connections, one takes an existing tcp net.Conn for use on Google App Engine, etc.
  • API uses channels (SendChannel) rather than a Send() method (Timehop currently puts a notification into an internal channel). Also uses a runLoop called sendListener.
  • When an error occurs, it sends the notification that failed and all the unset notifications over a CloseChannel.
  • To clear badges, it was relying on negative numbers (instead of 0), but now uses BadgeNumber from @themartorana. See Move away from pointers for badge field? #38.
  • It will clip the alert body if the payload is over Apple's 2K limit.

apns by @anachronistic https://github.com/anachronistic/apns

  • Alert uses an interface that can be either a string or an AlertDictionary. Timehop currently checks if only Alert.Body is set and serializes the JSON in the more efficient way, but the API still requires an Alert struct.
  • It has a PayloadString() for serializing the JSON without doing the binary encoding or sending it.
  • It connects, writes and waits for a response (with a timeout) all in one step. This may be fine for very low-volume push notifications, but Apple treats rapid connection and disconnection as a denial-of-service attack.
  • Overrides Badge 0 to -1 (not sure the point, vs just not using omitempty). Not sure if it's possible to leave badge as-is (omitempty)?
  • Does a check for MaxPayloadSizeBytes and returns an error. Timehop does not.
  • Generates a pseudo-random identifier up to 9999 rather than incrementing a counter or requesting it from the caller.

apns by Arash Payan @arashpayan https://github.com/arashpayan/apns

  • Uses a pseudo-random identifier like anachronistic.
  • Uses a slice to store the last 25 notifications (rather than a list). Hm.
  • Uses an invalidTokenHandler callback
  • Exponential backoff for reconnects.

apns by Ryan Slade @ryanslade https://github.com/ryanslade/apns

  • Has a shortcut for pushing a message with just an alert (PushMessage).
  • Uses an idChan to generate incrementing identifiers
  • Launches a go routine to resend payloads on failure.
  • Holds on to a slice of payloads and cleans them up after 5 minutes rather than using a circular list that limits the amount.

apns on App Engine by Siong @siong1987 https://github.com/siong1987/apns

  • This uses appengine.Context and appengine/socket.
  • It has it's own PEM loader that supports keys with a passphrase.
  • Uses the synchronous read after writing method.
  • Has a connection pool (10 connections to Apple).
  • Uses a pseudo-random identifier like anachronistic.

hermes APNS/GCM by Paul Karadimas @pkar https://github.com/pkar/hermes

Go-APNs by Matthew Price @mattprice https://github.com/mattprice/Go-APNs

apns-go by @Kwik-messenger https://github.com/Kwik-messenger/apns-go

go_apns by @MessageDream https://github.com/MessageDream/go_apns

Go-Apns by @virushuo Ju Huo https://github.com/virushuo/Go-Apns

  • The example is interesting in that mutates an existing payload, yet Send takes a pointer to a notification. Hm.
  • Overall a similar architecture with a sendLoop and reconnecting. It also uses an errorChan for reporting errors (but of type error) and a concrete NotificationError struct with an OtherError for storing read errors, etc.

APNs by Cornel Damian @corneldamian https://github.com/corneldamian/APNs

APNs by Matt DuVall @mduvall https://github.com/mduvall/go-apns

go-apns by Fabrizio Milo @Mistobaan https://github.com/Mistobaan/go-apns

Apns by @houxiaobei https://github.com/houxiaobei/Apns

@finkel
Copy link

finkel commented May 22, 2015

Great idea. The other library you might want to check out (the one I'm actually using now) is https://github.com/virushuo/Go-Apns

@pranavraja
Copy link

Thanks for the mention!
My library is a few years old, but in case it helps, the main goals were:

  • expose as much as possible to the client
  • provide a synchronous API and allow the caller to bring their own concurrency/pooling/etc.

I have a SendAll method which does resend messages internally, but in production we were unsure what the right tradeoffs would be so we ended up using the lower-level methods, logging all the failures/resends on the server and adjusting the batching later on to suit our traffic. Benchmarking this was difficult, as you can imagine, so i'm still not sure whether this made much difference - all I remember was that the throughput was 2x compared to my earlier attempt in Ruby =]

@joekarl
Copy link

joekarl commented May 23, 2015

Figure I'll chime in as well. As for mine I was specifically going for speed and correctness on error cases. Most libraries try to resend notifications in error situations which muddies up the error handling in certain cases. I just avoided all of that and just supply the needed information for the user to do what they need to do. This was a better situation than the Java-apns library I had worked on before where there were serious questions about whether error cases were handled correctly. To determine correctness in go-libapns there's actually some pretty decent tests covering the various failure methods from socket level to apple return codes.

Overall though I don't really know that anyone is using it heavily (haven't even been using it myself) but it should be fairly solid at this point. (There was a pretty egregious bug where in connection close all sent pns would say they couldn't be sent but that's been fixed).

@arashpayan
Copy link

My turn. 😄 The library is still being used in production on a service, but I don't think it gets massive amounts of traffic, so I can't speak to it's performance characteristics. Interesting point about it storing the recent notifications in a slice vs. a list. I'll need to revisit that the next time I use the code in a project.

I'd also want to improve the error handling. Currently, it only reports invalidToken errors back to the library user (https://github.com/arashpayan/apns/blob/40c200c63099bf022d02db2bc6ff7768953cf7f2/apns.go#L236) but I'd like to improve that so it returns all errors to the library user's callback.

@siong1987
Copy link

I created mine just because there wasn't a good one that I could actually use that worked on Google App Engine. I am no longer using it tho, i wouldn't recommend anyone using it :)

@nathany
Copy link
Contributor Author

nathany commented May 25, 2015

Wow. Thanks everyone. 😃

@pkar
Copy link

pkar commented May 28, 2015

Oh neat, a github mention. I can add that the hermes code is sending out ~20m notifications per day off of a Redis queue. Half for apns for probably a few hundred apps. The final error/retry handling was intentionally left out because it's tied to legacy infrastructure and would have made it a bigger mess

@Mistobaan
Copy link

Thank you for the mention. That was my first go project and probably is not the best APNS code out there. Came from the need of having that service when AWS did not provide APNS integration.

@joelchen
Copy link

There is also https://github.com/mentionapp/apns.go as mentioned in #50.

@nathany
Copy link
Contributor Author

nathany commented Dec 18, 2015

Since Apple dropped a new HTTP/2 API #57, I've been working on a new library that requires Go 1.6.
https://github.com/RobotsAndPencils/buford

I've used some of the ideas I saw in other libraries while designing the API. Hope you like it, but if not, this is a good time to help tweak it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants