Skip to content

m4dc4p/numbers

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Numbers
=======

A server for reading 9-digit numbers from network clients. Makes the following assumptions:

  * Valid input is a 9 digits followed by a newline (as defined by
    `System.lineSeperator`). Invalid input causes the client connection to be
    dropped immediately.

  * If any client writes `terminate` on an empty line, the server quits
    immediately.

Assumptions made:

  * Digits are encoded using ASCII/UTF-8.
  * Clients will generally be well-behaved and long-lived. 
  * The input given will span the entire range of 1B numbers.

Design 
======

I designed my solution around two blocking queues. Three "worker" classes use
those queues to process input, deduplicate numbers given, and write them to the
output log.

The first queue (named `toDeDupe` and allocated in `main`) holds arrays of
bytes read off the network. A single worker reads from this queue, converts the
bytes to Integers and deduplicates the numbers found. The second queue (named
`numbers` and also allocated in `main`) takes the unique numbers found by the
deduper. Another worker reads from `numbers` and writes those values to the
output log.

The first worker, defined in the `Reader` class, is the only worker
instantiated multiple times. It reads input from the network, parses it into
valid sequences of ASCII digits, and puts those bytes into the `toDeDupe`
queue. If any client gives bad input or disconnects, the worker closes the
connection and stops processing. If any client sends the `terminate` command,
then the worker throws an exception which will ultimately result in the server
process exiting.

The second worker, defined in the `DeDuper` class, reads input from its queue,
converts it to an Integer object and checks if the number has been seen
before. `DeDuper` checks for duplicates using a BitSet (named `seen`) with 1B
bits (the maximum value of possible 9-digit numbers). Only one instance of
`DeDupder` gets instantiated, so the memory consumed by `DeDuper` stays
constant, and no locking is required to check and update the bit set. (I tried
solutions using various flavors of HashMaps and Sets, but they tended to
consumer more and more memory over time.)  `DeDuper` then writes any unique
values to an output queue.

The third worker, defined in the `Drainer` class, reads input from the
`numbers` queue (which is filled by `DeDeuper`). `Drainer` writes each Integer
value to the output stream using the specified 9-digit, 0-padded format. Only
one instance of `Drainer` gets created.

The Server class defines the network server which listens for client
connections. For each client that connects, the server creates a `Reader`
instance. However, the server maintains a list of connected clients and will
only allow 5 connections. Every 100ms, the server checks the known list of
clients to see if any have disconnected or signaled termination (the
`pruneClients` method implements this process). If any client signals
termination, then the server throws an exception that eventually kills the
process. If a termination signal is not found, then the server removes any
finished clients from its list of clients, which will allow any new clients to
connect (up to the limit of 5).

Files
=====

  * src/main/java/.../service/Main.java -- Contains the Server, DeDuper
    and Drainer classes.
  
  * src/main/java/.../service/Reader.java -- This class reads input from the
    network and parses it into numbers (if the input is valid).  

  * src/main/java/.../client/Main.java -- Various clients for testing.
  
Building
========

Use maven to build and test the project (Java 7 is required):

  mvn install

Note that commands below are given relative to the root of the source
directory, assuming the jar resides in the `target` directory.

Running the Server
==================

`mvn install` produces an executable jar. To run the server:

  java -jar target/numbers-1.0-SNAPSHOT-shaded.jar

While the server runs, it produces three output files:

  * numbers.log -- Contains the list of unique numbers received so far.

  * timers.log -- A running report on various timers inserted in the code.

  * counters.log -- A running report showing various counters. They include:

    * Received -- Count of numbers received (including
      duplicates)

    * Duplicate -- The count of duplicate numbers seen.

    * Unique -- Count of unique numbers seen.

Clients
=======

Several types of clients can also be run:

  * "steady" client - Sends random numbers at a pre-defined rate (specified in
    units of 1,000 requests / sec).
  * "bad" client - Sends bad input some percentage of the time. Immediately
    reconnects when disconnected from the server.
  * "repeat" client - Sends the same number (randomly chosen) over and over.
  * default client - Sends random numbers as fast as possible.

To run the client program, put the `numbers` jar on the class path and execute
`com.codeslower.numbers.client.Main`:

  java -cp target/numbers-1.0-SNAPSHOT-shaded.jar com.codeslower.numbers.client.Main 

Passing `--help` will give usage information. The server must be running on the
localhost (at port 4000) for clients to connect.

   

About

A simple server to demonstrate use of Java NIO 2.0 networking and concurrency.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages