Monday, August 19, 2013

The Java NIO advantage



The challenge was to create a SIP User Agent with support for 50 Calls per second. The current implementation used UDP and java io api’s.  The implementation worked seamlessly for 10CPS from SIPP once we reached above 10CPS there was packet loss in the UDP Datagram socket. We change the code to support worker threads but still the loss was there. This prompted us to look for using other methods like queuing or   JMS.  I came across a article published by Jakob Jenkov on IO V/s NIO http://java.dzone.com/articles/java-nio-vs-io  and the tutorial provided by http://tutorials.jenkov.com/java-nio/index.html
Java IO V/s Java NIO

The main difference between Java IO and Java NIO is
IO
NIO
Stream oriented
Buffer oriented
Blocking IO
Non blocking IO

Selectors

The biggest difference is Java IO is stream based and NIO is buffer oriented. Java io being stream oriented can read one byte at a time . They are not cached. Hence we need to cache it in a buffer first.  Java NIO's being buffer oriented the approach different. Data is read into a buffer from which it can be processed later. You can move forth and back in the buffer as you need to. This gives you a bit more flexibility during processing. However, you also need to check if the buffer contains all the data you need in order to fully process it and, you need to make sure that when reading more data into the buffer, you do not overwrite data in the buffer you have not yet processed.
Java io is blocking which means the thread that read () or write () block until there is some data to read or write. When large chunk of messages are pumped into a UDP socket by the time worker thread reads the stream it is blocked which cause packet loss

Java IO UDP Datagram Server
UDPServer.Java

private   final int MAX_PACKET_SIZE = 2048;
private   final byte[] MAX_PACKET_ARRAY = new byte[MAX_PACKET_SIZE];
public void run()
                {
                                try
                                {
                  String value = new String();
                  rxSocket = new DatagramSocket(8080, InetAddress.getByName(localhost));
                  DatagramPacket incomingPacket = new DatagramPacket(MAX_PACKET_ARRAY,                                            MAX_PACKET_ARRAY.length);
                                               
                  while (true)
                  {
                      rxSocket.receive(incomingPacket);
                      if(incomingPacket.getLength()>0){
                        value = new String(incomingPacket.getData(), 0, incomingPacket.getLength());
                        logger.info("Message from client - "+value);
                        ProcessClientMessages pi=new ProcessClientMessages(value,sippclientthread,serverThread,serverinfoThread,incomingPacket.getAddress().getHostAddress(),incomingPacket.getPort());
                        tpes.submit(pi);
                      } 
                    }
                }catch (Exception ex) {
                                                ex.printStackTrace();
                                                logger.error("Callconnector Exception:" ,ex);
                                }
                }

The above program is to recieve the UDP packet using Java IO.


  
Java NIO UDP Server
UDPServer.Java

static int i=0;
public void run()
                {
                                try
                                {
                      String value = new String();
                          DatagramChannel channel = DatagramChannel.open( );
                          DatagramSocket socket = channel.socket( );
                          SocketAddress address = new InetSocketAddress("8080");
                          socket.bind(address);
                          ByteBuffer buffer = ByteBuffer.allocateDirect(65507);
                          byte byteArray[]=new byte[4096];
                  while (true)
                  {
                    SocketAddress client = channel.receive(buffer);
                            buffer.flip();
                            ipport=GeneralManipulation.getHostPort(client);
                            String fromIp=ipport[0];
                            String fromport=ipport[1];
                            while (buffer.hasRemaining( ))
                            { 
                                byteArray[i]=buffer.get();  
                                i=i+1;
                            }
                            i=0;
                            buffer.clear( );
                            value=new String(byteArray);
                    logger.info("str from client - "+value);
                    ProcessClientMessages pi=new ProcessClientMessages(value,sippclientthread,serverThread,serverinfoThread,fromIp,Integer.parseInt(fromport));
                    tpes.submit(pi);
                  }
                }catch (Exception ex) {
                                                ex.printStackTrace();
                                                logger.error("Callconnector Exception:" ,ex);
                                }
                }
The above is the code with java nio channel api that recieves messages on port 8080 and puts it in to the buffer and gets value from it and processes it. ByteBuffer : we allocate size to the bytebuffer and the channel which gets value will be in the buffer., (channel.receive(buffer);) then we iterate the buffer and get value from it.

Summary

NIO helps you to manage multiple channels using a single thread. If you need to connect multiple connections simultaneously using a single thread to manage all outbound connections implementing NIO server is probably an advantage  

1 comment :

  1. Boss, can I simply know the difference between stream and buffer.

    ReplyDelete