xio2
High performance I/O for the JVM
Overview
- Server Start
- Acceptor receives connection
- Channel Context parses request
- Service issues response
Server
One Server to rule them all, One Server to find them,
One Server to bring them all and in the darkness bind them
- Composes the routes map, server socket channel, acceptor, and event loop pool.
- Provides a method to add routes to the map.
- Configures event loop pool to use all of the available cores and starts it.
- Runs acceptor event loop until it terminates (effectively forever).
Acceptor
- Abstracts away the implementation details of java nio accept events.
- Implements an event loop that just deals with accept events.
- Accepts incoming connections, builds a channel context, attaches the context to an event loop from the pool.
ChannelContext
- Composes socket reading with http parsing to build an http request object.
- Maintains a simple state machine that represents the status of the request/response cycle.
- Finds a httpHandler in the routes map and dispatches it to handle the request, otherwise issues a 404
- Provides an abstraction to write http responses to the wire.
Route
- Implements a sinatra style url matcher using regex.
EventLoop
- Abstracts away the implementation details of java nio read/write events.
- Runs inside of it's own thread.
- Composes channel contexts with nio events to cause i/o.
EventLoopPool
- Convenience class to work with multiple event loops, starting, stopping and cycling through them.
Service
- Base class for defining how to respond to a request.
- Allows multiple Services to be chained together.
Usage
Http Server
Server s = Http.newServer();
HttpHandler awesomeHandler = new HttpHandler();
Service awesomeServ = new RateLimitServ().andThen(new BusinessLogicServ());
awesomeHandler.addRoute("/sweet", awesomeServ);
s.ssl(true);
s.serve(8443, awesomeHandler);
// and when you are all done
s.close();
Http Client
XioClient c = Http.newClient();
c.ssl(true);
c.connect("localhost:8443");
HttpRequest req = new HttpRequest.Builder()
.uri("/")
.build
HttpResponse resp = c.get(req);
System.out.println(resp.getStatus());
For dev on Mac
Increase ulimit for benchmarking and load testing
modify /Library/LaunchDaemons/limit.maxfiles.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxfiles</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxfiles</string>
<string>65536</string>
<string>65536</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>ServiceIPC</key>
<false/>
</dict>
</plist>
modify /Library/LaunchDaemons/limit.maxproc.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple/DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>limit.maxproc</string>
<key>ProgramArguments</key>
<array>
<string>launchctl</string>
<string>limit</string>
<string>maxproc</string>
<string>2048</string>
<string>2048</string>
</array>
<key>RunAtLoad</key>
<true />
<key>ServiceIPC</key>
<false />
</dict>
</plist>
Both plist files must be owned by root:wheel
and have permissions -rw-r--r--
. This permissions should be in place by default, but you can ensure that they are in place by running sudo chmod 644 <filename>
.
Now reboot your computer and run
$ ulimit -a
to verify that the limits have been changed