制作服务器队列java的最简单,最好的方法

时间:2010-06-09 21:52:08

标签: java sockets

我现在有一台服务器为每个连接的用户创建一个新线程,但是大约6个人在服务器上超过15分钟后,它往往会失败并给我java堆内存错误我有1个线程每隔30秒检查一次mysql数据库,看看当前登录的用户是否有任何新消息。什么是最简单的实现服务器队列的方法?

这是我服务器的主要方法:

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);
    }
}

3 个答案:

答案 0 :(得分:1)

好像你有内存泄漏。 6个线程并不多。我怀疑是因为ObjectInputStream和ObjectOutputStream缓存了所有传输的对象。这使得它们非常不适合长时间转移。你认为你正在发送一个然后是gc'ed的对象,但它实际上是由对象流保存在内存中。

要刷新流缓存,请使用

  objectOutputStream.reset()
writeObject()

编写对象后

编辑: 要获得线程池,可以将SocketHandler传递给Executor,而不是启动它自己的线程。您可以创建一个执行程序,如:

Executor executor = Executors.newFiexThreadPool(MaxUsers);

执行程序创建为字段,或与服务器套接字处于同一级别。然后 接受您将SocketHandler添加到执行程序的连接:

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

但是,如果您的客户长寿,那么这几乎没什么改进,因为与每个线程完成的工作量相比,线程启动时间很短。池对于执行许多小任务最有效,而不是几个大任务。

关于使服务器更健壮 - 一些快速提示

  • 确保以足够的内存启动它,或者至少设置最大内存以预计需要1000个用户。
  • 使用负载测试框架(例如Apache JMeter)来验证它是否会扩展到最大用户数。
  • 为数据库使用连接池,不要手动编写JDBC调用 - 使用已建立的框架,例如: Spring JDBC。
  • 默认情况下,每个线程以2MB堆栈开头。因此,如果您有1000个用户,那么将仅为堆栈使用~2GB的虚拟进程空间。在许多32位系统上,这是您可以拥有的用户空间量,因此没有数据空间。如果您需要更多用户,则可以扩展到更多进程,使用负载均衡器将请求传递给每个进程,或查看每个连接不需要线程的服务器解决方案。
  • 注重细节,特别是异常处理。
  • 记录,用于诊断故障。
  • JMX或其他监控服务器运行状况的可管理性,当值超出范围时通知您(例如,内存/ CPU长时间使用太高,或请求时间太慢。)

请参阅Architecture of a Highly Scalable Server

答案 1 :(得分:0)

您应该查看Java NIO以构建可伸缩服务器

答案 2 :(得分:0)

我会集中注意你为什么要用完堆空间,或者更确切地说,为什么你的程序在6个连接打开一段时间后会出现OOM错误。您的服务器应该能够扩展到至少更多的并发并发连接,但是如果没有获得有关您的环境,硬件等的更多详细信息,很难量化该数字。

您只发布了服务器的主要方法,因此很难判断是否存在可能导致堆空间不足的内存泄漏,资源泄漏等问题。您是否使用默认堆设置运行服务器?如果是这样,您可能想尝试增加堆大小,因为默认值非常保守。

Romain是正确的:您应该关闭try { ... } finally { ... }块中的流资源,以确保不会泄漏资源。

最后,您可能需要考虑将backlog参数传递给ServerSocket constructor。这指定了到该ServerSocket的传入连接的最大队列大小,之后拒绝任何新连接。但首先,您仍需要弄清楚为什么您的服务器无法处理超过6个连接。