Apache Thrift For Cross-Platform Web Services

In this post I’ll share my experiences on using Apache Thrift for developing a web service consumed by mobile and web clients, and why it didn’t quite work out as smoothly as I hoped.

The Problem

I had an idea for a mobile app (Android and iOS) backed by server-side functionality. A web version could be added later. The service would be written in Java or Scala.

Backward/forward compatibility is especially important with mobile apps. With webapps you can make a breaking change to the service, update the HTML/JavaScript assets at the same time and browsers will automatically fetch the new client code. But with mobile there’s no easy way to force people to upgrade to a new version of your native app, so the old client must keep working with new service.

Hence I wanted to take a schema-first approach and clearly define and evolve the service contract in some IDL. I also liked the idea of auto-generating code from the schema into different languages/platforms to cut development time. Finally, I thought using a binary format (as opposed to e.g. JSON) would help reduce network traffic.

The Solution

After some investigation Thrift seemed a very good fit for my needs. Its IDL supports all the common data types and it has an easy to read C-like syntax. Unlike e.g. Google Protocol Buffer it’s not just a serialisation format but also an RPC framework. It has a binary format. It supports not just Java but also Cocoa (iOS), and JavaScript. And there’s even an alternative code generator called Scrooge (by Twitter) for idiomatic Scala.

To provide me with further reassurance on the fact that it is suitable for mobile apps, Evernote apparently uses it.

The Reality

Everything worked well enough with a Java service and an Android client. Thrift generates all the code for the “transfer objects” defined in the schema, the full client, and a service interface to be implemented with the actual functionality in the server.

Thrift actually offers a number of different protocols, transports, and servers, best documented on Wikipedia. For my purposes the obvious choices were TServlet on the server and a THttpClient on the client. Since there is more than one binary format I opted for the TCompactProtocol that is described as more efficient.

The Java client worked pretty well on Android (2.3+). One pitfalls I initially fell into is that the generated client is not thread-safe and it may error about responses “out of sequence” if you use it for multiple concurrent requests.

When I switched the server to Scala and Scrooge, I realised that the code generated by Scrooge could not be used with a TServlet. It’s intended to be used with Finagle which is another Twitter project. I eventually worked around this limitation. It’s also possible to use Thrift-generated Java code with Scala of course, it’s just not as nice.

When I got to iOS, I discovered that the Cocoa client does not support the TCompactProtocol but only the TBinaryProtocol. So I had to change the service to serve both TCompactProtocol (already used by Android) and TBinaryProtocol (for iOS).

Oddly, Thrift always sets application/x-thrift as the Content-Type no matter the protocol/format, so I had to customise servlet and client to use different values application/x-binary-thrift and application/x-compact-thrift to be able to serve both at the same time.

An additional gripe with the Cocoa support is that network calls in an iOS app need to be async (otherwise the UI freezes) but the client does not generate an async interface, requiring you to write a lot of boilerplate code involving NSOperationQueues. But it worked in the end.

Finally, I started looking at the web client and discovered that the JavaScript library does not support TCompactProtocol nor TBinaryProtocol, but only TJSONProtocol.

Being open source you could always add/improve what is missing or lacking of course, but that would involve extra effort. And the code generator written in C++ doesn’t look particularly easy to customise. (The Scrooge one actually looks better.)

Conclusion

Apache Thrift offers a schema-first, code generation based RPC framework with support for many languages and platforms which conceptually is well suited for developing cross-language and cross-platform services.

However, you should be aware that Thrift is not a single serialisation format but has different protocols and transports. Although libraries for all the various different languages are included in the Thrift distribution, not all features are supported by all libraries, and individual libraries are of varying quality.

Therefore, you may want to check beforehand if the combination of protocol, transport, server, and languages you’re interested in works as expected.

2 thoughts on “Apache Thrift For Cross-Platform Web Services

  1. Very good post. One more fact: “Everything worked well enough with a Java service and an Android client” if you don’t hit the 64k method limit since Thrift generates thousands of thousands lines of code with simple data structures.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s