Friday, September 12, 2014

Adventures in Golang - Building a Celery Task API

I've been playing around a lot with RabbitMQ and Celery lately. They are really helpful tools for building asynchronous job processing systems but interfacing directly with the rabbit queues that power celery can be annoying. This is especially true if you are dealing with SSL encrypted AMQP sockets and third parties.

In order for secure communication between clients and RabbitMQ servers all of the clients need to have access to the client side certs and keys as well as the certificate authority cert. Additionally I've found that a lot of the open source celery client libraries don't support SSL connections at all. In many cases, in order to support a third party client you end up having to fork the client library, add SSL support, hope they are active and accept your pull request quickly, provide the third party with the SSL certs, and then help the third party connect to your servers.

The problem is that a lot of developers are not familiar with socket connections or AMQP in general so you end up investing more time to get them up and running. Restful services, however, have been popular for awhile now and a lot of developers have familiarized themselves with consuming them. So if all you need is to give people the ability to create tasks that will run asynchronously in the background the answer is simple. Build an API for them to consume.

I've been interested in golang for awhile now but never had the opportunity to build anything with it. I decided this was the perfect opportunity. So I spent a day or two and built the go-celery-api. Its my first attempt at something written in Go so I learned a lot in the process.

Go is an odd language when you have a background in more traditional object oriented languages. The structs and the way that functions bind to them feel odd, the receive operator was a new concept for me, and having functions that return multiple results is powerful but different. I'm used to handling exceptions when an error occurs but the go standard seems to be that functions should return a result object and an error object. If the error object is nil then you're good to go.

While most of these oddities are easy enough to get over and are to be expected when learning a new language the one thing that was somewhat shocking is that there is no officially accepted package management solution for go. PHP has composer, Node has npm, Ruby has gems, Javascript has bower, Go doesn't have any consensus. As I was looking for a package management solution for go it seemed that most people recommend copying the code directly into a vendor folder in your repo. That seems like insanity to me, but maybe I'm just spoiled.

There are some tools out there that provide package management and of the ones I looked at I liked gom the most. It provides the basic functionality of composer and I was pretty happen with that. Until I noticed that the packages I wanted to use weren't tagged. Apparently the golang standard is to maintain a clean master branch and not use tags. I guess that means that backwards compatibility changes are either not possible or then break everything using the package. At least gom allows me to lock dependencies to specific commit hashes.

Overall I think Go is an interesting language I'm looking forward to learning more about it. But for now I have a working API to create tasks for celery to execute. Check it out and let me know what you think.