Date post: | 20-Dec-2015 |
Category: |
Documents |
View: | 225 times |
Download: | 2 times |
System Programming
Practical session 12
Reactor
Thread-Per-Client downsides1. Each thread waste resources.
2. Blocking I/O.
3. 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
ServerSocketChannel
SocketChannel
SocketChannel
SocketChannel
SocketChannel
Selector
Server
ConnectionAcceptor
key
ProtocolTask
ConnectionHandler
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));
BuffersByteBuffer are buffers that hold bytes.
Channels know how to read and write to buffers.
final int NUM_OF_BYTES = 1024;
ByteBuffer buf = ByteBuffer.allocate(NUM_OF_BYTES);
From Channel to Buffer and back
Creating a Buffer
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
Blocks until at least one of the channels is ready for the registered event.
selector.select();
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
ConnectionAcceptor
Reactor Actors
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
ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.configureBlocking( false);
ssChannel.socket().bind(new InetSocketAddress(port));
ssChannel
Selector selector = Selector.open();
selector
ssChannel.register(selector, SelectionKey.OP_ACCEPT, connectionAcceptor);
public class Reactor{
...
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();
try {
acceptor.accept();
} catch (IOException e) {…)
if (selKey.isValid() && selKey.isReadable()) { //Handle reading… }
if (selKey.isValid() && selKey.isWritable())
{ //Handle writing… }
}
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();}
}
}
}
ssChannel
selector
Client connection request
sChannel
sChannel
sChannel
ssChannel
selector
sChannel
sChannel
sChannel
“Don’t worry”
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
sChannel
sChannel
sChannel
“Don’t worry”
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);
}
}
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) {
synchronized (_buffers) {
_buffers.add(b);
}
}
}
_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) {
synchronized (_buffers) {
_buffers.add(b);
}
}
}
_buffers
D o n ’ t w o r r y
_tokenizer
“Don’t worry”
ssChannel
selector
sChannel
sChannel
sChannel
“be happy\n”
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);
}
}
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();
}
}
ssChannel
selector
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();
}
}
“Your message “Don’t worry be
happy” received”
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) {…}
if (buf.remaining() != 0) {
_outData.add(0, buf); }
}
if (_protocol.shouldClose()) {
switchToWriteOnlyMode();
if (buf.remaining() == 0) {
closeConnection();
}
}
}
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) {…}
if (buf.remaining() != 0) {
_outData.add(0, buf); }
}
if (_protocol.shouldClose()) {
switchToWriteOnlyMode();
if (buf.remaining() == 0) {
closeConnection();
}
}
}
ssChannel
selector
sChannel
sChannel