JavaOne Notes: Grizzly HTTP Listener via NIO
Intro to NIO
� Selector � basic abstraction to enable multiplexed I/O
o Register one or more �selectable� channels
o Relationship between channel and selector represented by a selection key
o Selection key remembers the events you are interested in
o Selector�s select() method updates the keys which are �ready�
o Service each channel by iterating over they keys that are �ready�
Block/Non-Blocking
� Traditional java.net.Sockets are blocking
� Channels can be blocking or non-blocking
o Non-blocking channels
Never puts the invoking thread to sleep
� Operation completes right away and returns a result indicating what (if anything) was done
� Non-blocking makes it esy to manage many channels simultaneously
� But, you have to tell the channel to be blocking or non-blocking
� Additional book keeping required, you may get partial results back
Buffer and ByteBuffer
� Containers for handling data
� Worker very well together with Channels
o High performing if done right!
� To use Buffers you must understand
o Capacity � max num of elements
o Limit � count of �live� elements, don�t read/write beyond
o Position � index of next element to read./write
o Mark � a remembered position
o 0 <= mark <= position <= limit <= capacity
� ByteBuffers � two flavors
o HeapByteBuffer
� Underlying storage maintained in a Java language byte[]
o DirectByteBuffer
� Underlying storage maintained in native code, not in the Java language heap
�
� Tradeoffs with both of these approaches
� What�s true and what�s not true?
o Using NIO SocketChannels and ByteBuffers is easy == true
o Building a high performing and highly scalable app with NIO is easy == false
o Non-blocking is fort server-side apps only == false
Grizzly is part of App Server 9.0 (Glass fish); Open Sourced
Grizzly integrates w/ current Apache Tomcat HTTP Connector architecture (Coyote)
Problems w/ NIO Non-Blocking
� No guarantee that data is processed
count = socketChannel.read(byteBuffer)
� May not read the entire stream contents, requiring an extra read
o Occurs frequently when reading HTTP requests
� Same issue exists with writing data
A Task-based architecture has been used, each task representing an operation when manipulating the byte steam
� AcceptTask for managing OP_ACCEPT
� ReadTask for managing OP_READ
� ProcessorTask for parsing
Tasks can execute on their own thread, on a shared thread or using the Selector thread
� Better off reading and processing in same thread
� Single thread pool for read/process
� Thread doing accepts, pool doing reads
Non-Blocking NIO is very similar to libc select()/poll()
The Grizzle GW supports 2 strategies:
� Mode Blocking:
� Mode Non-Blocking: w/ algorithm to predict byte stream completion
� These are the modes that have produced best perfomance
Lessons Learned implementing Grizzly
� Buffer management is crucial
� DirectByteBuffer (DBB) is a big win
� DBB expensive to allocate
� Bext Practices
o Use DBB w/ SocketChannels to avoid unnecessary copying of date
o Avoid copy data out of DBB
o Create view buffers to minimize DBB allocation costs
� What�s a view buffer?
o A buffer that manages data in another buffer
� Why useful?
o Allocate one very large DBB and create smaller �views� or view buffers
o Reduce number of costly DBB allocactions
o Can re-use views
Gotchas
� Register �key� in same thread doing the select
o Unpredictable behavior in 1.4.x JVMs
� Enable/disable a key�s operation of interest in same thread as select
� Selector.select() and Selector.wakeup() are expensive
� Buffers � too many leads to GC issues
� Keep-Alive connections are well suited to using NIO (connection create/teardowns aren�t any more efficient under NIO)