Ruby, SOAP and You
SOAP is sort of a despised thing in the Ruby world. I bought the Rails SOA book and in the foreword by Obie Fernandez it basically says "Yea, SOAP ain't in here". And I get it. SOAP sucks. However, we don't always get to choose the vendors with the sexy APIs. Sometimes things are heaped upon us. Sometimes, you gotta do it. So I started poking around. My caveat is that soap4r just didn't really get attention from me because it seems pretty neglected, so I may be way off here. So I looked at Savon, which is something that is already heavily used in production where I work.
Anyway... (full Gist here: https://gist.github.com/1289826)Intro
Tests were run against localhost. The SOAP server is Fuse ESB (Apache Servicemix) with the 'cxf-osgi' example that provides a dirt simple HelloWorld. The CXF route utilizes the strategy outlined here: https://gist.github.com/1289254 and I've appended my dao code for the hell of it. The not showing bits are the generated sources from CXF. Everything was done for 1000 SOAP calls.
Versions
JRuby was 1.6.3 w/ --1.9. MRI Ruby was 1.9.2-p290. REE ree-1.8.7-2011.03. Rubinius was rbx-head via rvm. It choked on the threaded text, and macruby would run neither test. Apache CXF was 2.3.7.
Details
I ran everything on my Macbook Pro, 2.4GHz Core i5 w/8GB of RAM.
Issues
I continually ran into issues with Savon.
I would pretty consistently get what seemed to be threading issues with Nokogiri. This seemed to get alleviated by removing the WSDL parse step and just setting the namespace/endpoint directly, which as a caveat, is the recommended performant way according to the Savon docs. I used version 0.9.7 for my tests.
httpi which Savon uses throws errors when threaded.
NameError: uninitialized constant HTTPClient
I'm not sure what the root semantics causing this is, but the lib/httpi/adapter/httpclient.rb file references the httpclient class without requiring it. I suspect it works generally because there's an assumption that whatever has required the adapter has required httpclient. When I added the require statement to the adapter, this seemed to fix the issue.
First I tried my idea for the hybrid CXF/JRuby approach.
Really solid performance here. I may go back and update, but I'm sure that using a threadpool would pull out some much better numbers than this naive business.
Next was Savon, with 1000 threads.
The MRI and REE just couldn't quite keep up. And the JRuby perf. was so much worse I have to wonder what was going wrong. Maybe my 'fix' is partially to blame? No. I reran the threadless test (coming up), with and without my additional require statement and the difference is negligible.
So what about threadless?
Solid uptick, all around. JRuby Savon is not so bad, but still quite behind. Rubinius finally works, but ultimately lags. The kicker here though is that not using threads got some interesting performance bumps. The thing is, it makes me want to say this is more about the library and less about the Ruby impls. I just don't think that it's really meant for high concurrency. Because of the gnarly that is SOAP, for many it's about back end integration calls, nightly jobs and mundane things not performance oriented. But what about when you need throughput? Anyway, MRI actually comes quite close to the warmed JRuby/CXF combo. The thing I get out of this is that if you're in a CRuby environment and you need SOAP, it seems you should walk away from concurrency and look at some sort of command queue.
In the end I was interested to see how the CXF hybrid approach really shone. I've been dealing with SOAP stuff for a few years now and it's always just given me far and away better luck than anything else. Predecessor XFire worked out pretty well too. The negative here is that you've got to drop down and do the interface work in Java. Could you go farther and just do all your CXF wiring in Ruby and eschew the generated classes? Sure. Doing that bit in Java though gives me one wrapped library that's usable in any JVM language though. I'll probably eventually give it a spin though.
There ARE reasons other than performance though: Namespaces are an issue. That's why I hardcoded the SOAP body; because the hash syntax just wouldn't work. And no, this isn't some quirk to CXF. I've seen this with WSDLs from completely unrelated platforms. And, of course there's the fact that not all services produce 'good XML' or even valid XML. With the CXF generated source, I feel like I have a bit more unmarshalling tweak power, but I fully recognize I may just not be aware of the best ways to do it with Savon.
SOAP may be dirty, but it's not going anywhere. (Unfortunately)