Java: создание простого масштабирования сервера! Часть 2
В предыдущей статье мы подробно рассмотрели и создали сервер, который позволял пользователю запускать его и создавать к нему соединение. Затем наш сервер будет выводить все, что ввел пользователь. В этой статье мы будем использовать многопоточность, чтобы гарантировать, что наш сервер может обрабатывать несколько подключений к нему. На самом деле так работают настоящие серверы. Каждый раз, когда добавляется новое соединение, он порождает новый поток в этом порту для обработки входящих запросов. Эта статья будет короткой, так как она просто изменяет наш старый код.
1. Добавление многопоточности
1.1 Установка нашего лимита подключений
В нашем новом connectToServerMultiple()
методе (да, я плохо разбираюсь в именовании методов). Начнем с того, что укажем максимальное количество подключений, которое должен создать наш сервер. В EC-1 ниже мы просто установили для него значение 3, однако реальный сервер должен иметь возможность обрабатывать более тысячи соединений. Помните, что каждое новое соединение будет влиять на память + ЦП на вашем сервере. Мы также устанавливаем для нашего порта произвольный 8187, как показано в Пример кода 1 (EC-1) ниже.
Как и в нашей предыдущей статье, мы создаем сокет и помещаем его в try-catch, чтобы избежать ошибок.
EC-1: Stating our max connections public static void connectToServerMultiple() { int max_con = 3; //indicate maximum number of connections. int port = 8187; //Create multiple ServerSockets to connect to a server ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(port); } catch (IOException e) { e.printStackTrace(); }
1.2 Создание наших потоков
Мы указали необходимое количество подключений, все, что нам нужно сделать сейчас, это создать цикл for, который создаст новый поток, который выполняет следующие
- Создайте новый сокет для каждого подключения
- Слушает соединение.
EC-2 - Creating the ForLoop & Threads //Create a for loop that creates max_con number of threads for (int i = 0; i < max_con; i++) { ServerSocket finalServerSocket = serverSocket; //Create the thread Runnable runnable = () -> { try { Socket listenerSocket = finalServerSocket.accept(); InputStream inputToServer = listenerSocket.getInputStream(); OutputStream outputFromServer = listenerSocket.getOutputStream();
2. Добавление функциональности на наш сервер
В нашей предыдущей статье наш сервер просто принимал вводимые пользователем данные и выводил именно то, что ему было сказано. В этом примере мы сделаем наш сервер немного более полезным, позволив ему принимать любые числа и умножив их на 10.
Большая часть кода аналогична предыдущей статье, мы создаем Scanner
, который принимает входные данные нашего клиента и отправляет их на сервер, а также мы создаем PrintWriter
, который принимает выходные данные с сервера и отправляет их клиенту.
EC-3 | Adding functionality to the server //...continues from EC-2 Scanner input = new Scanner(inputToServer, "UTF-8"); PrintWriter printWriter = new PrintWriter(new OutputStreamWriter(outputFromServer, "UTF-8"), true); printWriter.println("Welcome Minion! I'll multiply the number you give by 10.\n Type -19 to quit"); printWriter.println("I'm Running on Thread: " + Thread.currentThread().getName()); boolean done = false; while(!done && input.hasNextLine()) { String line = input.nextLine(); double inputDouble = 0; try{ inputDouble = Double.parseDouble(line); if(line == null || inputDouble == -19) { done = true; printWriter.println("Sad to see you leave! ... Closing Connection!"); listenerSocket.close(); //close socket } printWriter.println("Your answer is: " + inputDouble * 10); }catch (Exception ex) { printWriter.println(":{( - Only insert numbers!!!"); } } } catch (IOException e) { e.printStackTrace(); } }; //Execute the runnables! Executors.newCachedThreadPool().execute(runnable);
Если пользователь вводит магическое число -19, это сигнализирует серверу, что пользователь хочет выйти, и закрывает сокет, разрывая соединение.
3. Собираем все вместе
Итак, мы добавили на наш сервер параллелизм с помощью цикла for, а также сделали наш сервер немного более полезным. Пришло время просмотреть весь код и запустить его! Скопируйте приведенную ниже суть в свою любимую Java IDE и запустите ее.
В вашем терминале используйте telnet, программу, которую мы загрузили в прошлый раз, и направьте ее на ваш работающий сервер. У вас должен получиться следующий результат.
На рисунке 1 выше я создал 4 подключения к работающему серверу через порт 8187. Обратите внимание, что на самом деле должны подключаться только 3, поскольку мы установили наш предел 3. Соединение в правом нижнем углу (BR) останавливается, а в верхнем левом (TL). , верхний правый (TR), нижний левый (BL) все соединяются. Также интересно то, что код распечатывает, в каком потоке запущен сокет. Это говорит о том, что наша многопоточность работает в полную силу. Что касается функциональности нашего сервера, TL, TR, BL, похоже, работают, как ожидалось, умножая ввод на 10. На TR мы закрываем соединение, вводя наш пресловутый -19, и сокет закрывается. Также обратите внимание, как только TR завершает свое соединение, BR не сразу занимает пространство этого соединения.
Поиграйте с кодом и различными сценариями и получайте удовольствие!
В следующей статье мы развернем наш сервер на реальном сервере в Google Cloud Compute Engine и подключимся к нему оттуда.
Спасибо за прочтение!
Twitter - @martinomburajr