Async Server And Fiber [email protected] Me /luikore ruby-china/luikore Motivation Challenges To Ruby Ruby Advantages Unicode done right

"".length

Builtin continuation - Fiber And... Fast How To Build A High Performance Server How To Build An Async Server How To Build An Async Server Yet Providing Good API Study The Background

The Neck Client Side Of View IO bound - network is slow CPU bound - complex page rendering Server load - wait for other requests to finish The Bottle Neck Server Side Of View IO bound - visiting database / oauth is slow CPU bound - context switch Scaling Increase App Throughput Reduce server side IO-waiting is the most effective way Create less threads, to avoid context switching Learn From Existing Solutions Chances To Ruby Limited Server Objects Many Crazy Clients Multiplexing Single Threaded One Client, One Process, Prefork -- multiplex with accept thin -- multiplex with load balancer Multithreaded One Client, One Thread, No Prefork , recommended for rails4 A simple way to implement is to use POSIX functions with callbacks: aio_read(), aio_write() Single Threaded And Event Loop One Client, One Event Context, Prefork , redis, nodejs, twisted, Thread Pool On Event Loop One Client, One Event Context, No Prefork erlang, golang, scala actors Hello World TCPSocket In 5 Minutes client TCPSocket.new host, port server TCPServer.new host, port receive socket.read socket.read_nonblock send socket.write socket.write_nonblock Hello World HTTP Server

server = TCPServer.new 'localhost', 4000 loop do c = server.accept c << "HTTP/1.1 200 OK Content-Length: 12

Hello world!" c.close end Make It Fast Non­Blocking Our Loop

loop do client = server.accept Thread.new do ... end end Threads Are Outdated Lets Use System Events Multiplexing Functions select IO.select r, w, err, timeout poll IO.poll fds, timeout System Events Linux For Example poll - iterate check all fds epoll - is more effective and provides more options Epoll In 5 Minutes

sudo apt-get install manpages-dev

man epoll Epoll In 5 Minutes register event:

epoll_ctl()

retrieve event:

epoll() Cons Ruby does not ship with epoll API. You need to wrap it with C-ext. Loop With Epoll

loop do event = epoll(timeout) if from_server?(event) connection = server.accept else connection = find_conn_by(event) end ... # resume processing end How To Make It Resumable? Eventmachine async web server clients are multiplexed to

EM::Connection you define callbacks for readable events:

def receive_data(data) "Resume Processing"

connection.receive_data read_nonblock Make It Good How EM Send_data Works write to socket, if the socket can’t take any more, append the rest to a buffer chain when the socket becomes writable again (IO event), try consume the buffer chain Pitfalls Callbacks Are Not Sufficient avoid loss of chained buffers

close_connection_after_writing

avoid infinite recursion when registering events in callback:

EM.next_tick{ ... } Resume Processing With Fiber Fiber was the “Thread” in 1.8 Like thread, but lighter, pausable, and no need to lock Example

fiber = Fiber.new do i = 0 loop do Fiber.yield i += 1 end end fiber.resume #=> 1 fiber.resume #=> 2 Read With Fiber

def read_with_fiber result = '' while buffer = read_nonblock result << buffer if nothing to read Fiber.yield :reading! end end result end Write With Fiber

def write_with_fiber content loop do write_nonblock content content = unwritten part of content break if content.empty? Fiber.yield :writing! end end Recall Our Loop

loop do event = epoll(timeout) if from_server?(event) connection = server.accept else connection = find_conn_by(event) end ... # resume processing end loop do event = epoll(timeout) if from_server?(event) connection = server.accept fiber = Fiber.new { ... read_with_fiber ... write_with_fiber } else fiber = find_fiber_by(event) end fiber.resume end Summary Network latency and C10k leads to async solutions. With Fiber it can be callback-free. In concept it is simple. In practice it is complex: protocols, IO exceptions, ... Product github.com/luikore/nyara faster than sinatra/expressjs in hello world benchmarks