Ruby best practice for "always on" app/service?

Y

yermej

I'm building an app that will essentially be a web service client. It
will listen for requests from our other internal applications and then
make calls to the proper service, reformatting the data before
returning it to the requesting application.

In the past, I've done this by running a web app via WEBrick or
mod_ruby. Our internal application hits a URL with the data required
for the service request, the client class is instantiated by the
server (be it WEBrick or apache), and the request is handled with
cookies being saved/loaded as needed for session based authentication.

Is there a more accepted way to handle this? Something where the web
service client would be always running and somehow listening for a
request? I may just stick with what we're currently doing, but I'd
like to know what my Ruby options are and what others are using in
similar situations.

Thanks.
Jeremy
 
R

Richard Conroy

[Note: parts of this message were removed to make it a legal post.]

I'm building an app that will essentially be a web service client. It
will listen for requests from our other internal applications and then
make calls to the proper service, reformatting the data before
returning it to the requesting application.

So the web service client IS a web app? That proxies/intermediates
between (1) a set of back end web services and (2) people or other
services?

In the past, I've done this by running a web app via WEBrick or
mod_ruby. Our internal application hits a URL with the data required
for the service request, the client class is instantiated by the
server (be it WEBrick or apache), and the request is handled with
cookies being saved/loaded as needed for session based authentication.

Well everything is Rack enabled now. Install Passenger, deploy your
Rack enabled web service proxy (simple sinatra apps are always good
for these kind of things), and put your feet up.

Rack compatible web container + Rack app is pretty much standard
practice for deploying Ruby web based solutions nowadays. Deploying
non-Rack ruby web apps would be considered an architecture smell now.
Only exceptions in my mind would be JRuby specific deployment options
like Warbler. Glassfish and the JBoss option (sorry cant think of it now),
and
some of those are Rack-enabled too.

Is there a more accepted way to handle this? Something where the web
service client would be always running and somehow listening for a
request? I may just stick with what we're currently doing, but I'd
like to know what my Ruby options are and what others are using in
similar situations.

You haven't gone into enough detail on the nature of the participants in
your services. Its pretty important to understand whether the communication
between your back-end web services and your web service client is
bi-directional
first. Another thing to consider is whether there is significant delay in
the execution
of the web services between your client and your back end services.

Note that if this delay is significant (500ms+), you are likely to need a
background job
scheduler too, of which there are many in Ruby (Delayed Job seems to be
getting
very popular now).
 
D

David Masover

In the past, I've done this by running a web app via WEBrick or
mod_ruby.

Or Mongrel, Thin, Unicorn, etc -- anything Rack-based. (If you aren't Rack-
based, you should be. Sinatra makes it easy, but even Rails is built on Rack
these days.)

But all of these are mostly performance-tuning hacks -- most don't
fundamentally change what you're doing.
Is there a more accepted way to handle this? Something where the web
service client would be always running and somehow listening for a
request?

How is that different than what you're doing?

I suppose something like mod_ruby might listen without a worker running, then
instantiate your app when you get a request. But many other servers simply
embed themselves into a running process -- that IS your app always running,
waiting for a request. Unicorn combines both -- it loads your app, then forks
it off into several workers, so you have MANY copies of your app always
running, waiting for a request.

Maybe I don't understand what you mean by "always on"?
 
Y

yermej

How is that different than what you're doing?

I suppose something like mod_ruby might listen without a worker running, then
instantiate your app when you get a request.

That's how mod_ruby and WEBrick work.

I wasn't entirely sure of what my question was when I started this
thread. A couple replies have helped me figure out what else to ask
about. I'll definitely look into the Rack-based options.
But many other servers simply
embed themselves into a running process -- that IS your app always running,
waiting for a request.

This is what I meant by "always on". Probably not the right term, but
it was a starting point.

I was also not detailed enough about what I'm doing. In my reply to
Richard's reply, I'll bring up my other concern which is how to handle
a situation where my proxy/adapter app is being called by other apps
on the same server rather than as a web service -- more of an
interprocess communication issue, I guess. However, for actual web
services, I now know to look to Rack-based technologies.

Thanks for the reply.
Jeremy
 
B

Brian Candler

Richard said:
Well everything is Rack enabled now. Install Passenger, deploy your
Rack enabled web service proxy (simple sinatra apps are always good
for these kind of things), and put your feet up.

Seconded.

Phusion Passenger is basically mod_rack for Apache. So you simply start
Apache, and it takes care of accepting requests, starting up the
appropriate number of child processes, and handing out requests to those
processes.

Each client is talking to once process at once, so you avoid all
threading headaches entirely.
 
Y

yermej

So the web service client IS a web app? That proxies/intermediates
between (1) a set of back end web services and (2) people or other
services?

Basically, but I'm not sure my web service client should be a web app
in all cases (this is getting confusing). As I mention in my reply to
David, I wasn't sure what exactly I was asking when I started this
thread. I just feel like I'm missing something that may be more robust
than WEBrick or mod_ruby. I'll definitely look into the Rack-based
technologies for future use.

At this point, I'm basically writing adapters to SOAP services
provided by outside companies to allow ordering and other inquiries.
They're accessed via the public internet so there can sometimes be
delays in responses, but generally under 2s, I think. Eventually I'll
be using similar SOAP services from several companies. The adapters
will be used so that internal apps can access those services via a
single API. A controller will decide which external company to
interact with in a given case and the adapter will handle the details
of that particular company's SOAP implementation.

Communication can be initiated from either end. In some cases my end
will initiate the SOAP communication (to begin an order, validate
data, etc.), while in other cases the outside company will initiate
the communication (provide status updates on an existing order). I.e.,
the adapters will contain SOAP clients and servers.

So, yes to bi-directional communication and delays. I'll have a look
at Delayed Job.

Going with something Rack-based may be all I need at this point.

A somewhat related question, what's the Ruby way to do communication
between two apps on the same machine. Is there something like IPC
sockets? Or should I be looking more at something like DRb
(particularly since the two apps might not always be on the same
machine). It seems that there should be something besides web apps for
that, but maybe I'm just making this harder than it needs to be.

I apologize for not really having a clear question. I do appreciate
the comments and suggestions so far and I now have some more
possibilities to explore. Thanks for the help.

Jeremy
 
R

Richard Conroy

[Note: parts of this message were removed to make it a legal post.]

At this point, I'm basically writing adapters to SOAP services
provided by outside companies to allow ordering and other inquiries.
They're accessed via the public internet so there can sometimes be
delays in responses, but generally under 2s, I think. Eventually I'll
be using similar SOAP services from several companies. The adapters
will be used so that internal apps can access those services via a
single API. A controller will decide which external company to
interact with in a given case and the adapter will handle the details
of that particular company's SOAP implementation.

This makes things a bit more challenging. While these kinds of architectures
aren't common, you do tend to see these kind of service broker/proxy adapter
patterns in enterprise systems, to solve the patterns you are describing
(putting a common service interface on a set of 3rd party services).

Communication can be initiated from either end. In some cases my end
will initiate the SOAP communication (to begin an order, validate
data, etc.), while in other cases the outside company will initiate
the communication (provide status updates on an existing order). I.e.,
the adapters will contain SOAP clients and servers.

So, yes to bi-directional communication and delays. I'll have a look
at Delayed Job.
DelayedJob won't help here, or not in the way you might think.
Bi-directional
SOAP communications is a totally different beast to HTTP communication,
so web architectures or web middleware isn't a good fit unless you are
adapting the SOAP interfaces.

By adapting I mean that you are converting a series of SOAP interfaces into
a RESTful one.

You really need to put individual technology pieces aside, and
concentrate on what you need to achieve with your architecture. From the
details you have given me, you have actually described a complex
undertaking.

At the heart of it all, you are going to consume and republish every vendors
SOAP API as your own, standardising it on the way and hope that you
introduce
no new issues or lose fidelity in the vendors API along the way.

There's a ton of work in there, risky too, and not much smell of any
benefit.
I have heard of similar strategies where the standardising API work got
pushed
onto the vendors themselves.

Going with something Rack-based may be all I need at this point.

A somewhat related question, what's the Ruby way to do communication
between two apps on the same machine. Is there something like IPC
sockets? Or should I be looking more at something like DRb
(particularly since the two apps might not always be on the same
machine). It seems that there should be something besides web apps for
that, but maybe I'm just making this harder than it needs to be.

The web apps communicate with other web apps that coincidentally happen
to be on the same machine. This is the easiest, and best if the server is
inside your firewall and not performance critical.

There are other ways of doing distributed computing. Besides SOAP/RPC
and RESTful methods, there is also the various Message Queueing protocols,
but they might be even less of a fit that than the options discussed already
here.
 
Y

yermej

Thanks for taking the time to reply. This conversation has given me
some new ideas to check out which is probably about as much as I could
have hoped for. As you've pointed out, I have a difficult road ahead
so I'll just get back to it.

Jeremy
 
C

Charles Oliver Nutter

Well everything is Rack enabled now. Install Passenger, deploy your
Rack enabled web service proxy (simple sinatra apps are always good
for these kind of things), and put your feet up.

It can be even easier... using JRuby, gem install trinidad and run it like =
this:

trinidad --threadsafe --rackup config.ru

One process, one instance, solid runtime, and as many concurrent
requests as you can throw at it:

~/projects/jruby =E2=9E=94 cat hello_world.ru
hello_world =3D lambda do |env|
[200, {"Content-Type" =3D> "text/plain"}, ["Hello World!"]]
end

run hello_world

~/projects/jruby =E2=9E=94 trinidad --threadsafe --rackup hello_world.ru

...

~ =E2=9E=94 ab -c 100 -n 1000 http://localhost:3000/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking localhost (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Completed 600 requests
Completed 700 requests
Completed 800 requests
Completed 900 requests
Completed 1000 requests
Finished 1000 requests


Server Software: Apache-Coyote/1.1
Server Hostname: localhost
Server Port: 3000

Document Path: /
Document Length: 12 bytes

Concurrency Level: 100
Time taken for tests: 0.440 seconds
Complete requests: 1000
Failed requests: 0
Write errors: 0
Total transferred: 166720 bytes
HTML transferred: 12504 bytes
Requests per second: 2274.66 [#/sec] (mean)
Time per request: 43.963 [ms] (mean)
Time per request: 0.440 [ms] (mean, across all concurrent requests)
Transfer rate: 370.34 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 13 10.2 13 34
Processing: 12 30 10.7 29 72
Waiting: 9 24 8.2 24 67
Total: 17 43 10.2 43 73

Percentage of the requests served within a certain time (ms)
50% 43
66% 45
75% 48
80% 49
90% 54
95% 63
98% 65
99% 67
100% 73 (longest request)
Rack compatible web container + Rack app is pretty much standard
practice for deploying Ruby web based solutions nowadays. Deploying
non-Rack ruby web apps would be considered an architecture smell now.
Only exceptions in my mind would be JRuby specific deployment options
like Warbler. Glassfish and the JBoss option (sorry cant think of it now)= ,
and
some of those are Rack-enabled too.

The JBoss offering is called TorqueBox. It's a server and a lot more;
there's wrappers for web services, timed/offline jobs. Maybe a little
heavy for a one-off service, though.
Note that if this delay is significant (500ms+), you are likely to need a
background job
scheduler too, of which there are many in Ruby (Delayed Job seems to be
getting
very popular now).

If the delay is significant, you should use JRuby with a threadsafe
framework. About three hours ago I demonstrated JRuby + Trinidad +
Rails easily handling 100-way concurrency against a 0.5s-delayed
action. So easy it should be a crime.

- Charlie
 
R

Richard Conroy

[Note: parts of this message were removed to make it a legal post.]

It can be even easier... using JRuby, gem install trinidad and run it like
this:
Nice heads-up Charles. I didn't go into too much detail with JRuby options,
as I thought that the OP was installing on a Unix environment anyway.

I wasn't even aware of Trinidad before. Will have to give that a look. I am
always on the lookout for simple Windows deployment options, and the
only decent options tend to be JRuby based.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,223
Members
46,815
Latest member
treekmostly22

Latest Threads

Top