Non blocking io with netty

8,270 views
8,037 views

Published on

Introducción a IO no bloqueante y sus ventajas.
Como hacer servidores con netty

Published in: Technology, News & Politics
0 Comments
47 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
8,270
On SlideShare
0
From Embeds
0
Number of Embeds
59
Actions
Shares
0
Downloads
0
Comments
0
Likes
47
Embeds 0
No embeds

No notes for slide
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Non blocking io with netty

    1. 1. Non-Blocking IO with NettyMariano Cortesi Fernando Zunino@marianocortesi @fzunino
    2. 2. Por que Non Blocking IO?
    3. 3. Por que Non Blocking IO?
    4. 4. Por que Non Blocking IO?
    5. 5. Por que Non Blocking IO?
    6. 6. Construyendo servidores Estructura básica Leer Decodificar Codificar Enviar ProcesarRequest Request Respuesta Respuesta
    7. 7. El Camino Tradicional ✓ Thread por conexión ✓ Uso de IO bloqueante ✓ Modelo sencillo de programación Leer Decodificar Procesar Codificar EnviarCliente HandlerCliente Servidor Leer Decodificar Procesar Codificar Enviar HandlerCliente Leer Decodificar Procesar Codificar Enviar Handler
    8. 8. An Echo Server • Java OIOpublic class ThreadPoolEchoServer { public static void main(String[] args) throws IOException { ServerSocket servSock = new ServerSocket(20007); Executor service = Executors.newCachedThreadPool(); while (!Thread.interrupted()) { Socket clntSock = servSock.accept(); service.execute(new EchoWorker(clntSock)); } }}
    9. 9. An Echo Server • Java OIOpublic class ThreadPoolEchoServer { Inicialización de socket para escucha public static void main(String[] args) throws IOException { ServerSocket servSock = new ServerSocket(20007); Executor service = Executors.newCachedThreadPool(); while (!Thread.interrupted()) { Socket clntSock = servSock.accept(); service.execute(new EchoWorker(clntSock)); } }}
    10. 10. An Echo Server • Java OIO public class ThreadPoolEchoServer { public static void main(String[] args) throws IOException {Inicialización de ServerSocket servSock = new ServerSocket(20007);pool de threads Executor service = Executors.newCachedThreadPool(); while (!Thread.interrupted()) { Socket clntSock = servSock.accept(); service.execute(new EchoWorker(clntSock)); } } }
    11. 11. An Echo Server • Java OIOpublic class ThreadPoolEchoServer { public static void main(String[] args) throws IOException { ServerSocket servSock = new ServerSocket(20007); Executor service = Executors.newCachedThreadPool(); while (!Thread.interrupted()) { Se acepta conexión Socket clntSock = servSock.accept(); service.execute(new EchoWorker(clntSock)); } }}
    12. 12. An Echo Server • Java OIOpublic class ThreadPoolEchoServer { public static void main(String[] args) throws IOException { ServerSocket servSock = new ServerSocket(20007); Executor service = Executors.newCachedThreadPool(); while (!Thread.interrupted()) { Socket clntSock = servSock.accept(); service.execute(new EchoWorker(clntSock)); } } Se ejecuta worker thread}
    13. 13. An Echo Server • Java OIOpublic class ThreadPoolEchoServer { public static void main(String[] args) throws IOException { ServerSocket servSock = new ServerSocket(20007); Executor service = Executors.newCachedThreadPool(); while (!Thread.interrupted()) { Socket clntSock = servSock.accept(); service.execute(new EchoWorker(clntSock)); } }}
    14. 14. An Echo Server • Java OIOpublic class EchoWorker implements Runnable { public EchoWorker(final Socket s) { this.socket = s; } public void run() { try { InputStream in = this.socket.getInputStream(); OutputStream out = this.socket.getOutputStream(); int recvMsgSize; byte[] receiveBuf = new byte[256]; while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } } catch (IOException e) { // ... } finally { try { this.socket.close(); } catch (IOException e) { // ... } } }}
    15. 15. An Echo Server • Java OIOpublic class EchoWorker implements Runnable { public EchoWorker(final Socket s) { this.socket = s; } Obtención de Streams de public void run() { entrada y salida try { InputStream in = this.socket.getInputStream(); OutputStream out = this.socket.getOutputStream(); int recvMsgSize; byte[] receiveBuf = new byte[256]; while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } } catch (IOException e) { // ... } finally { try { this.socket.close(); } catch (IOException e) { // ... } } }}
    16. 16. An Echo Server • Java OIOpublic class EchoWorker implements Runnable { public EchoWorker(final Socket s) { this.socket = s; } public void run() { try { InputStream in = this.socket.getInputStream(); OutputStream out = this.socket.getOutputStream(); int recvMsgSize; Se lee hasta que el cliente cierre la conexión byte[] receiveBuf = new byte[256]; (bloqueante) while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } } catch (IOException e) { // ... } finally { try { this.socket.close(); } catch (IOException e) { // ... } } }}
    17. 17. An Echo Server • Java OIOpublic class EchoWorker implements Runnable { public EchoWorker(final Socket s) { this.socket = s; } public void run() { try { InputStream in = this.socket.getInputStream(); OutputStream out = this.socket.getOutputStream(); int recvMsgSize; byte[] receiveBuf = new byte[256]; while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } Se reenvia lo leido al } catch (IOException e) { // ... cliente (bloqueante) } finally { try { this.socket.close(); } catch (IOException e) { // ... } } }}
    18. 18. An Echo Server • Java OIOpublic class EchoWorker implements Runnable { public EchoWorker(final Socket s) { this.socket = s; } public void run() { try { InputStream in = this.socket.getInputStream(); OutputStream out = this.socket.getOutputStream(); int recvMsgSize; byte[] receiveBuf = new byte[256]; while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } } catch (IOException e) { // ... } finally { try { this.socket.close(); } catch (IOException e) { // ... } el socket Se cierra } }}
    19. 19. An Echo Server • Java OIOpublic class EchoWorker implements Runnable { public EchoWorker(final Socket s) { this.socket = s; } public void run() { try { InputStream in = this.socket.getInputStream(); OutputStream out = this.socket.getOutputStream(); int recvMsgSize; byte[] receiveBuf = new byte[256]; while ((recvMsgSize = in.read(receiveBuf)) != -1) { out.write(receiveBuf, 0, recvMsgSize); } } catch (IOException e) { // ... } finally { try { this.socket.close(); } catch (IOException e) { // ... } } }}
    20. 20. El Camino Tradicional: Problemas
    21. 21. El Camino Tradicional: Problemas✓ Estado compartido entre clientes
    22. 22. El Camino Tradicional: Problemas✓ Estado compartido entre clientes ✓Sincronización
    23. 23. El Camino Tradicional: Problemas✓ Estado compartido entre clientes ✓Sincronización✓ Priorización de clientes
    24. 24. El Camino Tradicional: Problemas✓ Estado compartido entre clientes ✓Sincronización✓ Priorización de clientes✓ Alta escala (C10K)
    25. 25. El Camino Tradicional: Problemas✓ Estado compartido entre clientes ✓Sincronización✓ Priorización de clientes✓ Alta escala (C10K) ✓ Conexiones Persistentes
    26. 26. El Camino Tradicional: Problemas✓ Estado compartido entre clientes ✓Sincronización✓ Priorización de clientes✓ Alta escala (C10K) ✓ Conexiones Persistentes ✓ Context Switching Overhead
    27. 27. El Camino Tradicional: Problemas✓ Estado compartido entre clientes ✓Sincronización✓ Priorización de clientes✓ Alta escala (C10K) ✓ Conexiones Persistentes ✓ Context Switching Overhead ✓ Consumo de memoria
    28. 28. Non Blocking IO: What’s the story?✓ Modelo orientado a eventos Reactor Pattern✓ Un único thread de procesamiento✓ Uso de Readiness Notification y Non Blocking IO Leer Cliente Decodificar read events Procesar Cliente Reactor write events Codificar Cliente Enviar
    29. 29. Non Blocking IO vs. Blocking IO
    30. 30. Non Blocking IO en Java
    31. 31. Non Blocking IO en Java✓ Java NIO
    32. 32. Non Blocking IO en Java✓ Java NIO✓ Frameworks
    33. 33. Non Blocking IO en Java✓ Java NIO✓ Frameworks ✓ Apache Mina
    34. 34. Non Blocking IO en Java✓ Java NIO✓ Frameworks ✓ Apache Mina ✓ JBoss Netty
    35. 35. Non Blocking IO en Java - NIO Diseño Java NIO Dispatcher EventHandler * 1 Selector SelectionKey SelectableChannel SocketChanneljava.nio.channels
    36. 36. An Echo Server • Java NIOpublic class Dispatcher { public Dispatcher(final int port, final EventHandler handler) { ... } public void run() throws IOException { Selector selector = Selector.open(); ServerSocketChannel listenChannel = ServerSocketChannel.open(); listenChannel.socket().bind(new InetSocketAddress(this.port)); listenChannel.configureBlocking(false); listenChannel.register(selector, SelectionKey.OP_ACCEPT); while (!Thread.interrupted()) { if (selector.select(3000) == 0) continue; Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); if (key.isAcceptable()) { handler.handleAccept(key); } if (key.isReadable()) { handler.handleRead(key); } if (key.isValid() && key.isWritable()) { handler.handleWrite(key); } keyIter.remove(); // remove from set of selected keys } } }}
    37. 37. An Echo Server • Java NIOpublic class Dispatcher { public Dispatcher(final int port, final EventHandler handler) { ... Crea selector para } monitorear sockets public void run() throws IOException { pasivos y conexiones Selector selector = Selector.open(); ServerSocketChannel listenChannel = ServerSocketChannel.open(); listenChannel.socket().bind(new InetSocketAddress(this.port)); listenChannel.configureBlocking(false); listenChannel.register(selector, SelectionKey.OP_ACCEPT); while (!Thread.interrupted()) { if (selector.select(3000) == 0) continue; Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); if (key.isAcceptable()) { handler.handleAccept(key); } if (key.isReadable()) { handler.handleRead(key); } if (key.isValid() && key.isWritable()) { handler.handleWrite(key); } keyIter.remove(); // remove from set of selected keys } } }}
    38. 38. An Echo Server • Java NIOpublic class Dispatcher { public Dispatcher(final int port, final EventHandler handler) { ... } public void run() throws IOException { Inicialización de socket Selector selector = Selector.open(); para escucha en forma no bloqueante ServerSocketChannel listenChannel = ServerSocketChannel.open(); listenChannel.socket().bind(new InetSocketAddress(this.port)); listenChannel.configureBlocking(false); listenChannel.register(selector, SelectionKey.OP_ACCEPT); while (!Thread.interrupted()) { if (selector.select(3000) == 0) continue; Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); if (key.isAcceptable()) { handler.handleAccept(key); } if (key.isReadable()) { handler.handleRead(key); } if (key.isValid() && key.isWritable()) { handler.handleWrite(key); } keyIter.remove(); // remove from set of selected keys } } }}
    39. 39. An Echo Server • Java NIOpublic class Dispatcher { public Dispatcher(final int port, final EventHandler handler) { ... } public void run() throws IOException { Selector selector = Selector.open(); ServerSocketChannel listenChannel = ServerSocketChannel.open(); listenChannel.socket().bind(new InetSocketAddress(this.port)); listenChannel.configureBlocking(false); Registra socket listenChannel.register(selector, SelectionKey.OP_ACCEPT); declarando interés en nuevas conexiones while (!Thread.interrupted()) { if (selector.select(3000) == 0) continue; Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); if (key.isAcceptable()) { handler.handleAccept(key); } if (key.isReadable()) { handler.handleRead(key); } if (key.isValid() && key.isWritable()) { handler.handleWrite(key); } keyIter.remove(); // remove from set of selected keys } } }}
    40. 40. An Echo Server • Java NIO public class Dispatcher { public Dispatcher(final int port, final EventHandler handler) { ... } public void run() throws IOException { Selector selector = Selector.open(); ServerSocketChannel listenChannel = ServerSocketChannel.open(); listenChannel.socket().bind(new InetSocketAddress(this.port));Monitorea actividad en listenChannel.configureBlocking(false); todos los sockets listenChannel.register(selector, SelectionKey.OP_ACCEPT); registrados while (!Thread.interrupted()) { if (selector.select(3000) == 0) continue; Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); if (key.isAcceptable()) { handler.handleAccept(key); } if (key.isReadable()) { handler.handleRead(key); } if (key.isValid() && key.isWritable()) { handler.handleWrite(key); } keyIter.remove(); // remove from set of selected keys } } } }
    41. 41. An Echo Server • Java NIOpublic class Dispatcher { public Dispatcher(final int port, final EventHandler handler) { ... } public void run() throws IOException { Selector selector = Selector.open(); ServerSocketChannel listenChannel = ServerSocketChannel.open(); listenChannel.socket().bind(new InetSocketAddress(this.port)); listenChannel.configureBlocking(false); listenChannel.register(selector, SelectionKey.OP_ACCEPT); while (!Thread.interrupted()) { if (selector.select(3000) == 0) continue; Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { Detecta y dispara eventos SelectionKey key = keyIter.next(); de acuerdo al tipo if (key.isAcceptable()) { handler.handleAccept(key); } if (key.isReadable()) { handler.handleRead(key); } if (key.isValid() && key.isWritable()) { handler.handleWrite(key); } keyIter.remove(); // remove from set of selected keys } } }}
    42. 42. An Echo Server • Java NIOpublic class Dispatcher { public Dispatcher(final int port, final EventHandler handler) { ... } public void run() throws IOException { Selector selector = Selector.open(); ServerSocketChannel listenChannel = ServerSocketChannel.open(); listenChannel.socket().bind(new InetSocketAddress(this.port)); listenChannel.configureBlocking(false); listenChannel.register(selector, SelectionKey.OP_ACCEPT); while (!Thread.interrupted()) { if (selector.select(3000) == 0) continue; Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); while (keyIter.hasNext()) { SelectionKey key = keyIter.next(); if (key.isAcceptable()) { handler.handleAccept(key); } if (key.isReadable()) { handler.handleRead(key); } if (key.isValid() && key.isWritable()) { handler.handleWrite(key); } keyIter.remove(); // remove from set of selected keys } } }}
    43. 43. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } public void handleRead(SelectionKey key) throws IOException { SocketChannel clntChan = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); if (bytesRead == -1) { // Did the other end close? clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } } public void handleWrite(SelectionKey key) throws IOException { ByteBuffer buf = (ByteBuffer) key.attachment(); buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } buf.compact(); }}
    44. 44. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; Acepta nueva conexión public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } public void handleRead(SelectionKey key) throws IOException { SocketChannel clntChan = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); if (bytesRead == -1) { // Did the other end close? clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } } public void handleWrite(SelectionKey key) throws IOException { ByteBuffer buf = (ByteBuffer) key.attachment(); buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } buf.compact(); }}
    45. 45. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } Registra nuevo socket public void handleRead(SelectionKey key) throws IOException { declarando interés en SocketChannel clntChan = (SocketChannel) key.channel(); lectura y asocia buffer ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); if (bytesRead == -1) { // Did the other end close? clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } } public void handleWrite(SelectionKey key) throws IOException { ByteBuffer buf = (ByteBuffer) key.attachment(); buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } buf.compact(); }}
    46. 46. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } public void handleRead(SelectionKey key) throws IOException { SocketChannel clntChan = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); Lee datos en buffer if (bytesRead == -1) { // Did the other end close? asociado clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } } public void handleWrite(SelectionKey key) throws IOException { ByteBuffer buf = (ByteBuffer) key.attachment(); buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } buf.compact(); }}
    47. 47. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } public void handleRead(SelectionKey key) throws IOException { SocketChannel clntChan = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); if (bytesRead == -1) { // Did the other end close? clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); Detecta fin de conexión } } public void handleWrite(SelectionKey key) throws IOException { ByteBuffer buf = (ByteBuffer) key.attachment(); buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } buf.compact(); }}
    48. 48. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } public void handleRead(SelectionKey key) throws IOException { SocketChannel clntChan = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); if (bytesRead == -1) { // Did the other end close? clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } } Declara interés en public void handleWrite(SelectionKey key) throws IOException { escritura si hay datos a ByteBuffer buf = (ByteBuffer) key.attachment(); escribir buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } buf.compact(); }}
    49. 49. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } public void handleRead(SelectionKey key) throws IOException { SocketChannel clntChan = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); if (bytesRead == -1) { // Did the other end close? clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } } public void handleWrite(SelectionKey key) throws IOException { ByteBuffer buf = (ByteBuffer) key.attachment(); buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); Envia datos al socket if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } buf.compact(); }}
    50. 50. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } public void handleRead(SelectionKey key) throws IOException { SocketChannel clntChan = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); if (bytesRead == -1) { // Did the other end close? clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } } public void handleWrite(SelectionKey key) throws IOException { ByteBuffer buf = (ByteBuffer) key.attachment(); buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } Revoca interés en escritura buf.compact(); si no hay nada para escribir }}
    51. 51. An Echo Server • Java NIOpublic class EchoProtocolEventHandler implements EventHandler { private static final int BUFSIZE = 256; public void handleAccept(SelectionKey key) throws IOException { SocketChannel clntChan = ((ServerSocketChannel) key.channel()).accept(); clntChan.configureBlocking(false); clntChan.register(key.selector(), SelectionKey.OP_READ, ByteBuffer.allocate(BUFSIZE)); } public void handleRead(SelectionKey key) throws IOException { SocketChannel clntChan = (SocketChannel) key.channel(); ByteBuffer buf = (ByteBuffer) key.attachment(); long bytesRead = clntChan.read(buf); if (bytesRead == -1) { // Did the other end close? clntChan.close(); } else if (bytesRead > 0) { key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE); } } public void handleWrite(SelectionKey key) throws IOException { ByteBuffer buf = (ByteBuffer) key.attachment(); buf.flip(); SocketChannel clntChan = (SocketChannel) key.channel(); clntChan.write(buf); if (!buf.hasRemaining()) { key.interestOps(SelectionKey.OP_READ); } buf.compact(); }}
    52. 52. An Echo Server • Java NIOpublic interface EventHandler { void handleAccept(SelectionKey key) throws IOException; void handleRead(SelectionKey key) throws IOException; void handleWrite(SelectionKey key) throws IOException;}public class NIOEchoServer { public static void main(String[] args) throws IOException { Dispatcher dispatcher = new Dispatcher(20007, new EchoProtocolEventHandler()); dispatcher.run(); }}
    53. 53. Netty!! from Netty Homepage The Netty project is an effort to provide anasynchronous event-driven network applicationframework and tools for rapid development ofmaintainable high performance & high scalability protocol servers & clients
    54. 54. Netty!! from Netty Homepage The Netty project is an effort to provide anasynchronous event-driven network applicationframework and tools for rapid development ofmaintainable high performance & high scalability protocol servers & clients
    55. 55. Netty!! from Netty Homepage The Netty project is an effort to provide anasynchronous event-driven application network framework and tools for rapid development of maintainable high performance & high scalability protocol servers & clients
    56. 56. Netty!! from Netty Homepage The Netty project is an effort to provide anasynchronous event-driven network applicationframework and tools for rapid development of high performance & high scalabilitymaintainable protocol servers & clients
    57. 57. Netty!! from Netty Homepage The Netty project is an effort to provide anasynchronous event-driven network application rapid developmentframework and tools for ofmaintainable high performance & high scalability protocol servers & clients
    58. 58. Netty!! from Netty Homepage The Netty project is an effort to provide an asynchronous event-driven network application framework and tools for rapid development of maintainable high performance & high scalability protocol servers & clientsWarning! Netty no es un WebServer
    59. 59. Netty Model • Channels creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler ✓ Representa a una conexion P2P (point to point) ✓ Abstrae UDP / TCP ✓ Abstrae NIO / OIO ✓ API interacción conexión (abrir, cerrar, escribir, leer)
    60. 60. Netty Model • ChannelBuffers creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler
    61. 61. Netty Model • ChannelBuffers creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler ✓ Abstrae de NIO Buffers & byte arrays ✓ API simplificada respecto NIO Buffers ✓ Toda lectura y escritura se hace con ellos
    62. 62. Netty Model • ChannelFactory creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler
    63. 63. Netty Model • ChannelFactory creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler ✓ Crear Channels ✓ ChannelFactory impl. para UDP / TCP y OIO / NIO ✓ Non Blocking con: NioServerSocketChannelFactory
    64. 64. Netty Model • ChannelEvent creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler
    65. 65. Netty Model • ChannelEvent creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler ✓ Representa un evento I/O asociado a un Channel ✓ Puede ser upstream (entrada) or downstream (salida) ✓ Se pueden crear eventos custom ✓ Algunos tipos de evento: ★ messageReceived ★ channelOpen ★ channelClosed ★ write
    66. 66. Netty Model • ChannelPipeline creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler
    67. 67. Netty Model • ChannelPipeline creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler ✓ Asociado a un Channel ✓ Atrapa y Handlea los ChannelEvents ✓ Implementa el patron Intercepting Filter
    68. 68. Netty Model • ChannelHandler creates handlesChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handlesChannelBuffer ChannelEvent ChannelHandler
    69. 69. Netty Model • ChannelHandler creates handles ChannelFactory Channel ChannelPipeline es with & writ generates is sorted list of s read handles ChannelBuffer ChannelEvent ChannelHandler ✓ Building block de un ChannelPipeline ✓ Handlea ChannelEvents (upstream y/o downstream) ✓ Decide sobre el forwarding de eventos a sus paresupstream Handler Handler Handler Handler event N -1 First Last 2nd downstream event
    70. 70. An Echo Server • Netty NIO Main Classpublic class EchoServer { public static void main(String[] args) throws Exception { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    71. 71. An Echo Server • Netty NIO Main Classpublic class EchoServer { Utility Class para public static void main(String[] args) throws Exception {inicializar un Server // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    72. 72. An Echo Server • Netty NIO Main Classpublic class EchoServer { public static void main(String[] args) throws Exception { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( ChannelFactory para Tcp new NioServerSocketChannelFactory( w/ NIO Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    73. 73. An Echo Server • Netty NIO Main Classpublic class EchoServer { public static void main(String[] args) throws Exception { // Configure the server. Boss pool thread ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); Workers pool thread // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    74. 74. An Echo Server • Netty NIO Main Class public class EchoServer { public static void main(String[] args) throws Exception { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { ChannelPipelineFactorypara cada nuevo Channel return Channels.pipeline(new EchoServerHandler()); establcido } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); } } Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    75. 75. An Echo Server • Netty NIO Main Classpublic class EchoServer { public static void main(String[] args) throws Exception { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); Un solo handler. El echo Handler // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    76. 76. An Echo Server • Netty NIO Main Classpublic class EchoServer { public static void main(String[] args) throws Exception { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( new NioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); }} Finalmente inicializamos el servidor Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    77. 77. An Echo Server • Netty NIO Handlerpublic class EchoServerHandler extends SimpleChannelHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { e.getChannel().write(e.getMessage()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { e.getCause().printStackTrace(); Channel ch = e.getChannel(); ch.close(); }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    78. 78. An Echo Server • Netty NIO Handler Utility class para handlearpublic class EchoServerHandler extends SimpleChannelHandler { eventos típicos @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { e.getChannel().write(e.getMessage()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { e.getCause().printStackTrace(); Channel ch = e.getChannel(); ch.close(); }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    79. 79. An Echo Server • Netty NIO Handler public class EchoServerHandler extends SimpleChannelHandler { @OverrideEvent Handler para public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { nuevo mensaje e.getChannel().write(e.getMessage()); } @OverrideEvent Handler para void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { public excepciones e.getCause().printStackTrace(); Channel ch = e.getChannel(); ch.close(); } } Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    80. 80. An Echo Server • Netty NIO Handlerpublic class EchoServerHandler extends SimpleChannelHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { e.getChannel().write(e.getMessage()); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { e.getCause().printStackTrace(); Contexto del Canal Channel ch = e.getChannel(); Permite interactuar con su ch.close(); pipeline }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    81. 81. An Echo Server • Netty NIO Handlerpublic class EchoServerHandler extends SimpleChannelHandler { @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { e.getChannel().write(e.getMessage()); } Interacción con el Channel: Lectura y Escritura @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { e.getCause().printStackTrace(); Channel ch = e.getChannel(); ch.close(); }} Ejemplo en: http://docs.jboss.org/netty/3.2/xref/org/jboss/netty/example/echo/package-summary.html
    82. 82. An Echo Server • Netty OIO Vuelta a OIO!public class EchoServer { public static void main(String[] args) throws Exception { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( new OioServerSocketChannelFactory( Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); }}
    83. 83. An Echo Server • Netty OIO Vuelta a OIO!public class EchoServer { public static void main(String[] args) throws Exception { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap( ChannelFactory para Tcp new OioServerSocketChannelFactory( w/ OIO Executors.newCachedThreadPool(), Executors.newCachedThreadPool())); // Set up the pipeline factory. bootstrap.setPipelineFactory(new ChannelPipelineFactory() { public ChannelPipeline getPipeline() throws Exception { return Channels.pipeline(new EchoServerHandler()); } }); // Bind and start to accept incoming connections. bootstrap.bind(new InetSocketAddress(8080)); }}
    84. 84. A Distributed Logger • DiseñoProtocolo✓ Formato textual, de linea✓ Paquete tamaño variable✓ Mensaje por fin de linea✓ Formato: “${level} - ${mensaje}” DEBUG - Algo esta mal!!
    85. 85. Framer HandlerDecoder Handler pipeline LoggerPrinter Handler A Distributed Logger • Diseño
    86. 86. Framer HandlerDecoder Handler LoggerPrinter Handler A Distributed Logger • Framer
    87. 87. A Distributed Logger • Framer Decoder Handler Framer Handler LoggerPrinter Handler Framerimport org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;import org.jboss.netty.handler.codec.frame.Delimiters;public class LoggerFramerHandler extends DelimiterBasedFrameDecoder { public LoggerFramerHandler() { super(8000, Delimiters.lineDelimiter()); }}
    88. 88. A Distributed Logger • Framer Decoder Handler Framer Handler LoggerPrinter Handler Framerimport org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;import org.jboss.netty.handler.codec.frame.Delimiters;public class LoggerFramerHandler extends DelimiterBasedFrameDecoder { public LoggerFramerHandler() { Framer para demarcar super(8000, Delimiters.lineDelimiter()); paquetes a través de un } delimitador}
    89. 89. A Distributed Logger • Framer Decoder Handler Framer Handler LoggerPrinter Handler Framerimport org.jboss.netty.handler.codec.frame.DelimiterBasedFrameDecoder;import org.jboss.netty.handler.codec.frame.Delimiters;public class LoggerFramerHandler extends DelimiterBasedFrameDecoder { public LoggerFramerHandler() { super(8000, Delimiters.lineDelimiter()); }} Delimitador para lineas
    90. 90. Framer HandlerDecoder Handler LoggerPrinter Handler A Distributed Logger • Framer
    91. 91. Framer HandlerDecoder Handler LoggerPrinter Handler A Distributed Logger • Decoder
    92. 92. A Distributed Logger • Decoder Decoder Handler Framer Handler LoggerPrinter Handler Decoder@Overridepublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { Charset ch = Charset.defaultCharset(); String msg = ((ChannelBuffer) e.getMessage()).toString(ch); String[] args = msg.split("-", 2); LogEvent logEvent = new LogEvent(Level.valueOf(args[0].trim()), args[1].trim()); Channels.fireMessageReceived(ctx, logEvent);}
    93. 93. A Distributed Logger • Decoder Decoder Handler Framer Handler LoggerPrinter Handler Decoder@Override El framer deja unpublic void messageReceived(ChannelHandlerContext ctx, MessageEventmsg { ChannelBuffer como e) Charset ch = Charset.defaultCharset(); String msg = ((ChannelBuffer) e.getMessage()).toString(ch); String[] args = msg.split("-", 2); LogEvent logEvent = new LogEvent(Level.valueOf(args[0].trim()), args[1].trim()); Channels.fireMessageReceived(ctx, logEvent);}
    94. 94. A Distributed Logger • Decoder Decoder Handler Framer Handler LoggerPrinter Handler Decoder@Overridepublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { Charset ch = Charset.defaultCharset(); String msg = ((ChannelBuffer) e.getMessage()).toString(ch); String[] args = msg.split("-", 2); parseo y creación de un LogEvent LogEvent logEvent = new LogEvent(Level.valueOf(args[0].trim()), args[1].trim()); Channels.fireMessageReceived(ctx, logEvent);}
    95. 95. A Distributed Logger • Decoder Decoder Handler Framer Handler LoggerPrinter Handler Decoder@Overridepublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { Charset ch = Charset.defaultCharset(); String msg = ((ChannelBuffer) e.getMessage()).toString(ch); String[] args = msg.split("-", 2); LogEvent logEvent = new LogEvent(Level.valueOf(args[0].trim()), args[1].trim()); Channels.fireMessageReceived(ctx, logEvent);} propago el mensaje, como LogEvent
    96. 96. Framer HandlerDecoder Handler LoggerPrinter Handler A Distributed Logger • Decoder
    97. 97. Framer HandlerDecoder Handler LoggerPrinter Handler A Distributed Logger • Printer
    98. 98. A Distributed Logger • Printer Decoder Handler Framer Handler LoggerPrinter Handler Printer@Overridepublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { LogEvent logEvent = (LogEvent) e.getMessage(); System.out.println(logEvent);}
    99. 99. A Distributed Logger • Printer Decoder Handler Framer Handler LoggerPrinter Handler Printer El msg ahora es un@Override LogEventpublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { LogEvent logEvent = (LogEvent) e.getMessage(); System.out.println(logEvent);}
    100. 100. A Distributed Logger • Printer Decoder Handler Framer Handler LoggerPrinter Handler Printer@Overridepublic void messageReceived(ChannelHandlerContext ctx, MessageEvent e) { LogEvent logEvent = (LogEvent) e.getMessage(); System.out.println(logEvent);}
    101. 101. Conclusiones✓ Non-Blocking IO ✓ Importa! ✓ Aplicaciones altamente concurrentes (c10k)✓ Netty ✓ java nio made easy ✓ modelo flexible para crear servidores NIO (o OIO)
    102. 102. ¿¿ Preguntas ??We are hiring! Apply at jobs@zaubersoftware.com

    ×