tags:

views:

71

answers:

3

i have a server at the moment which makes a new thread for every user connected but after about 6 people are on the server for more than 15 mins it tends to flop and give me java heap out of memory error i have 1 thread that checks with a mysql database every 30 seconds to see if any of the users currently logged on have any new messages. what would be the easiest way to implement a server queue?

this is the my main method for my server:

public class Server {

    public static int MaxUsers = 1000;
    //public static PrintStream[] sessions = new PrintStream[MaxUsers];
    public static ObjectOutputStream[] sessions = new ObjectOutputStream[MaxUsers];
    public static ObjectInputStream[] ois = new ObjectInputStream[MaxUsers];
    private static int port = 6283;
   public static Connection conn;
       static Toolkit toolkit;
    static Timer timer;

    public static void main(String[] args) {
        try {
            conn = (Connection) Mysql.getConnection();
        } catch (Exception ex) {
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
        System.out.println("****************************************************");
        System.out.println("*                                                  *");
        System.out.println("*                    Cloud Server                  *");
        System.out.println("*                       ©2010                      *");
        System.out.println("*                                                  *");
        System.out.println("*                   Luke Houlahan                  *");
        System.out.println("*                                                  *");
        System.out.println("* Server Online                                    *");
        System.out.println("* Listening On Port " + port + "                           *");
        System.out.println("*                                                  *");
        System.out.println("****************************************************");
        System.out.println("");
        mailChecker();
        try {
            int i;
            ServerSocket s = new ServerSocket(port);
            for (i = 0; i < MaxUsers; ++i) {
                sessions[i] = null;
            }
            while (true) {
                try {
                    Socket incoming = s.accept();                    
                    boolean found = false;
                    int numusers = 0;
                    int usernum = -1;
                    synchronized (sessions) {
                        for (i = 0; i < MaxUsers; ++i) {
                            if (sessions[i] == null) {
                                if (!found) {
                                    sessions[i] = new ObjectOutputStream(incoming.getOutputStream());
                                    ois[i]= new ObjectInputStream(incoming.getInputStream());
                                    new SocketHandler(incoming, i).start();
                                    found = true;
                                    usernum = i;
                                }
                            } else {
                                numusers++;
                            }
                        }
                        if (!found) {
                            ObjectOutputStream temp = new ObjectOutputStream(incoming.getOutputStream());
                            Person tempperson = new Person();
                            tempperson.setFlagField(100);
                            temp.writeObject(tempperson);
                            temp.flush();
                            temp = null;
                            tempperson = null;
                            incoming.close();
                        } else {
                        }
                    }
                } catch (IOException ex) {
                    System.out.println(1);
                    Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        } catch (IOException ex) {
            System.out.println(2);
            Logger.getLogger(Server.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
        public static void mailChecker() {
        toolkit = Toolkit.getDefaultToolkit();
        timer = new Timer();
        timer.schedule(new mailCheck(), 0, 10 * 1000);
    }
}
A: 

You should check out Java NIO for building scalable servers

dty
NIO just gives you non-blocking I/O, it's not a magic scalability bullet.
skaffman
Can you still use object input and output streams with java nio? I have put many hours in to this client and server and don't really want to start again :(
It's not a magic bullet, but you're going to struggle to build anything scalable if you're using the thread-per-client model.No, I don't believe you can use object streams easily with NIO. I have a vague recollection of a library that would let you do that, but I can't remember the details.Perhaps you'd be better suited with RMI?
dty
The likes of Apache Mina or JBoss Netty are frameworks to build this sort of thing without doing it all from scratch.
skaffman
Basicly what I'm trying to achieve in the long run is a social netowking application that has live connections with instant messanging mail messanging and all the bells and whistles
regarding "putting in many hours". If you start with a design decision based on insufficient facts and/or experience you will end up putting "many, many, _MANY_ hours" in it. The trick is knowing when to drop some the code, and being able to refactor the rest into the replacement code.
Thorbjørn Ravn Andersen
+1  A: 

It seems like you have a memory leak. 6 threads is not much. I suspect it is because ObjectInputStream and ObjectOutputStream cache all the objects transmitted. This makes them quite unsuitable for long transfers. You think you are sending an object that is then gc'ed, but it's really being held in memory by the object streams.

To flush the streams cache, use

  objectOutputStream.reset()

after writing your objects with writeObject()

EDIT: To get thread pooling, the SocketHandler can be passed to an Executor instead of starting it's own thread. You create an executor like:

Executor executor = Executors.newFiexThreadPool(MaxUsers);

The executor is created as a field, or at the same level as the server socket. Then after accepting a connection you add the SocketHandler to the executor:

executor.execute(new SocketHandler(...));

However, if your clients are long lived, then this will make little improvement, since the thread startup time is small compared to the amount of work done on each thread. Pools are most effective for executing many small tasks, rather than a few large ones.

As to making the server more robust - some quick hints

  • ensure it is started with sufficient memory, or at least that the maximum memory is set to anticipate the need of 1000 users.
  • use a load test framework, such as Apache JMeter to verify it will scale to the maximum number of users.
  • use a connection pool for your database, and don't hand-code JDBC calls - use an established framework, e.g. Spring JDBC.
  • Each thread starts with 2MB stack by default. So, if you have 1000 users, then that will use ~2GB of virtual process space just for the stack. ON many 32-bit systems, this is the amount of user space you can have, so there will be no room for data. If you need more users, then either scale out to more processes, with a load balancer passing requests to each process, or look at server solutions that do not require a thread per connection.
  • attention to detail, particularly exception handling.
  • logging, for diagnosing failures.
  • JMX or other managability to monitor server health, with notification to you when values go out of bounds (e.g. memory/cpu use too high for a long period, or request time slow.)

See Architecture of a Highly Scalable Server

mdma
@mdma: Bingo! Good catch.@houlahan: You do have a memory leak in the way you're using the `ObjectInputStream` and `ObjectOutputStream` classes.
ssahmed555
Still if I'm wanting thousands of connections to this server making threads is expencive :( need something more robust but still be able to sent objects backwards and forwards to perticular clients I've looked around but I carnt find alot of information on robust java servers
thank you mdma! that's exactly the information i have been looking for i am gonna look in to using Apache JMeter and spring JDBC :)
A: 

I would focus attention on why you are running out of heap space, or rather why your program gets an OOM error when 6 connections have been open for some time. Your server should be able to scale to at least many more simultaneous concurrent connections, but it's hard to quantify that number without getting more details about your environment, HW, etc.

You've only posted the main method for your server so it's hard to tell if there are memory leaks, resource leaks, etc. that might be causing you to run out of heap space. Are you running your server with the default heap settings? If so, you might want to try increasing your heap size, as the defaults are quite conservative.

Romain is correct: you should be closing your stream resources in a try { ... } finally { ... } block to make sure you are not leaking resources.

Lastly, you might want to consider passing the backlog parameter to the ServerSocket constructor. This specifies the maximum queue size for incoming connections to that ServerSocket, after which any new connections are refused. But first you still need to figure out why your server cannot handle more than 6 connections.

ssahmed555
I think it is as mdma said about me not resetting the stream I'm currently away from my computer at the moment so I carnt check :/ I will upload the rest of the server when I get home in the morning