Emmanuel Lecharny wrote:
MINA uses IoProcessor to handle incoming requests. When you instanciate
the server, more than one of these objects are created, so you can
process more than one request at the same time. Each of those
IoProcessor has its own selector, but then, you should consider that the
processing of one request is done in one single thread, ie the
IoProcessor thread. More than that, each session is associated with a
single IoProcessor object, so it can't be processed by another
IoProcessor instance.
That being said, you may need to decouple the processing from the IO
handling. For instance, assuming that you may have a lot of small and
fast requests to process, plus from time to time one big expensive and
CPU consuming request, you may want to free the IoProcessor as fast as
possible for the small requests to be processed quickly. This is what
the Executor filter is good for. When you add such a filter in the
chain, it simply enqueue all the requests and ask a thread pool to
process them, letting the IoProcessor doing its job quickly.
In any case, when the request has been processed, the response is
written into a queue by the executor thread, AFAICT. As the write is
done at the end of the processing (in your IoHanlder implementation),
it's still traversing the chain using the thread selected by the
executor filter, up to the point the request is written to the queue.
Then the execution stack is unfold. It seems a bit complex, and it might
have been a bit better to separate the incoming and outgoing chains, but
at least, it does the job.
I hope I brought some faint light about the way MINA is working from the
inside out. Just tell me if it's enough for waht you need.
Thanks !
Thanks for the detailed explanation, Emmanuel. I actually understood
most of this pretty well already, though, as I've been digging through
the code quite extensively to try to understand what was going on.
But a) if I'm reading the code correctly, it looks like what you wrote
is not exactly correct, and b) I'm not sure you understood what I was
trying to communicate in my last message - that the way MINA is coded,
having a thread pool executor for write operations is completely
useless, since the write thread pool will *never* get used to actually
perform socket IO.
Let me explain. Let's say you have a filter chain like so:
DefaultIoFilterChainBuilder filterChainBuilder =
protocolAcceptor.getFilterChain();
filterChainBuilder.addLast("codec", new
ProtocolCodecFilter(codecFactory));
threadPool = new OrderedThreadPoolExecutor();
filterChainBuilder.addLast("writer thread pool", new
ExecutorFilter(threadPool, IoEventType.WRITE));
protocolAcceptor.setHandler(protocolHandler);
So the intention here is that after the codec does its work (i.e.,
processes an incoming message and generates a response) the actual write
operation (i.e., actually physically writing the IoBuffer bytes out to
the socket) should get performed in a threadPool thread.
But that does not happen. Instead, the write operation gets performed
in the IoProcessor thread. And then, later, although the filter chain
does invoke filterWrite() and messageSent() on the ExecutorFilter, that
winds up essentially being a NO-OP, since there is nothing downstream
from the executor filter that is doing any socket IO. So the whole
concept of a writer thread pool is not possible to implement the way
MINA is coded.
I guess what I'm finding unexpected about the way MINA is coded is this:
I would expect that messages/method-calls would traverse down the filter
chain and then, finally, after the final (tail) filter in the chain is
executed, then MINA would write the output out to the socket.
But according to DefaultIoFilterChain.HeadFilter, it looks like the
*head* of the filter chain, not the tail, is what's writing the output
out to the socket:
private class HeadFilter extends IoFilterAdapter {
@SuppressWarnings("unchecked")
@Override
public void filterWrite(NextFilter nextFilter, IoSession session,
WriteRequest writeRequest) throws Exception {
...
s.getWriteRequestQueue().offer(s, writeRequest);
I don't quite understand this. And as a matter of fact, it seems like
this could cause some VERY unexpected behavior.
For example, say I wanted to add a
org.apache.mina.filter.util.WriteRequestFilter at the end of my filter
chain which alters the outgoing message before it gets sent. But this
would not work! Since the message gets queued for send at the *head* of
the filter chain, any changes I make to the message at the tail of the
chain would have no effect.
In any case, the bottom line is: it looks like my efforts to try to get
MINA to send messages out through the socket in a different thread
cannot be successful. No matter how I configure things, MINA will
always do the socket IO in the IoAcceptor thread.
It's easy to verify this, by the way: Try adding a "write thread pool"
to your filter chain like I described above. Then, in the
messageReceived() method of your protocol handler, where you write your
response, include the following code:
WriteFuture writeFuture = session.write(responseStr);
writeFuture.addListener(
new IoFutureListener<WriteFuture>() {
public void operationComplete(WriteFuture
future) {
System.out.println("message write occurred in thread:
"+Thread.currentThread().getName());
}
}
);
On my machine, the output always occurs in thread "NioProcessor-1",
instead of one of the executor filter threads.
I hope I was clearer this time and that this makes some sense to you.
Can you shed any light on what's happening here and/or why the write
request is getting queued in the head filter?
Thanks,
DR