Пул потоков на веб-сервере в Java

1

У меня есть многопоточный веб-сервер, и теперь я хочу реализовать пул потоков, но даже после того, как посмотрел на него, я не понимаю, как я могу это сделать в своем коде :(

Может ли кто-нибудь помочь мне получить его лучше? Мне действительно нужно понять, как то, что я читаю, можно использовать здесь, потому что я не вижу связи и как это работает.

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;


public class WebServer {

    static class RequisicaoRunnable implements Runnable {

        private Socket socket;

        RequisicaoRunnable(Socket socket) {
            this.socket = socket;
        }

        @Override
        public void run() {
            try {
                //System.out.println("connection from " + socket.getInetAddress().getHostName());
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
                //System.out.println("READING SOCKET...");
                String str = in.readLine();
                String[] arr = str.split(" ");
                if (arr != null && arr.length > 2) {
                    while(!str.equals("")) {
                        //System.out.println(str);
                        str = in.readLine();
                    }
                    if (arr[0].equals("GET")) {
                        //System.out.println("REQUESTED RESOURCE: " + arr[1]);
                        String nomeArquivo = arr[1];
                        if (arr[1].startsWith("/")) {
                            nomeArquivo = nomeArquivo.substring(1);
                        }
                        if (nomeArquivo.equals("")) {
                            nomeArquivo = "index.html";
                        }
                        File f = new File(nomeArquivo);
                        if (f.exists()) {
                            FileInputStream fin = new FileInputStream(f);
                            socket.getOutputStream().write("HTTP/1.0 200 OK\n\n".getBytes());
                            byte[] buffer = new byte[1024];
                            int lidos;
                            do {
                                lidos = fin.read(buffer);
                                if (lidos > 0) {
                                    socket.getOutputStream().write(buffer, 0, lidos);
                                }
                            } while (lidos > 0);
                            fin.close();
                        } else {
                            socket.getOutputStream().write("HTTP/1.0 404 Not Found\n\n".getBytes());
                            socket.getOutputStream().write("<html><body>HTTP/1.0 404 File Not Found</body></html>\n\n".getBytes());
                        }
                    } else {
                        socket.getOutputStream().write("HTTP/1.0 501 Not Implemented\n\n".getBytes());
                    }
                }
                socket.close();
            } catch (IOException e) { }
        }
    }

    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("waiting connections....");
        while (true) {
            Socket socket = serverSocket.accept();
            RequisicaoRunnable req = new RequisicaoRunnable(socket);
            new Thread(req).start();
        }
    }

}
  • 0
    Есть ли причина, по которой вы не используете фреймворк для своего сервера? то есть весна
  • 0
    @tmanion в этом случае я только что сделал самый первый веб-сервер. Что ты предлагаешь? :)
Показать ещё 1 комментарий
Теги:
multithreading
webserver
pool

4 ответа

1
Лучший ответ

Идея за Thread pool заключается в том, что при запуске создается определенное количество потоков, а затем назначается им задача. Альтернативно удалять головную боль при создании потоков каждый раз. Несколько дней назад я реализовал его, вот что я сделал.

  1. Создайте несколько потоков при запуске, они разделяют очередь запросов
  2. Темы постоянно ищут очередь, и когда запрос приходит, один из потоков отправляет запрос и выполняет действие
  3. Очередь будет синхронизирована 3.

Вот некоторые методы очереди

Queue#add();    //add the socket at the end
Queue#removeFront();//remove socket
Queue#isEmpty();//boolean if queue is empty
Queue#size(); //return size of queue
Queue#getMaxSize();//get maximum allowed size for queue

Обработка запроса выполняется

public class Processor implements Runnable {

    private Queue<Socket> requests;
    private boolean shut;

    Processor(Queue<Socket> requests) {
        this.requests = requests;
        shut = false;
    }

    @Override
    public void run() {
        while(!shut) {
            if(requests.isEmpty()) {
                try{
                    Thread.sleep(#rendomeTimemill);
                } catch(InterruptedException e){}
            }else {
                Socket skt = Queue.removeFront();
                try {
                    //System.out.println("processing request from " + socket.getInetAddress().getHostName());
                    //do you want
                } catch (Exception e) {
                } finally {
                    if(skt != null) {
                        try{ skt.close(); skt = null; } catch(IOException ex){}

                    }
                }
            }
        }
    }
    public void stopNow() {
        shut = true;
        Thread.interrupt();
    }
}

в вашем основном потоке создайте очередь для запросов

//start your server socket
Queue<Socket> requests = new Queue<Socket>();

Начать пул рабочих потоков

Precessor []workers = new Processor[NUM_WORKER];
for(int i=0;i<NUM_WORKER; i++) {
    worker[i] = new Processor(requests);
    Thread th = new Thread(worker[i]);
    th.strat();
}

в запросе прослушивания

//while loope that run forever
// accept socket
    if(requests.size() == requests.getMaxSize()) {
        socket.getOutputStream().write("HTTP/1.0 505 Error\n\n".getBytes());
        socket.getOutputStream().write("<html><body>Try again</body></html>\n\n".getBytes());
        socket.close();
    } else {
            requests.add(socket);
    }

когда вы хотите выкрикивать сервер

for(int i=0;i<NUM_WORKER; i++) {
    worker[i].stopNow();
}

Примечание. Меня беспокоило не HTTP-заголовки, поэтому я неспецифичен, но вы должны реализовать полный HTTP-заголовок, например Content-type, Content-length и т.д.

  • 0
    у потребителя тоже будет что-то особенное? Или я беспокоюсь только о продюсере?
  • 0
    Потребитель @ShiroyamaCah будет обслуживать требуемый запрос (прочитайте и запишите эту страницу в outputstream . См. processor class .
1

JDK может быть хорошим местом для запуска Исполнителя или ExecutorService, это то, что вы ищете. Материал для чтения: http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html

Примеры в нем довольно полные, я думаю, но вот пример использования кода, который вы опубликовали:

public static void main(String[] args) throws IOException {




ServerSocket serverSocket = new ServerSocket(8080);
    System.out.println("waiting connections....");

    ExecutorService pool = Executors.newCachedThreadPool();

   while (true) {
        Socket socket = serverSocket.accept();
        RequisicaoRunnable req = new RequisicaoRunnable(socket);
        pool.execute(req);
    }
}

Мы создаем службу исполнителя, которая поддерживается пулом кэшированных потоков. Вы можете изменить это для любого типа пула, который вам нравится, изменив тип службы-исполнителя, которую вы получаете от Исполнителей: http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors. HTML

В приведенном ниже примере мы используем пул кэшированных потоков, который должен создавать новые потоки по мере необходимости, но использовать старые, когда они становятся доступными (завершайте все, что они выполняли). Если вы просматриваете методы, предоставляемые в этом классе, вы можете создавать службы Executor, которые поддерживаются различными типами пулов потоков, например, один поток, фиксированное количество потоков и т.д.

Пример выше должен работать так, как есть, но если вы хотите изменить способ работы пула потоков, попробуйте использовать другой пул потоков. Пул кэшированных потоков будет означать, что каждое соединение будет немедленно обслуживаться, однако оно может создать неограниченное количество потоков.

с другой стороны, если вы хотите, чтобы исполнитель использовал блокирующую очередь, как было предложено fge, вы можете попробовать создать пул фиксированных потоков:

Executors.newFixedThreadPool(x)

вы получаете бесплатную блокировку.

  • 0
    Извините за вопрос, потому что java - мой второй язык программирования и все еще новый: в этом случае у меня все еще было бы больше функций исполнителя для применения в моем коде, чтобы он работал так, как я хочу, верно? Я понял, что newCachedThreadPool () поможет с использованием и повторным использованием потоков, что уже хорошо для меня, но есть ли у вас предпочтения? Как .... исполнитель, который вы бы посоветовали как лучший в моем случае?
  • 0
    Я думаю, что этот пример должен работать как есть. если у вас есть больше классов, реализующих Runnable, вы можете выполнить их в том же Executor или создать другой, если вам нужно больше пулов потоков. добавлена заметка об использовании очереди.
1

Вы можете использовать, например, BlockingQueue. Это является основой для сценария производителя/потребителя.

В твоем случае:

  • производитель имеет гнездо сервера; он принимает новые клиентские сокеты и толкает клиентские сокеты в очередь;
  • потребители захватывают клиентские сокеты из очереди и запросов процесса.

Помимо всего этого, вы также можете использовать ограниченную очередь; вы можете попробовать и нажать новый клиентский сокет в очередь; если очередь заполнена, вы можете по умолчанию использовать "no can not do".

Сценариев много. Нет ответа.

  • 0
    У меня уже проделана работа с продюсером / потребителем ( drive.google.com/file/d/0ByARXuWndnxMZ1k5Y0NEcGFtSnc/… ), однако я очень плохо разбираюсь в быстром программировании, поэтому я обычно много изучаю и прошу многое компенсировать. Верите ли вы, что то, что у меня есть от производителя / потребителя, может работать в моей ситуации?
  • 0
    Имхо, вы не делаете это правильно. Прочитайте мой пост еще раз внимательно. Классы вашего производителя и потребителя должны быть разделены; но они должны иметь общую очередь.
0

Хорошо, идея достаточно проста. В настоящий момент основной цикл создает новый объект RequisicaoRunnable и новый поток для его запуска каждый раз, когда он получает соединение от клиента. Идея пула потоков состоит в том, чтобы каждый раз создавать новые потоки.

В простейшей версии пула потоков вы создаете блокирующую очередь, и вы создаете и запускаете фиксированное количество рабочих потоков, прежде чем вводить основной цикл. Основной цикл будет выглядеть почти так же, как и у вас сейчас, но вместо запуска Thread для запуска каждого нового RequisicaoRunnable он просто добавит новый объект в очередь.

Ваши рабочие потоки одинаковы: while (! ShutdownHasBeenRequested()) {RequisicaoRunnable requisicaoRunnable = getATaskFromTheQueue(); requisicaoRunnable.run(); requisicaoRunnable.run(); } }

Таким образом, каждая новая задача (клиент) будет выполняться (обрабатываться) следующим доступным потоком из вашего пула.

Если это домашнее задание, вы в значительной степени захотите реализовать то, что я описал, заполнив некоторые детали по мере необходимости.

Если это не домашнее задание, тогда рассмотрите возможность использования java.util.concurrent.ThreadPoolExcecutor(). Нет смысла повторно изобретать колесо, когда там есть совершенно хорошее колесо, ожидающее его использования.


Изменение: как сказал fge, одним из улучшений было бы отсылать ответ "извините, попробуйте еще раз позже", когда новые соединения будут быстрее, чем вы можете их обработать. Когда в очереди слишком много ожидающих подключений (т.е. Когда вы достигаете предела BoundedQueue), это когда вы знаете, чтобы выручить и отправить ответ "попробуйте еще раз позже".

  • 0
    Моя основная проблема в программировании заключается в том, что я не тороплюсь с идеей, поэтому я много исследую и много спрашиваю. Java - второй язык программирования, который у меня есть, и он все еще довольно новый для меня, так что извините, если то, что я собираюсь спросить, довольно очевидно: как будет работать создание очереди блокировки? Я видел это, но я не вижу, как я мог сделать это в моем коде.
  • 0
    Стандартные библиотеки Java имеют открытый исходный код, что означает, что вы можете получить исходный код и прочитать его. Взгляните на java.util.concurrent.ArrayBlockingQueue или LinkedBlockingQueue, например.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню