views:

478

answers:

5

I'm writing a daemon in Java that takes in requests from multiple providers in which the provider would return a service object for the daemon to run as a thread.

Application architecture:

  • Main class
  • Providers
  • Services
  • Libraries / Models
  • List item

Main class Basically loads the configuration file, initializes libraries required, etc. Once the daemon is initialized, it loops through all the providers and initializes them. After that, the daemon goes into an endless loop constantly going through each provider evenly distributed and tries to fetch a new service.

Providers A provider is basically an adapter to a 3rd party service. This could be e-mail, fax, a job queue, database, etc. When a provider is initialized it connects to the 3rd party service. Whenever the application fetches a new service object from the provider, it checks the 3rd party service, if it finds a new request, it parses it and then returns a new service object to the application.

Services A service is basically a specialized job. When a provider gets a new request, it returns a specific service type related to the request. When the application runs a new service thread, it will handle the request and then close.

Here is an example, although it probably won't ever be used like this. A new provider is created that listens on an HTTP port. A new HTTP request comes in asking to process a string (reverse it) and store it in the database reversed. A new request comes in, the provider returns the service to the application, the application launches a new service in which is reverses the string and updates the database. When it's updated, the application sees that the job is done and is able to fetch a new one from another provider.

Question: Are there any design patterns like MVC that would handle an architecture like this? I realize that MVC is a very bad term to use, as it contains multiple patterns. I guess what I'm asking is, the approach I'm going for okay? Would it be a good idea to take the concepts behind MVC and move it to this application? Providers being controllers, services being actions tied to models? Although there is never any views in this type of application.

Edit:

  • The daemon handles requests asynchronously to the requester. The requester never receives a direct response.
  • The application is distributed, so you can run this daemon on as many servers as you want. Whenever a provider retrieves a new request it locks the request so the other daemons do not duplicate the request. Jeff Storey mentioned JMS which could be useful for this. I've also thought about integrating Memcache to handle locking on top of how the providers handle it.

Thanks

+2  A: 

Web servers (and not only the HTTP kind) use the Request/Response pattern. A socket listener waits for incoming connections, uses a request factory to create a Request object and then passes this to a handler which returns a response object (or the factory also creates a response object and the handler just modifies it).

In the HTTP world, the handler is identified by the URL and the URL paramaters or the POST data becomes Request data. The response (usually some HTML page) is encoded from the response object and sent back to the browser.

Aaron Digulla
So the concept I'm going for in the architecture (although the names provider, service, aren't great) is what I should be going for? The thing is this one daemon can have multiple handlers. The request contains the handler identifier along with other data. There will never be a response to the caller as it needs to happen asynchronously. Thanks for the answer.
William
Yep. If you haven't already, then look at the Execution framework in Java 5 which will allow you to push requests into a job queue and run many handlers in threads.
Aaron Digulla
@Aaron If you're talking about Java's Executors, I'm using it right now to handle managing the different threads (jobs). The "job" being the service object which is a Runnable. It looks like everything is somewhat the way it's supposed to be. It looks like I just need to figure out how to separate this system better. Thanks a lot Aaron, I'm going to set this as the accepted answer as you confirmed what I was thinking.
William
+3  A: 

Depending on how large you need to scale, you might want to consider a messaging service like JMS. Your producers and consumers (i.e. providers and responders) could publish to and listen to the appropriate JMS topics.

Jeff Storey
In terms of scaling, this system is highly distributed. You can launch this daemon on as many servers as you want. Each provider is setup to lock a request so no other daemons grab it. All providers will only be using services that have that ability. Although there might be cases where this isn't possible. I'll look into JMS, thanks.
William
Yes, look to the MQ design.
Xepoch
@Xepoch thanks. We're using Amazon SQS right now and it's working out fine. The problem is some providers (email for instance) won't be able to be added to the queue, the whole point of this daemon was the ability to access multiple 3rd party services, only handle the requests once, and have it highly distributed. Not sure JMS will help any of this. Although it is useful and I might use it for other uses.
William
+1  A: 

Based on my understanding of the requirements you described, I do have a couple of suggestions.

Disclaimer: I don't really know about your scaling requirements or your resource constraints, and if you're running a daemon these may be important. As always, take the forthcoming suggestions with a grain of salt.

My initial reaction is that there's probably no single design pattern that will solve your entire problem, but there are some design patterns that might help you with subsections of it.

1) It sounds like your Main class is going to be polling the providers. You may want to do something more message driven. Have a look at the Observer pattern and the related publish/subscribe pattern if/when you need to scale up.

2) You may want to think about pre-allocated thread pools for your providers. It may not be strictly necessary, but it might make sense for your particular situation.

3) Based on your description, you may be heading into architecture astronaut territory (i.e. generalizing everything before you need to). Unless you're going to be distributing an API or have a lot of people working on/re-using this code, I'd caution against this type of behavior: it's a big time sink and it's frustrating to aim so high and then only implement a tiny fraction of the initial ambition.

Some of this advice may not apply directly to your problem, but I hope it helps.

Seth
I'm using pre-allocated threds already based off the total about of CPU's on the server. I'm using something very similar to the observer pattern for my providers, but I should probably just switch completely to that pattern it more organized. The daemon was fairly easy to build and didn't take that much time thanks to Java's libraries. My intent isn't to prematurely scale this system. Thanks for the advice. :) +1
William
+1  A: 

What you're describing sounds a lot like the the kind of thing that enterprise integration patterns solve.

Lightweight Java libraries that support these patterns are things like Apache Camel or Spring Integration or you could use a full Enterprise Service Bus like ServiceMix or Mule.

Here's a rough translation of your terms:

Main Class - In a full ESB this would be the ESB itself, i.e. the application server. You could write this yourself or you could use an existing ESB or general purpose application server like OSGI.

Providers - Channel Adapters.

Services - These will be various components. Some may be EIP components like Message Transformers as well as Service Activators or other Channel Adapters.

jamie mccrindle
I'm already using a messaging system (Amazon SQS), the problem is there is one main provider that can't use the queue which causes a problem. It looks like the Channel Adapter fits what I'm doing with providers very well. The process manager sounds about right, although I'm using it in the main class. The services is just a type of runnable to be executed by the process manager. Thanks for the help +1
William
Arg so many good answers, it's too bad you can't accept multiple answers. I think this actually fits my question more specifically as it goes into enterprise type patterns use for what I want, although the patterns suggested by services don't seem like what I need. Thanks
William
No problem. If anything the Enterprise Integration Patterns is useful for establishing a common language. It can quickly degenerate into architecture astronautism, though. The lightweight integration libraries do quite well at distilling it out in a pragmatic way.
jamie mccrindle
Also, if you don't want to deal with JMS, Spring has the Spring Integration project which provides many of these concepts.
Jeff Storey
I edited the bit about services because it wasn't quite right. See Service Activators in Spring Integration or Bean Endpoints in Camel to get a better idea. Both require a registry of "services", usually just the Spring ApplicationContext.
jamie mccrindle
Service activator actually sounds about right. Thanks a lot Jamie. Maybe I should look into Spring a bit more, it sounds like a great framework.
William
+1  A: 

A non-Java specific (by which I mean a generic approach, not one that doesn't apply when using Java) is to use a simple Request/Response approach as noted by Aaron Digulla. This is a very common scenario but easy to over-engineer (as really, it's quite straightforward and simple to write - especially when using appropriate supporting libraries).

If your app for web service to add/remove entires from a database is more than a 200-300 lines of code then it's probably over-engineered (even with reasonable error handling and dynamic configuration).

If your database supports locking and enforcing uniqueness (and transactions, if you require that sort of functionality), I'd advise you don't overcomplicate things by writing a locking demon - just let the database take care of it and handle the exceptions.

If you really do need to do locking (e.g. because you are writing to some external service like a bit of hardware) then you can use a typical UNIX approach to locking resources and write a single-instanced daemon that runs on a local socket which all your web services can talk to - depending on if it indicates a resources is busy they could either reject requests or queue them (e.g. in memory in a queueing system, a table in an SQL DB, etc.) with a a daemon which process queued requests (which also talks to the locking service).

To keep things really simple you can always just always push requests to the queueing system from the web service, I would recommend that unless you need to distinguish between "this action is now being performed" and "this action is queued" in your responses to the client submitting the request.

For the interface itself, personally I favour something that is and widely endorsed as an open standard, easy to implement and highly interpretable, like Document/Literal SOAP service, in preference to something like RPC/encoded (deprecated & evil) or JMS (which is proprietary, though in practice supported widely to varying degrees).

If you are writing an exclusively internal service and you an exclusively a Java shop then JMS is highly suitable but if it's not an internal application it's best to play nicely and not assume everyone else will want to have to use a JMS client just to talk to your service (I evangelise Doc/Lit SOAP because it can be parsed by anything than can handle even basic XML and is WS-I endorsed).

Iain Collins
Thanks for your answer. This system isn't very overly complicated, the reason I'm posting on here in the first place is that I felt I might have not followed design patterns when making the system because of how simple I wrote it. There is already a queue system in place, connecting directly to the daemon will never happen. Providers connect to 3rd party services to REQUEST new jobs, they don't receive them directly. The single instance daemon might be what I move to later on to handle jobs, but as of right now my current architecture is fine.
William