Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Threads & JRuby: the simple alternative to evented

Threads & JRuby: the simple alternative to evented

Although event-driven solutions for handling concurrency are getting a lot of attention, they are not without compromise. The JVM provides a robust, battle-tested threading model, along with powerful concurrency libraries that make it easy to solve a wide-variety of problems that need a concurrent solution. By using JRuby, developers can get the advantages of dynamic languages like JavaScript, but wield the full power of the JVM, all while producing clean, comprehensible, testable code.

This talk will relate a real-world problem faced by LivingSocial, and its solution, to a more general set of guidelines, tools, and techniques that any developer can use to deliver a highly performant, concurrent system that is easy to understand.

David Copeland

July 19, 2012
Tweet

More Decks by David Copeland

Other Decks in Programming

Transcript

  1. Threads & JRuby the simple alternative to evented [email protected] @davetron5000

    www.naildrivin5.com www.awesomecommandlineapps.com Thursday, July 19, 12
  2. { :me => { :twitter => “@davetron5000”, :author => “Build

    Awesome Command-Line Applications in Ruby”, } } Thursday, July 19, 12
  3. { :me => { :twitter => “@davetron5000”, :author => “Build

    Awesome Command Line Applications in Ruby”, :techlead => “LivingSocial”, } } Thursday, July 19, 12
  4. easy to understand? easy to test? Will our code be

    easy to write? Thursday, July 19, 12
  5. fork { # your code } not available on JVM

    or in JavaScript Thursday, July 19, 12
  6. var http = require('http'); http.createServer(function (req, res) { res.writeHead(200, {'Content-Type':

    'text/plain'}); res.end('Hello World\n'); }).listen(1337, '127.0.0.1'); Thursday, July 19, 12
  7. // bring in special classes and setup eventedThing.whenIOIsReady(function(results) { //

    do something with results }).startEventLoop(); Thursday, July 19, 12
  8. // bring in special classes and setup eventedThing.whenIOIsReady(function(results) { //

    do something with results otherEventedThing.whenMoreIOIsReady(function(moreResults) { // do something with more I/O }); }).startEventLoop(); Thursday, July 19, 12
  9. // bring in special classes and setup eventedThing.whenIOIsReady(function(results) { //

    do something with results otherEventedThing.whenMoreIOIsReady(function(moreResults) { // do something with more I/O evenMoreEventedThings.whenYetMorIOIsReady(function(moreResults) { // do even more stuff with I/O }); }); }).startEventLoop(); Thursday, July 19, 12
  10. public class EchoServer { public static void main(String[] args) throws

    Exception { ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); bootstrap.bind(new InetSocketAddress(8080)); } } Thursday, July 19, 12
  11. public class EchoServer { public static void main(String[] args) throws

    Exception { ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); bootstrap.bind(new InetSocketAddress(8080)); } } Thursday, July 19, 12
  12. public class EchoServerHandler extends SimpleChannelUpstreamHandler { @Override public void messageReceived(ChannelHandlerContext

    ctx, MessageEvent e) { e.getChannel().write(e.getMessage()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { e.getChannel().close(); } } Thursday, July 19, 12
  13. new Thread(new Runnable() { public void run() { // your

    code } }).start(); Thursday, July 19, 12
  14. What if I need to calculate π to the 2,345,123rd

    decimal place? Thursday, July 19, 12
  15. new Thread(new Runnable() { public void run() { List<Person> people

    = queryPeople(); for (Person p: people) { if (p.getAge() < 18) { // do one thing for minors } else { // do another thing for adults } } } }).start(); Thursday, July 19, 12
  16. Thread.new { query_people.each do |p| if p.get_age < 18 #

    do one thing for minors else # do another thing for adults end end } Thursday, July 19, 12
  17. Thread.new { query_people.each do |p| if p.get_age < 18 #

    do one thing for minors else # do another thing for adults end end } Thursday, July 19, 12
  18. Thread.new { query_people.each do |p| if p.get_age < 18 #

    do one thing for minors else # do another thing for adults end end } Thursday, July 19, 12
  19. Other Rubies? MRI 1.8 Green Threads No Parallelism MRI 1.9

    Real Threads No Parallelism Rubinius Real Threads, no GIL True Parallelism Thursday, July 19, 12
  20. Other Rubies? MRI 1.8 Green Threads No Parallelism MRI 1.9

    Real Threads No Parallelism Rubinius Real Threads, no GIL True Parallelism Thursday, July 19, 12
  21. Start & Manage (Dealing with) Shared State Using Third Party

    Libraries Context Switching Thursday, July 19, 12
  22. Thread.new { # your code } Thread.new { # moar

    code } # etc Thursday, July 19, 12
  23. threads = [] threads << Thread.new { # your code

    } threads << Thread.new { # moar code } # etc threads.each(&:join) # All threads have completed exit 0 Thursday, July 19, 12
  24. java_import java.util.concurrent service = Executors.new_fixed_thread_pool(100) service.execute { # your code

    } service.execute { # some other code } service.shutdown service.await_termination(10,SECONDS) service.shutdown_now Thursday, July 19, 12
  25. java_import java.util.concurrent service = Executors.new_fixed_thread_pool(100) service.execute { # your code

    } service.execute { # some other code } service.shutdown service.await_termination(10,SECONDS) service.shutdown_now Thursday, July 19, 12
  26. java_import java.util.concurrent service = Executors.new_fixed_thread_pool(100) service.execute { # your code

    } service.execute { # some other code } service.shutdown service.await_termination(10,SECONDS) service.shutdown_now Thursday, July 19, 12
  27. java_import java.util.concurrent service = Executors.new_fixed_thread_pool(100) service.execute { # your code

    } service.execute { # some other code } service.shutdown service.await_termination(10,SECONDS) service.shutdown_now Thursday, July 19, 12
  28. java_import java.util.concurrent service = Executors.new_fixed_thread_pool(100) service.execute { # your code

    } service.execute { # some other code } service.shutdown service.await_termination(10,SECONDS) service.shutdown_now Thursday, July 19, 12
  29. java_import java.util.concurrent service = Executors.new_fixed_thread_pool(100) service.execute { # your code

    } service.execute { # some other code } service.shutdown service.await_termination(10,SECONDS) service.shutdown_now Thursday, July 19, 12
  30. service = Executors.new_fixed_thread_pool(10) tcp_server = TCPServer.new("127.0.0.1",8080) Signal.trap('SIGINT') { service.shutdown }

    loop do { s = tcp_server.accept service.execute { s.puts calculate_pi() s.close } break if service.is_shutdown } service.await_termination(10,TimeUnit.SECONDS) service.shutdown_now Thursday, July 19, 12
  31. results = [] Thread.new { results << some_result() } Thread.new

    { results << other_result() } Thursday, July 19, 12
  32. mutex = Mutex.new results = [] Thread.new { mutex.synchronize {

    results << some_result() } } Thread.new { mutex.synchronize { results << other_result() } } Thursday, July 19, 12
  33. Performance Gains with Context Switching Increase in # of Threads

    Speedup Cost of Context Switching Takes Over Thursday, July 19, 12
  34. Your Code Your Code Your Code Your Code Thread Thread

    Thread Thread Pool Your Code Your Code Your Code Your Code Thursday, July 19, 12
  35. Your Code Your Code Your Code Your Code Thread Thread

    Thread Thread Pool Your Code Your Code Your Code Your Code Thursday, July 19, 12
  36. Your Code Your Code Your Code Your Code Thread Thread

    Thread Thread Pool Your Code Your Code Your Code Thursday, July 19, 12
  37. Echo Server •Listen on a port •Respond to each request

    in a new Thread •Extra Credit: Record stats on requests in a shared data structure Thursday, July 19, 12
  38. Connection Pool •Allow N clients to access X shared instances

    of, say, Redis (where N > X) •Clients “check out” a connection and get exclusive access •Clients “check in” when done •Instances get re-used Thursday, July 19, 12