Announcement Announcement Module
Collapse
No announcement yet.
Unicast channel that dispatches concurrently Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Unicast channel that dispatches concurrently

    Hi,

    I'm testing the MarshallingWebServiceInboundGateway and I'd like to have one taskExecutor that handles the http requests (I'm using the JDK6 HttpServer) and puts the unmarshalled objects on a channel and a different taskExecutor that processes the messages on the other end. But I don't want this to be a polling channel and a polling service activator, I'd like the messages to be directly submitted to the second taskExecutor for processing, and I'd then tune the TE's queue to be the size I want instead of having a queueChannel that is polled. I don't imagine there's an existing way for doing that?

    I figured you'd need a different type of "concurrent" channel (one that runs the dispatching to the endpoint in a TE). Looking at the current 1.0.2 implementation this would seem pretty easy to implement, since the AbstractDispatcher already has a TE, you would only need to modify the AbstractUnicastDispatcher to dispatch the messages concurrently if the TE reference has been set. Then you'd need a new channel class in the style of:

    Code:
    public class ConcurrentChannel extends AbstractSubscribableChannel<AbstractUnicastDispatcher> implements BeanFactoryAware
    {
        private volatile TaskExecutor taskExecutor;
    
        public ConcurrentChannel(){
            this(new RoundRobinDispatcher());
        }
    
        // Other constructors and setters...
    
        @Override
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException{
            if (this.taskExecutor != null) {
                this.getDispatcher().setTaskExecutor(this.taskExecutor);
            }
        }
    }
    And the XML could look like this:

    Code:
    <channel id="concurrentChannel">
    	<concurrent taskExecutor="taskExecutorRef"/>
    </channel>
    This type of channel would also be useful in the case you want to split a message and handle all split messages concurrently. I guess the only alternative to this would be to do the concurrent part in the message handler itself, but then you need all that boilerplate code...

  • #2
    Basically, if we simply add support for task-executor on any channel that does not have a queue, then a DirectChannel would be capable of delegating to the TaskExecutor just like a PublishSubscribe channel. In other words, both of these would be possible (whereas only the first is currently an option):
    Code:
    <publish-subscribe-channel id="pubsub" task-executor="pool1"/>
    
    <channel id="direct" task-executor="pool2"/>
    As you pointed out, the underlying AbstractDispatcher already supports a TaskExecutor, so it would not be difficult to extend that option to DirectChannel.

    I want to make sure I understand your splitter example. You are saying that the "output-channel" of a splitter would have the task-executor reference?

    Comment


    • #3
      Yes, the "output-channel" of the splitter would have the task-executor reference.

      I agree that the best solution would be to allow the task-executor for all channels, and thus a DirectChannel would work. The only problem I guess is that the documentation specifically defines the DirectChannel as one in which both sides are handled by the same thread, which is crucial in the transaction scenario described in the documentation. This would of course still hold if no task-executor is defined for the DirectChannel. I guess it's just a matter of clearly documenting the difference from a transactional point-of-view.

      Comment


      • #4
        Exactly. The transactional case is very important. With a task-executor the calling thread's transaction would only rollback in case the TaskExecutor itself throws an Exception (rejects the task), but of course anything that happens on the other side of the channel would not affect the caller's transaction.

        This then begs the question if we need to support transaction demarcation on the channel when a task-executor is present. In other words, the task that is executed (essentially the 'send()' call itself) would be transactional.

        Thoughts?

        Comment


        • #5
          Hmm, interesting question... I suppose you could benefit from transactional support in a scenario where you would want to
          1. Start a transaction
          2. Split the message into parts for concurrent handling
          3a. Gather the results and commit if all operations were successful
          3b. Rollback if one of the operations threw an exception

          I must admit I'm not that familiar with how threads and transaction contexts are mapped to each other, so I don't know if this is even feasible... Shoot a link if you know of any page that explains this thoroughly...

          In my WebService scenario I want to have two task-executors, one that is potentially quite large and handles CPU intensive tasks like marshalling/unmarshalling and that is sized according to the hardware characteristics of the machine it's running on (potentially a large number of CPUs) and another task-executor on the receive side of the channel that handles IO to a database and that is sized according to the database connection pool size (< than the number of CPUs). In this scenario the transactions would be started on the receive side of the channel in the message handler.

          Comment


          • #6
            Now that I think about it it's very difficult to see a scenario where you'd have a bunch of unrelated database updates that you could run in any order concurrently and that would still need to be in the same transaction. But even if you did, I don't think the JTS/JTA would support this, especially using separate connections? In any case and to answer your question, you probably don't need to support transaction on channels that have a task-executor past the send()-method. This is pretty much how JMS works too isn't it? The transaction on the JMS queue is successful if the message is successfully sent to the queue, not if it's ever read by anybody?

            Comment


            • #7
              Yes, that is how JMS transactions work. The difference of course is that the JMS Destination can be persistent, so it makes sense for that to be the transactional boundary even if something goes wrong on the receiving side.

              However, since we provide JMS Channel Adapters (and will be adding a more convenient JMS "channel": http://jira.springframework.org/browse/INT-592), I think it's ok to avoid transaction-managers on the channel itself.

              The main reason is - as you implied - the scope of the transaction would most likely be too large. It is much better to have transactions at the handler level (or the method invoked within a Service Activator). We just want to consider that gap between send and receive (hence the JMS channel I pointed to above).

              There may be cases where multiple Messages need to be handled as part of a composite group, but we will be addressing that at the "process" level in 2.0. In many of those cases, Spring Batch actually provides the best model.

              Thanks for all the input!

              Comment


              • #8
                I've been thinking a little bit more about this...

                I'm wondering - can you explain what advantages you see with a TaskExecutor on a DirectChannel as opposed to a poller configuration for a QueueChannel (where the poller has a TaskExecutor reference, a configurable timeout, 'maxMessagesPerPoll', etc.) ?

                Thanks.

                Comment


                • #9
                  Well, with the polling solution I have many more options to fine tune: I now have to care about the size of the channel's queue, how that relates to the max-messages-per-poll (which it probably does), what's the optimal interval for polling, etc... If I have multiple gateways and service activators I have to configure and tune one channel queue and poller for each instead of having that one TE and it's one queue. This might also raise a fairness issue: Instead of individual messages going to the TE's FIFO queue in the order they were sent to the different channels I'll have messages going in sets not based on the order they were sent but the order the different pollers happened to empty the channel queues. So I would think that the DirectChannel with the TE provides a solution that is simpler to configure, more efficient and probably more fair.

                  Thanks.
                  Last edited by fdurden; Mar 19th, 2009, 06:21 AM.

                  Comment


                  • #10
                    Hi,

                    So are there any chances this will be done? Should I file a JIRA issue about it? I didn't see anything in the 1.0.2.RELEASE about it.

                    Thanks!

                    Comment


                    • #11
                      Please do raise an issue. We decided that we needed to put more thought into this, and therefore did not try to quickly add it in 1.0.2.

                      Comment


                      • #12
                        I'm assigning this to 1.0.3 now. We'll most likely be adding some comments to the issue, please feel free to provide feedback there.

                        Comment


                        • #13
                          If interested in this issue, please view the JIRA issue and click on the FishEye tab for some detailed views of the recent commits.

                          The channel implementation has been committed (but might be renamed), and the namespace support will be added tomorrow.

                          -Mark

                          Comment

                          Working...
                          X