У меня есть многопоточный веб-сервер, и теперь я хочу реализовать пул потоков, но даже после того, как посмотрел на него, я не понимаю, как я могу это сделать в своем коде :(
Может ли кто-нибудь помочь мне получить его лучше? Мне действительно нужно понять, как то, что я читаю, можно использовать здесь, потому что я не вижу связи и как это работает.
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();
}
}
}
Идея за Thread pool
заключается в том, что при запуске создается определенное количество потоков, а затем назначается им задача. Альтернативно удалять головную боль при создании потоков каждый раз. Несколько дней назад я реализовал его, вот что я сделал.
Вот некоторые методы очереди
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 и т.д.
outputstream
. См. processor class
.
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)
вы получаете бесплатную блокировку.
Вы можете использовать, например, BlockingQueue
. Это является основой для сценария производителя/потребителя.
В твоем случае:
Помимо всего этого, вы также можете использовать ограниченную очередь; вы можете попробовать и нажать новый клиентский сокет в очередь; если очередь заполнена, вы можете по умолчанию использовать "no can not do".
Сценариев много. Нет ответа.
Хорошо, идея достаточно проста. В настоящий момент основной цикл создает новый объект RequisicaoRunnable и новый поток для его запуска каждый раз, когда он получает соединение от клиента. Идея пула потоков состоит в том, чтобы каждый раз создавать новые потоки.
В простейшей версии пула потоков вы создаете блокирующую очередь, и вы создаете и запускаете фиксированное количество рабочих потоков, прежде чем вводить основной цикл. Основной цикл будет выглядеть почти так же, как и у вас сейчас, но вместо запуска Thread для запуска каждого нового RequisicaoRunnable он просто добавит новый объект в очередь.
Ваши рабочие потоки одинаковы: while (! ShutdownHasBeenRequested()) {RequisicaoRunnable requisicaoRunnable = getATaskFromTheQueue(); requisicaoRunnable.run(); requisicaoRunnable.run(); } }
Таким образом, каждая новая задача (клиент) будет выполняться (обрабатываться) следующим доступным потоком из вашего пула.
Если это домашнее задание, вы в значительной степени захотите реализовать то, что я описал, заполнив некоторые детали по мере необходимости.
Если это не домашнее задание, тогда рассмотрите возможность использования java.util.concurrent.ThreadPoolExcecutor(). Нет смысла повторно изобретать колесо, когда там есть совершенно хорошее колесо, ожидающее его использования.
Изменение: как сказал fge, одним из улучшений было бы отсылать ответ "извините, попробуйте еще раз позже", когда новые соединения будут быстрее, чем вы можете их обработать. Когда в очереди слишком много ожидающих подключений (т.е. Когда вы достигаете предела BoundedQueue), это когда вы знаете, чтобы выручить и отправить ответ "попробуйте еще раз позже".