Самый простой и лучший способ сделать серверную очередь Java - PullRequest
0 голосов
/ 10 июня 2010

В настоящее время у меня есть сервер, который создает новый поток для каждого подключенного пользователя, но после того, как около 6 человек находятся на сервере в течение более 15 минут, он имеет тенденцию к провалу и дает мне ошибку Java из-за ошибки памяти, у меня есть 1 поток это проверяет базу данных mysql каждые 30 секунд, чтобы узнать, есть ли у новых пользователей, вошедших в систему, новые сообщения. Какой самый простой способ реализовать очередь на сервере?

это мой основной метод для моего сервера:

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 ]

1 голос
/ 10 июня 2010

Кажется, у вас утечка памяти.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.
  • Каждый поток начинается со стека 2 МБ по умолчанию.Итак, если у вас 1000 пользователей, то для стека будет использовано ~ 2 ГБ виртуального пространства процесса.На многих 32-битных системах это количество пространства, которое вы можете иметь, поэтому места для данных не будет.Если вам нужно больше пользователей, то либо увеличьте масштаб до большего числа процессов, с помощью балансировщика нагрузки, передающего запросы каждому процессу, либо посмотрите на серверные решения, для которых не требуется поток на соединение.
  • внимание к деталям, особенно исключениеобработка.
  • регистрация, для диагностики сбоев.
  • JMX или другая возможность контроля состояния сервера, с уведомлением, когда значения выходят за пределы (например, использование памяти / процессора слишком долго в течение длительного времени).период или медленное время запроса.)

См. Архитектура высокопроизводительного сервера

0 голосов
/ 10 июня 2010

Я бы сосредоточил внимание на том, почему вам не хватает места в куче, или, скорее, почему ваша программа получает ошибку OOM, когда в течение некоторого времени открыто 6 соединений. Ваш сервер должен быть способен масштабироваться как минимум до множества одновременных одновременных подключений, но это число трудно определить количественно, не получая более подробную информацию о вашей среде, HW и т. Д.

Вы опубликовали только основной метод для своего сервера, поэтому трудно сказать, есть ли утечки памяти, утечки ресурсов и т. Д., Которые могут приводить к тому, что вам не хватает места в куче. Вы используете свой сервер с настройками кучи по умолчанию? Если это так, вы можете попробовать увеличить размер кучи, поскольку значения по умолчанию довольно консервативны.

Ромен верен: вы должны закрывать свои потоковые ресурсы в блоке try { ... } finally { ... }, чтобы убедиться, что вы не пропускаете ресурсы.

Наконец, вы можете рассмотреть возможность передачи параметра backlog в Конструктор ServerSocket . Указывает максимальный размер очереди для входящих подключений к этому ServerSocket, после чего любые новые подключения отклоняются. Но сначала вам нужно выяснить, почему ваш сервер не может обрабатывать более 6 соединений.

0 голосов
/ 10 июня 2010

Вам следует проверить Java NIO для построения масштабируемых серверов

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...