System Programming Practical session 12 Reactor
Thread-Per-Client downsides Each thread waste resources. Blocking I/O. Vulnerability to denial of service attack. The Reactor design pattern solve these problems. One thread deals with communication. Fixed number of threads deal with work. communication and work layers are separate and asynchronous. Non-blocking I/O.
Non-Blocking I/O Server ConnectionAcceptor ServerSocketChannel key Selector ConnectionAcceptor ServerSocketChannel SocketChannel ConnectionHandler SocketChannel SocketChannel ProtocolTask SocketChannel
Channels Channels wrap sockets, and allow non-blocking I/O. read(), write() , accept() can be non blocking. Setting up a non-blocking ServerSocketChannel listening on a specific port. int port = 9999; ServerSocketChannel ssChannel = serverSocketChannel.open(); ssChannel.configureBlocking(false); ssChannel.socket().bind(new InetSocketAddress(port));
Buffers ByteBuffer are buffers that hold bytes. Channels know how to read and write to buffers. Creating a Buffer final int NUM_OF_BYTES = 1024; ByteBuffer buf = ByteBuffer.allocate(NUM_OF_BYTES); From Channel to Buffer and back numBytesRead = _socketChannel.read(buf1); numBytesWritten = _socketChannel.write(buf2); Return –1 if channel is closed. Update position marker of the buffer.
Selector The selector monitors the channels for new events (new data arrived, new connection). A Selector is registered to each channel with an attachment to handle the event. An appropriate attachment is invoked for each new event. Selector selector = Selector.open(); Object anAttachment = new Object(); socketChannel.register(selector, SelectionKey.OP_READ, anAttachmemt);
select() Method selector.select(); Blocks until at least one of the channels is ready for the registered event. A list of SelectionKeys is returned. Each Selectionkey is associated with one event, and holds the attachment registered with the event.
Reactor Actors Reactor – The main class. Creates ServerSocket channel Registers the Selector For each event, invokes the appropriate attachment ConnectionAcceptor ConnectionHandler
Reactor Actors ConnectionAcceptor accept() Creates SocketChanel. Register the Selector. Creates ConnectionHandler.
Reactor Actors ConnectionHandler Read() Reads new data from channel. Adds ProtoclTask for yet unprocessed input data to a fixed thread pool. Write() Receives output data from ProtocolTask. Writes the data to the channel.
Reactor Actors ProtocolTask Passes unprocessed input data to message tokenizer. Processes each complete message. Passes output data to ConnectionHandler.
Execution example ssChannel selector executor public class Reactor{ ... selector ssChannel ServerSocketChannel ssChannel = ServerSocketChannel.open(); ssChannel.configureBlocking( false); ssChannel.socket().bind(new InetSocketAddress(port)); Selector selector = Selector.open(); ssChannel.register(selector, SelectionKey.OP_ACCEPT, connectionAcceptor); ExecutorService executor = Executors.newFixedThreadPool( _poolSize); executor
while (_shouldRun && selector.isOpen()) { try { selector.select(); } catch (IOException e) {…} Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey selKey = (SelectionKey) it.next(); it.remove(); if (selKey.isValid() && selKey.isAcceptable()) { ConnectionAcceptor acceptor = (ConnectionAcceptor) selKey.attachment(); acceptor.accept(); } catch (IOException e) {…) if (selKey.isValid() && selKey.isReadable()) { //Handle reading… } if (selKey.isValid() && selKey.isWritable()) { //Handle writing… } }
ssChannel selector sChannel sChannel sChannel public class ConnectionAcceptor { … public void accept() { SocketChannel sChannel = _ssChannel.accept(); if (sChannel != null) { sChannel.configureBlocking(false); SelectionKey key =sChannel.register( _data.getSelector(), 0); ConnectionHandler handler = ConnectionHandler.create(sChannel, _data, key); handler.switchToReadOnlyMode();} } Client connection request selector ssChannel sChannel sChannel sChannel
selector ssChannel “Don’t worry” sChannel sChannel sChannel
while (_shouldRun && selector.isOpen()) { try { selector.select(); } catch (IOException e) {…} Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey selKey = (SelectionKey) it.next(); it.remove(); if (selKey.isValid() && selKey.isAcceptable()) { … } if (selKey.isValid() && selKey.isReadable()) { ConnectionHandler handler = (ConnectionHandler) selKey.attachment(); handler.read(); } if (selKey.isValid() && selKey.isWritable()) { //Handle writing… }
ssChannel selector “Don’t worry” sChannel sChannel sChannel public class ConnectionHandler {… public void read() { ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); int numBytesRead = 0; try { numBytesRead =sChannel.read(buf); } catch (IOException e) { numBytesRead = -1; } if (numBytesRead == -1) { closeConnection(); _protocol.connectionTerminated(); return; } buf.flip(); _task.addBytes(buf); _data.getExecutor().execute(_task); selector ssChannel “Don’t worry” sChannel sChannel sChannel
_buffers public class ProtocolTask implements Runnable { private final Vector<ByteBuffer> _buffers = new Vector<ByteBuffer>(); … public synchronized void run() { synchronized (_buffers) { while(_buffers.size() > 0) { ByteBuffer buf = _buffers.remove(0); this._tokenizer.addBytes(buf); } } while (_tokenizer.hasMessage()) { public void addBytes(ByteBuffer b) { _buffers.add(b); _buffers
_tokenizer “Don’t worry” _buffers D o n ’ t w o r r y public class ProtocolTask implements Runnable { private final Vector<ByteBuffer> _buffers = new Vector<ByteBuffer>(); … public synchronized void run() { synchronized (_buffers) { while(_buffers.size() > 0) { ByteBuffer buf = _buffers.remove(0); this._tokenizer.addBytes(buf); } } while (_tokenizer.hasMessage()) { public void addBytes(ByteBuffer b) { _buffers.add(b); _tokenizer “Don’t worry” _buffers D o n ’ t w o r r y
ssChannel selector “be happy\n” sChannel sChannel sChannel public class ConnectionHandler {… public void read() { ByteBuffer buf = ByteBuffer.allocate(BUFFER_SIZE); int numBytesRead = 0; try { numBytesRead =sChannel.read(buf); } catch (IOException e) { numBytesRead = -1; } if (numBytesRead == -1) { closeConnection(); _protocol.connectionTerminated(); return; } buf.flip(); _task.addBytes(buf); _data.getExecutor().execute(_task); selector ssChannel “be happy\n” sChannel sChannel sChannel
“Don’t worry be happy\n” public class ProtocolTask implements Runnable {… … public synchronized void run() { synchronized (_buffers) { while(_buffers.size() > 0) { ByteBuffer buf = _buffers.remove(0); this._tokenizer.addBytes(buf); } } while (_tokenizer.hasMessage()) { String msg = _tokenizer.nextMessage(); String response = this._protocol.processMessage(msg); if (response != null) { try { ByteBuffer bytes = _tokenizer.getBytesForMessage(response); this._handler.addOutData(bytes); } catch (CharacterCodingException e) { … } } } } public void addBytes(ByteBuffer b) { synchronized (_buffers) { _buffers.add(b); } } _tokenizer “Don’t worry be happy\n” response “Your message “Don’t worry be happy” received”
public class ConnectionHandler{ … public synchronized void addOutData(ByteBuffer buf) { _outData.add(buf); switchToReadWriteMode(); } public void switchToReadWriteMode() { _skey.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); _data.getSelector().wakeup();
selector ssChannel sChannel sChannel sChannel
while (_shouldRun && selector.isOpen()) { try { selector.select(); } catch (IOException e) {…} Iterator it = selector.selectedKeys().iterator(); while (it.hasNext()) { SelectionKey selKey = (SelectionKey) it.next(); it.remove(); if (selKey.isValid() && selKey.isAcceptable()) { … } if (selKey.isValid() && selKey.isReadable()){…} if (selKey.isValid() && selKey.isWritable()){ ConnectionHandler handler = (ConnectionHandler) selKey.attachment(); handler.write(); }
ssChannel selector sChannel sChannel sChannel public synchronized void write() { if (_outData.size() == 0) { switchToReadOnlyMode(); return; } ByteBuffer buf = _outData.remove(0); if (buf.remaining() != 0) { try { _sChannel.write(buf); } catch (IOException e) {…} _outData.add(0, buf); } } if (_protocol.shouldClose()) { switchToWriteOnlyMode(); if (buf.remaining() == 0) { closeConnection(); ssChannel selector “Your message “Don’t worry be happy” received” sChannel sChannel sChannel
ssChannel selector sChannel sChannel public synchronized void write() { if (_outData.size() == 0) { switchToReadOnlyMode(); return; } ByteBuffer buf = _outData.remove(0); if (buf.remaining() != 0) { try { _sChannel.write(buf); } catch (IOException e) {…} _outData.add(0, buf); } } if (_protocol.shouldClose()) { switchToWriteOnlyMode(); if (buf.remaining() == 0) { closeConnection(); ssChannel selector sChannel sChannel