Scalable network services - Java non-blocking IO - Reactor pattern

Slides:



Advertisements
Similar presentations
System Integration and Performance
Advertisements

Categories of I/O Devices
1 (Review of Prerequisite Material). Processes are an abstraction of the operation of computers. So, to understand operating systems, one must have a.
Concurrency Important and difficult (Ada slides copied from Ed Schonberg)
1/1/ / faculty of Electrical Engineering eindhoven university of technology Introduction Part 3: Input/output and co-processors dr.ir. A.C. Verschueren.
Network Operating Systems Users are aware of multiplicity of machines. Access to resources of various machines is done explicitly by: –Logging into the.
Threads Load new page Page is loading Browser still responds to user (can read pages in other tabs)
Page 1 Processes and Threads Chapter Processes 2.2 Threads 2.3 Interprocess communication 2.4 Classical IPC problems 2.5 Scheduling.
Exceptions in Java Fawzi Emad Chau-Wen Tseng Department of Computer Science University of Maryland, College Park.
Inter Process Communication:  It is an essential aspect of process management. By allowing processes to communicate with each other: 1.We can synchronize.
System Programming Practical session 12 Reactor.
System Programming Practical session 11 Multiple clients server Non-Blocking I/O.
16: Distributed Systems1 DISTRIBUTED SYSTEM STRUCTURES NETWORK OPERATING SYSTEMS The users are aware of the physical structure of the network. Each site.
Rensselaer Polytechnic Institute CSC 432 – Operating Systems David Goldschmidt, Ph.D.
1 Thread Pools. 2 What’s A Thread Pool? A programming technique which we will use. A collection of threads that are created once (e.g. when server starts).
1 Threads Chapter 4 Reading: 4.1,4.4, Process Characteristics l Unit of resource ownership - process is allocated: n a virtual address space to.
Fundamentals of Python: From First Programs Through Data Structures
Java Metroplex User's Group
Practical Session 11 Multi Client-Server Java NIO.
Socket Programming -What is it ? -Why bother ?. Basic Interface for programming networks at transport level It is communication end point Used for inter.
Java NIO. NIO: New I/O Prior to the J2SE 1.4 release of Java, I/O had become a bottleneck. –JIT performance was reaching the point where one could start.
System Calls 1.
The Client-Server Model – part II
Interrupts and DMA CSCI The Role of the Operating System in Performing I/O Two main jobs of a computer are: –Processing –Performing I/O manage and.
FINAL MPX DELIVERABLE Due when you schedule your interview and presentation.
Rensselaer Polytechnic Institute CSCI-4210 – Operating Systems CSCI-6140 – Computer Operating Systems David Goldschmidt, Ph.D.
REVIEW On Friday we explored Client-Server Applications with Sockets. Servers must create a ServerSocket object on a specific Port #. They then can wait.
Lecture 3 Process Concepts. What is a Process? A process is the dynamic execution context of an executing program. Several processes may run concurrently,
1 (Worker Queues) cs What is a Thread Pool? A collection of threads that are created once (e.g. when a server starts) That is, no need to create.
Device Drivers CPU I/O Interface Device Driver DEVICECONTROL OPERATIONSDATA TRANSFER OPERATIONS Disk Seek to Sector, Track, Cyl. Seek Home Position.
Li Tak Sing COMPS311F. Case study: consumers and producers A fixed size buffer which can hold at most certain integers. A number of producers which generate.
1 Chapter 28 Networking. 2 Objectives F To comprehend socket-based communication in Java (§28.2). F To understand client/server computing (§28.2). F To.
School of Engineering and Computer Science Victoria University of Wellington Copyright: Peter Andreae david streader, VUW Networking and Concurrency COMP.
Dynamic Architectures (Component Reconfiguration) with Fractal.
Ron Hitchens Java NIO Book Website JavaPolis Antwerp, Belgium Dec 3, 2003 © 2003, Ronsoft.
Threaded Programming in Python Adapted from Fundamentals of Python: From First Programs Through Data Structures CPE 401 / 601 Computer Network Systems.
Practical Session 12 Reactor Pattern. Disadvantages of Thread per Client It's wasteful – Creating a new Thread is relatively expensive. – Each thread.
Java for High Performance Computing java.nio: High Performance I/O for Java Instructor: Bryan Carpenter.
Chapter 2 Processes and Threads Introduction 2.2 Processes A Process is the execution of a Program More specifically… – A process is a program.
ICS 313: Programming Language Theory Chapter 13: Concurrency.
Practical Session 11 Multi Client-Server Java NIO.
Processes CSCI 4534 Chapter 4. Introduction Early computer systems allowed one program to be executed at a time –The program had complete control of the.
CE Operating Systems Lecture 2 Low level hardware support for operating systems.
Exceptions Chapter 16 This chapter explains: What as exception is Why they are useful Java exception facilities.
CE Operating Systems Lecture 2 Low level hardware support for operating systems.
Threads versus Events CSE451 Andrew Whitaker. This Class Threads vs. events is an ongoing debate  So, neat-and-tidy answers aren’t necessarily available.
UNIT-6. Basics of Networking TCP/IP Sockets Simple Client Server program Multiple clients Sending file from Server to Client Parallel search server.
Netprog: Client/Server Issues1 Issues in Client/Server Programming Refs: Chapter 27.
Advanced Java Session 4 - Extra New York University School of Continuing and Professional Studies.
Interrupts and Exception Handling. Execution We are quite aware of the Fetch, Execute process of the control unit of the CPU –Fetch and instruction as.
SPL/2010 Reactor Design Pattern 1. SPL/2010 Overview ● blocking sockets - impact on server scalability. ● non-blocking IO in Java - java.niopackage ●
Embedded Real-Time Systems Processing interrupts Lecturer Department University.
FILES AND EXCEPTIONS Topics Introduction to File Input and Output Using Loops to Process Files Processing Records Exceptions.
Threads in Java Two ways to start a thread
Threaded Programming in Python
Thread Pools (Worker Queues) cs
Thread Pools (Worker Queues) cs
Advanced Topics in Concurrency and Reactive Programming: Asynchronous Programming Majeed Kassis.
Java Programming Language
Distributed Systems - Comp 655
Reactor Design Pattern
Topics Introduction to File Input and Output
MARIE: An Introduction to a Simple Computer
Issues in Client/Server Programming
Threads Chapter 4.
Foundations and Definitions
Topics Introduction to File Input and Output
Chapter 13: I/O Systems.
The reactor design pattern
Thread per client and Java NIO
Presentation transcript:

Scalable network services - Java non-blocking IO - Reactor pattern

Classic ServerSocket Loop class Server implements Runnable { public void run() { try { ServerSocket ss = new ServerSocket(PORT); while (!Thread.interrupted()) new Thread(new Handler(ss.accept())).start(); // or, single-threaded, or a thread pool } catch (IOException ex) { /* ... */ } } static class Handler implements Runnable { final Socket socket; Handler(Socket s) { socket = s; } byte[] input = new byte[MAX_INPUT]; socket.getInputStream().read(input); byte[] output = process(input); socket.getOutputStream().write(output); private byte[] process(byte[] cmd) { /* ... */ } Note: most exception handling avoided in code examples Classic ServerSocket Loop

Graceful degradation under increasing load (more clients) Scalability Goals Graceful degradation under increasing load (more clients) Continuous improvement with increasing resources (CPU, memory, disk, bandwidth) Also, to meet availability and performance goals Response time, Peak demand

java.nio Server Side

java.nio Support Channels Buffers Selectors SelectionKeys Connections to files, sockets etc that support non-blocking operations (read, write) Buffers Array-like objects that can be directly read or written by Channels Selectors Tell which of a set of Channels have IO events SelectionKeys Maintain IO event status and bindings

Event-driven Designs Usually more efficient than alternatives Fewer resources Don't usually need a thread per client Less overhead Less context switching, often less locking But dispatching can be slower Must manually bind actions to events Usually harder to program Must break up into simple non-blocking actions Similar to GUI event-driven actions Cannot eliminate all blocking: GC, page faults, etc Must keep track of logical state of service

Program structure with Java non-blocking IO Create Selector and one or more Channels Register Channels to a Selector Mark the events of interest to detect in each channel. Loop: Call to the select operation of Selector. Returns a number of the keys whose ready set has changed. (each key corresponds to a channel) Using the selected keys: Check ready operations for each key. Remove the key from the selected keys set. That clears the ready operations set of the key. Do appropriate processing.

Program structure with Java non-blocking IO // Create the server socket channel ServerSocketChannel server = ServerSocketChannel.open(); // nonblocking I/O server.configureBlocking(false); // host-port 8000 server.socket().bind(new java.net.InetSocketAddress(host,8000)); // Create the selector Selector selector = Selector.open(); // Recording server to selector (type OP_ACCEPT) server.register(selector,SelectionKey.OP_ACCEPT);

Program structure with Java non-blocking IO for(;;) { // Waiting for events selector.select(); // Get keys Set keys = selector.selectedKeys(); Iterator i = keys.iterator(); // For each keys... while(i.hasNext()) { SelectionKey key = (SelectionKey) i.next(); // Remove the current key i.remove();

Program structure with Java non-blocking IO // if isAccetable = true - then a client required a connection if (key.isAcceptable()) { // get client socket channel SocketChannel client = server.accept(); // Non Blocking I/O client.configureBlocking(false); // recording to the selector (reading) client.register(selector, SelectionKey.OP_READ); continue; } // if isReadable = true - then the server is ready to read if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); try { client.read(buffer); } catch (Exception e) {… }

Buffers Buffer  a container for a fixed amount of data. Similar to a byte [] array But encapsulated such that internal storage can be a block of system memory. Direct memory mapping Zero-copy read/receive  high performance communication

Creating buffers Factory methods ByteBuffer allocate(int capacity) creates a ByteBuffer with an ordinary Java backing array of size capacity. ByteBuffer allocateDirect(int capacity) Does its best to provide zero-copy IO ByteBuffer wrap(byte [] array) wrap() methods create ByteBuffer’s backed by all or part of an array allocated by the user ByteBuffer wrap(byte [] array, int offset, length) all static methods of the ByteBuffer class.

Channels high-level version of file-descriptors from POSIX of operating systems. a handle for performing I/O and control operations on an open file/socket. java.nio associates a channel with any RandomAccessFile, FileInputStream, FileOutputStream, Socket, ServerSocket or DatagramSocket object. the channel just provides extra NIO-specific functionality. NIO buffer objects can written to or read from channels directly.

Opening Channels Socket channel classes have static factory methods called open(), e.g.: SocketChannel sc = SocketChannel.open() ; Sc.connect(new InetSocketAddress(hostname, portnumber)) ;

Using Channels All channels that implement the ByteChannel interface provide a read() and a write() instance method: int read(ByteBuffer dst) int write(ByteBuffer src) The Java read() attempts to read from the channel as many bytes as there are remaining to be written in the dst buffer. Returns number of bytes actually read, or -1 if end-of-stream. Also updates dst buffer position. Similarly write() attempts to write to the channel as many bytes as there are remaining in the src buffer. Returns number of bytes actually read, and updates src buffer position.

Using Channels – Example Assume a source channel src and a destination channel dest: ByteBuffer buffer = ByteBuffer.allocateDirect(BUF_SIZE) ; while(src.read(buffer) != -1) { buffer.flip() ; // Prepare read buffer for “draining” while(buffer.hasRemaining()) dest.write(buffer) ; buffer.clear() ; // Empty buffer, ready to read next chunk. } Notes: a write() call (or a read() call) may or may not succeed in transferring whole buffer in a single call. Hence need for inner while loop. Example introduces two new methods on Buffer: hasRemaining() returns true if position < limit; clear() sets position to 0 and limit to buffer’s capacity.

Basic Socket Channel Operations Typical use of a server socket channel: ServerSocketChannel ssc = ServerSocketChannel.open() ; ssc.socket().bind( new InetSocketAddress(port) ) ; while(true) { SocketChannel sc = ssc.accept() ; … process a transaction with client through sc … } The client: SocketChannel sc = SocketChannel.open() ; sc.connect( new InetSocketAddr(serverName, port) ) ; … initiate a transaction with server through sc … Then typically use read() and write() calls on the SocketChannel  four operations: accept(), connect(), write(), read()

Nonblocking Operations socket.configureBlocking(false) ; A read() operation transfers data that is immediately available. If no data immediately available, returns 0. If data cannot be immediately written, a write() will immediately return 0. For a server socket, if no client is currently trying to connect, the accept() method immediately returns null. connect() is more complicated generally connections would always block for some interval waiting for the server to respond. In non-blocking mode connect() generally returns false. But the negotiation with the server is nevertheless started. The finishConnect() method on the same socket should be called later. It also returns immediately. Repeat until it return true.

Setting Up Selectors To create: To add channel: open() factory method. To add channel: SelectionKey register(Selector sel, int ops) ops  bit-set representing the interest set for this channel: composed by oring together one or more of: SelectionKey.OP_READ SelectionKey.OP_WRITE SelectionKey.OP_CONNECT SelectionKey.OP_ACCEPT A channel added to a selector must be in nonblocking mode! The register() method returns the SelectionKey created This automatically gets stored in the Selector, so in most cases you probably don’t need to save the result yourself.

Example Create a selector and register three pre-existing channels: Selector selector = Selector.open() ; channel1.register (selector, SelectionKey.OP_READ) ; channel2.register (selector, SelectionKey.OP_WRITE) ; channel3.register (selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE) ; Notes channel1, channel2, channel3 must all be in non-blocking mode at this time, and must remain in that mode as long as they are registered in any selector. You remove a channel from a selector by calling the cancel() method of the associated SelectionKey.

select() and the Selected Key Set call select() To inspect the set of registered channels Return value: zero if no status changes occurred. Side effect: set the selected keys embedded in the selector. A selector maintains a Set object representing this selected keys set. Each key is associated with a channel,  set of selected channels. The set of selected keys is different from (presumably a subset of) the registered key set. Each time the select() method is called it may add new keys to the selected key set, as operations become ready to proceed. The programmer is responsible for explicitly removing keys from the selected key set belonging to the selector

Ready Sets Each key in the registered key set has an associated interest set subset of the 4 possible operations on channels. Similarly each key in the selected key set has an associated ready set, a subset of the interest set—representing the actual operations that have been found ready to proceed. You can extract the ready set from a SelectionKey as a bit-set, by using the method readyOps(). Or you can use the convenience methods: isReadable() isWriteable() isConnectable() isAcceptable()

A Pattern for Using select() // … register some channels with selector … while(true) { selector.select() ; Iterator it = selector.selectedKeys().iterator() ; while( it.hasNext() ) { SelectionKey key = it.next() ; if( key.isReadable() ) … perform read() operation on key.channel() … if( key.isWriteable() ) … perform write() operation on key.channel() … if( key.isConnectable() ) … perform connect() operation on key.channel() … if( key.isAcceptable() ) … perform accept() operation on key.channel() … it.remove() ; }

Remarks This general pattern will probably serve for most uses of select(): Perform select() and extract the new selected key set For each selected key, handle the actions in its ready set Remove the processed key from the selected key set Note the remove() operation on an Iterator removes the current item from the underlying container. More generally, the code that handles a ready operation may also alter the set of channels registered with the selector e.g after doing an accept() you may want to register the returned SocketChannel with the selector, to wait for read() or write() operations. In many cases only a subset of the possible operations read, write, accept, connect are ever in interest sets of keys registered with the selector, so you won’t need all 4 tests.

Key Attachments Problem: when it.next() returns a key, there is no convenient way of getting information about the context in which the associated channel was registered with the selector. Solution: specify an arbitrary object as an attachment to the channel register it; Later when you get the key from the selected set, you can extract the attachment, and use its content in to decide what to do.

Simplistic Use of Key Attachments channel1.register (selector, SelectionKey.OP_READ, new Integer(1) ) ; // attachment … channel3.register (selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, new Integer(3) ) ; // attachment while(true) { Iterator it = selector.selectedKeys().iterator() ; SelectionKey key = it.next() ; if( key.isReadable() ) switch( ((Integer) key.channel().attachment() ).value() ) { case 1 : … action appropriate to channel1 … case 3 : … action appropriate to channel3 … }

Reactor pattern

Classic Network Service Design Each handler started in its own thread Problems?

Divide processing into small tasks Execute each task when it is ready Divide and Conquer Divide processing into small tasks Each task performs an action without blocking Execute each task when it is ready Here, an IO event usually serves as trigger

Reactor Pattern Purpose Avantages: Receive incoming messages from multiple concurrent clients. Process these messages using event handlers one at a time. Useful in e.g. servers, graphics systems, component frameworks. (example: Java AWT) Avantages: Serialisation of events  simpler concurrency in application. Modularity/portability improved.

Reactor Pattern – Structure

Reactor Pattern Structure (2) Handle Receives events; E.g. a network connection, timer, user interface device Synchronous Event Demultiplexer select() waits until an event is received on a Handle and returns the event. Often implemented as part of an operating system. Initiation Dispatcher Uses the Synchronous Event Demultiplexer to wait for events. Dispatches events to the Event Handlers. Event Handler Application-specific event processing code.

Reactor Pattern – Dynamics Setup Create Initiation Dispatcher. Register Event Handlers with Initiation Dispatcher. Main loop Call handleEvents in Initiation Dispatcher repeatedly. Initiation Dispatcher calls select in Synchronous Event Demultiplexer, blocking until an event is received. The Initiation Dispatcher calls handleEvent in the corresponding Event Handler, passing it the event. End Unregister Event Handlers from Initiation Dispatcher.

Summary Scalable network services Reactor pattern Java non-blocking IO _______________________________________Typical program structure Create Selector and one or more Channels Register Channels to a Selector Mark the events of interest to detect in each channel. Loop: Call to the select operation of Selector. Returns a number of the keys whose ready set has changed. (each key corresponds to a channel) Using the selected keys: Check ready operations for each key. Remove the key from the selected keys set. Do appropriate processing.

Example Code Introducing Nonblocking Sockets http://www.onjava.com/pub/a/onjava/2002/09/04/nio.html?page=1

Implementing Reactor 2: Dispatch loop // class Reactor continued public void run() { // normally in a new Thread try { while (!Thread.interrupted()) { selector.select(); Set selected = selector.selectedKeys(); Iterator it = selected.iterator(); while (it.hasNext()) dispatch((SelectionKey)(it.next()); selected.clear(); } } catch (IOException ex) { /* ... */ } void dispatch(SelectionKey k) { Runnable r = (Runnable)(k.attachment()); if (r != null) r.run();

Implementing Reactor 3: Acceptor // class Reactor continued class Acceptor implements Runnable { // inner public void run() { try { SocketChannel c = serverSocket.accept(); if (c != null) new Handler(selector, c); } catch(IOException ex) { /* ... */ } } // end class Acceptor } // end class reactor

Implementing Reactor 4: Handler setup final class Handler implements Runnable { final SocketChannel socket; final SelectionKey sk; ByteBuffer input = ByteBuffer.allocate(MAXIN); ByteBuffer output = ByteBuffer.allocate(MAXOUT); static final int READING = 0, SENDING = 1; int state = READING; Handler(Selector sel, SocketChannel c) throws IOException { socket = c; c.configureBlocking(false); // Optionally try first read now sk = socket.register(sel, 0); sk.attach(this); sk.interestOps(SelectionKey.OP_READ); sel.wakeup(); } boolean inputIsComplete() { /* ... */ } boolean outputIsComplete() { /* ... */ } void process() { /* ... */ }

Reactor 5: Request handling // class Handler continued public void run() { try { if (state == READING) read(); else if (state == SENDING) send(); } catch (IOException ex) { /* ... */ } } void read() throws IOException { socket.read(input); if (inputIsComplete()) { process(); state = SENDING; // Normally also do first write now sk.interestOps(SelectionKey.OP_WRITE); void send() throws IOException { socket.write(output); if (outputIsComplete()) sk.cancel();

Simple NIO Client Example SocketChannel client = SocketChannel.open(); // Create client SocketChannel client.configureBlocking(false); // nonblocking I/O client.connect(new java.net.InetSocketAddress(host,8000)); Selector selector = Selector.open(); // Create selector // register to selector (OP_CONNECT type) SelectionKey clientKey = client.register(selector, SelectionKey.OP_CONNECT); while (selector.select(500)> 0) { // Waiting for the connection Set keys = selector.selectedKeys(); // Get keys Iterator i = keys.iterator(); while (i.hasNext()) { // process keys SelectionKey key = (SelectionKey)i.next(); i.remove(); // Remove the current key SocketChannel channel = (SocketChannel)key.channel(); if (key.isConnectable()) { // Attempt a connection if (channel.finishConnect()) { // attempt to finalize connect // Write continuously on the buffer buffer = ByteBuffer.wrap(new String(" Client " + id + " ").getBytes()); channel.write(buffer); buffer.clear(); } Simple NIO Client Example