ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Building Highly Scalable Servers with Java NIO
Pages: 1, 2, 3

After being registered, handlers can activate or deactivate interest in specific I/O events by updating their interest sets. Internally, the SelectorThread class updates the interest set of the corresponding SelectionKey. There is no support for de-registering a channel, since this can be easily accomplished by closing the socket.



Here is what the SelectorThread class looks like:

public class SelectorThread implements Runnable {
  
  /** 
   * Graceful shutdown. 
   */
  public void requestClose() {
    ...
  }
  
  /**
   * Adds a new interest to the list of events 
   * where a channel is registered. 
   */
  public void addChannelInterestNow(
      SelectableChannel channel,
      int interest) throws IOException {
    ...
  }

  /**
   * Like addChannelInterestNow(), but executed 
   * asynchronously on the selector thread. 
   */
  public void addChannelInterestLater(
      SelectableChannel channel, 
      int interest,
      CallbackErrorHandler errorHandler) {
    ...    
  }
  
  /**
   * Removes an interest from the list of events 
   * where a channel is registered. 
   */
  public void removeChannelInterestNow(
      SelectableChannel channel, 
      int interest) throws IOException {
      ...
  }
  
  /**
   * Like removeChannelInterestNow(), but executed 
   * asynchronously on the selector thread. 
   */
  public void removeChannelInterestLater(
      SelectableChannel channel, 
      int interest,
      CallbackErrorHandler errorHandler)  {
    ...
  }

  /**
   * Like registerChannelLater(), but executed 
   * asynchronously on the selector thread. 
   */
  public void registerChannelLater(
      SelectableChannel channel, 
      int selectionKeys, 
      SelectorHandler handlerInfo,
      CallbackErrorHandler errorHandler) {
    ...
  }  

  /**
   * Registers a SelectableChannel with this 
   * selector. 
   */
  public void registerChannelNow(
      SelectableChannel channel, 
      int selectionKeys, 
      SelectorHandler handlerInfo) 
      throws IOException {
    ...
  }  
  
  /**
   * Executes the given task in the selector 
   * thread. Does not wait for its execution.
   */
  public void invokeLater(Runnable run) {
    ...
  }
  
  /**
   * Executes the given task synchronously in the
   * selector thread, waiting for its execution.
   */
  public void invokeAndWait(final Runnable task) 
    throws InterruptedException  
  {
    ...
  }  

  /**
   * Main cycle. This is where event processing 
   * and dispatching happens.
   */
  public void run() {
    ...
  }
}

The purpose of the invoke*() methods and of the two variants (*Now() and *Later()) for most of the public methods will be explained shortly.

Threading in a Multiplexed World

In theory, with I/O multiplexing it is possible to have a single thread do all of the work in a server application. In practice, that is a very bad idea. When using a single thread, it is not possible to hide the latency of disk I/O (Java NIO does not support non-blocking file operations) or to take advantage of systems with multiple CPUs. As a rough guideline, a server application should have at least 2*n threads, with n being the number of execution units available. Therefore, we had to implement a way of dividing the work among threads.

Getting the threading model right was the hardest part of the development. We considered the following architectures:

  • M dispatchers/no workers
    Several event-dispatch threads doing all the work (dispatching events, reading, processing requests, and writing).

  • 1 dispatcher/N workers
    A single event-dispatch thread and multiple worker threads.

  • M dispatchers/N workers
    Multiple event dispatch threads and multiple worker threads.

In all cases, incoming connections are assigned to an event-dispatch thread (a SelectorThread) for the duration of their lives. In the first architecture, I/O events are fully processed by the event-dispatch thread. In the other two, the processing is delegated to worker threads.

Pages: 1, 2, 3

Next Pagearrow