Re: Java NIO Strategy



mearvk wrote:
[Quote from:
http://weblogs.java.net/blog/jfarcand/archive/2006/06/tricks_and_tips.html]

At this stage, socketChannel is ready to read bytes. Hence you invoke
socketChannel.read(byteBuffer), and you find that you haven't read all
the bytes from the socket (or you are ready to handle the next
request), so you decide to register the SelectionKey back to the
Selector by doing:

selectionKey.interestOps(selectionKey.interestOps() |
SelectionKey.OP_READ);

Yet another thing I don't understand in these blogs. You should only have gotten here if you were already registered for OP_READ. Just stay that way!

Boum...the little ... is where the devil is hiding! What you are
attaching to the SelectionKey is very dangerous, because there is some
probability that your SelectionKey might never return to a
ready-operation state, leaving the SelectionKey and its evil attachment
forever inside the Selector keys set.

Rubbish. This can only happen if you don't do the idle processing I described in an earlier posting. *That's* the devil.

Does this make sense? If you explicitly tell your program to re-insert
the SelectionKey back into the key set and we know it has more data to
be read (per the premise), then unless your server is completely
pegged, wouldn't it *always* return to a ready-operation state?

Only if more data arrives.

The 'premiss' is meaningless: 'At this stage, socketChannel is ready to read bytes. Hence you invoke
socketChannel.read(byteBuffer), and you find that you haven't read all
the bytes from the socket (or you are ready to handle the next
request)'. How can you find you haven't read all the bytes from the socket, except by trying another read? And this case even if it existed is logically very different from the case where you are ready to handle the next request.

Could you do:

if((count = socketChannel.read(byteBuffer))> -1)
{
//read into temp buffer
}
else
{
//forward buffer
}

So that your time in the readFromSocketChannel method is
shorter/fairer? This assumes that the key gets placed back into the
Selector key set and eventually (depending on load)gets re-handled
promptly.

I agree and this is what I do except that I don't do all this key manipulation. Also I check for 0 and -1 separately as they are very different cases.

I register and de-register as follows (assuming this is a server):

(a) when I get an OP_ACCEPT and accept a channel I register it for OP_READ.

(b) When I've had enough read events and read enough data to constitute a complete request (an interesting problem in itself) I deregister for OP_READ and pass the request off for processing. If I get EOF instead I close the channel, physically and logically.

(c) When I get the response back from wherever it was processed I attempt a write. If this doesn't succeed completely I register OP_WRITE. Any time I get OP_WRITE and the write succeeds completely I deregister OP_WRITE and register OP_READ.

(d) I do the reads and writes in a single attempt without looping, for better fairness between channels.

(e) In the idle loop, if I find a channel that has been registered for OP_WRITE for too long I close it physically and logically and abort the transaction internally. If I find a channel that has been registered for OP_READ for a long time I might time out the connection, depending on the application. If I find a channel that hasn't been registered for *anything* for a long time, it means that some transaction is still in progress and I might want to inquire into why it is taking so long.

(f) If the transactions are such that there are done in-line rather than in separate worker threads, i.e. inline at the OP_READ site, when I get the response I don't write it straight away, I register OP_WRITE on the channel and deregister OP_READ, and let the reply be written out on the next iteration of the Selector. Again, this promotes fairness among channels.

The reason for deregistering OP_READ in each case above is that you usually can't logically handle another request from the same channel until you've completed the previous one and written the reply. So you should quench that channel. Otherwise you have to read the new data, and put it somewhere, which takes memory. Better to stop reading and, eventually, stall the sender, by closing the TCP window. It's a bit like the principle of letting passengers get off the bus before the new ones get on.

The really scary thing about these blogs is that this guy apparently works for Sun Microsystems. Here's another gem I noticed:

while (bb.hasRemaining()) {
int len = ch.write(bb);
if (len < 0)
throw new EOFException();
}

etc. I've already commented that write() never returns a negative result. What fascinates me now is the concept of throwing an EOFException when *writing*. This seems to have been lifted holus-bolus from the old NIO tutorial code, which was obviously cut-and-pasted from the read code, and which was corrected at my request last year as being meaningless.
.



Relevant Pages

  • [RFC][PATCH 6/13] Equinox SST driver:hardware registers
    ... * Each ICP provides a set of input and output registers per channel. ... * banked input register structure - two sets for each channel ... event (SSP64) */ ... complete attn */ ...
    (Linux-Kernel)
  • Re: Server-to-Server Remoting
    ... You can't send an object from server b that you got at ... unncessary as your making a client call and there is already a channel ... >My problem is that I can't figure out how to register and instantiate ... which is a subclass of BaseDataService called ...
    (microsoft.public.dotnet.framework.remoting)
  • [PATCH] IPMI: Support registering for a command per-channel
    ... This patch adds the ability to register for a command per-channel. ... specific channel instead the current behaviour of all channels. ...
    (Linux-Kernel)
  • RE: UnregisterChannel Issue in remoting
    ... then registered the remote type: ... When I tried to register the remote ... We need to unregister the remote type after the channel is ... the current problem is how to unregister a remote type. ...
    (microsoft.public.dotnet.distributed_apps)
  • Re: Channel relationship to RegisterWellKnownServiceType()
    ... A channel is a communication mechanism. ... a channel registered in order to communicate with other App domains. ... known objects are objects that are accessible in an App domain via a known ... > The first 2 lines create and "Register" a Channel. ...
    (microsoft.public.dotnet.framework.remoting)