简体   繁体   English

Java中Web服务器中的线程池

[英]Thread pool in a web server in Java

I have my multithread web server and now i wish to implement a thread pool, however even after looking about it i don't get how can i do it in my code :( 我有我的多线程Web服务器,现在我希望实现一个线程池,但是即使在查看它之后,我也无法在我的代码中找到它:(

Could someone help me get it better? 有人可以帮我更好吗? I really need to understand how what i read can be used here, because i don't see the connection and how that works. 我真的需要了解如何在这里使用阅读的内容,因为我看不到连接及其工作原理。

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

}

JDK might be a good place to start An Executor or ExecutorService should is what you're looking for. JDK可能是启动Executor或ExecutorService的理想场所。 Reading material: http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html 阅读材料: http : //docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html

The examples in there are pretty complete I think, but here's an example using the code you posted: 我认为其中的示例相当完整,但这是使用您发布的代码的示例:

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

We create an executor service that is backed by a cached thread pool. 我们创建由缓存线程池支持的执行程序服务。 You can swap this out for any type of pool you like by changing the type of executor service you get from Executors: http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors.html 您可以通过更改从执行程序获得的执行程序服务的类型,将其交换为所需的任何类型的池: http : //docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executors。 html

In the example I've given we use a cached thread pool which should create new threads as needed but re use old ones as they become available (finish whatever they were executing). 在我给出的示例中,我们使用了一个高速缓存的线程池,该线程池应根据需要创建新线程,但在可用时重新使用旧线程(完成所有正在执行的操作)。 If you look through the methods provided in that class you can create Executor services that are backed by various types of thread pool eg single thread, fixed number of threads, etc. 如果查看该类中提供的方法,则可以创建由各种类型的线程池支持的Executor服务,例如单线程,固定数量的线程等。

The example above should work as is, but if you want to change how the thread pool works try another thread pool type. 上面的示例应按原样工作,但是如果您想更改线程池的工作方式,请尝试其他线程池类型。 The cached thread pool will mean each connection will immediately be serviced, however it can create an unbounded number of threads. 缓存的线程池将意味着每个连接将立即得到服务,但是它可以创建无限数量的线程。

on the other hand if you wanted the executor to use a blocking queue as suggested by fge you could try a fixed thread pool instead: 另一方面,如果您希望执行程序按照fge的建议使用阻塞队列,则可以尝试使用固定线程池:

Executors.newFixedThreadPool(x)

you get the blocking queue for free with that. 您可以免费获得阻塞队列。

You can use, for instance, a BlockingQueue . 例如,您可以使用BlockingQueue This is the basis for a producer/consumer scenario. 这是生产者/消费者方案的基础。

In your case: 在您的情况下:

  • the producer holds the server socket; 生产者持有服务器套接字; it accepts new client sockets and pushes the client sockets onto the queue; 它接受新的客户端套接字并将客户端套接字推送到队列中;
  • the consumers grab client sockets from the queue and process requests. 使用者从队列中获取客户端套接字并处理请求。

On top of all that, you can also use a bounded queue; 最重要的是,您还可以使用有队列。 you can try and push a new client socket to the queue; 您可以尝试将新的客户端套接字推送到队列中; if the queue is full you can then default to a "no can't do" consumer. 如果队列已满,则可以默认为“不能做”的使用者。

Scenarios are many. 场景很多。 There is not one answer. 没有一个答案。

Idea behind the Thread pool is that create a specified number of threads at start and then assign task to them. Thread pool背后的想法是在启动时创建指定数量的线程,然后将任务分配给它们。 Alternatively removing headache of creating threads each time. 或者,消除每次创建线程的麻烦。 I was implemented it a little some days ago, here is what I done. 几天前我已经实现了它,这是我所做的。

  1. Create some threads at start they share a request queue 在启动时创建一些线程,它们共享一个请求队列
  2. Threads are constantly looking for queue and when a request come one of the thread dispatch the request and perform action 线程一直在寻找队列,并且当请求到达时,其中一个线程调度该请求并执行操作
  3. The Queue will be synchronized 3. 队列将同步3。

Here are some queue methods 这是一些排队方法

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

Your Request processing runnable 您的请求处理可运行

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

in your main thread create a queue to put requests 在您的主线程中创建一个队列来放置请求

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

Start worker thread pool 启动工作线程池

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

in request listening 要求聆听

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

when you want to shout down server 当您想关闭服务器时

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

Note: My concern was not the HTTP headers, so im not specific, but you must implement the complete HTTP header eg Content-type, Content-length etc. 注意:我所关心的不是HTTP标头,所以不是特定的,但是您必须实现完整的HTTP标头,例如Content-type,Content-length等。

OK, the idea is simple enough. 好的,这个想法很简单。 You main loop currently creates a new RequisicaoRunnable object and a new Thread to run it each time it gets a connection from a client. 您的主循环当前会创建一个新的RequisicaoRunnable对象和一个新的线程,以在每次从客户端获得连接时运行该对象。 The idea behind a thread pool is to avoid creating new Threads each time. 线程池背后的想法是避免每次都创建新的线程。

In the simplest version of a thread pool, you create a blocking queue, and you create and start a fixed number of worker threads before you enter your main loop. 在最简单的线程池版本中,您将创建一个阻塞队列,并在进入主循环之前创建并启动固定数量的工作线程 The main loop will look almost exactly the same as what you have now, but instead of starting a Thread to run each new RequisicaoRunnable, it will simply add the new object to the queue. 主循环看起来几乎与您现在所拥有的循环完全相同,但是它不会启动线程来运行每个新的RequisicaoRunnable,而是只会将新对象添加到队列中。

Your worker threads are all the same: while (! shutdownHasBeenRequested()) { RequisicaoRunnable requisicaoRunnable = getATaskFromTheQueue(); 您的工作线程是相同的:while(!shutdownHasBeenRequested()){RequisicaoRunnable requisicaoRunnable = getATaskFromTheQueue(); requisicaoRunnable.run(); requisicaoRunnable.run(); } }

That way, each new task (client) will be executed (handled) by the next available thread from your pool. 这样,池中的下一个可用线程将执行(处理)每个新任务(客户端)。

If this is a homework assignment then you'll pretty much want to implement what I described, filling in some details as needed. 如果这是一项家庭作业,那么您将非常想实现我所描述的内容,并根据需要填写一些细节。

If it's not homework, then consider using a java.util.concurrent.ThreadPoolExcecutor() instead. 如果不是家庭作业,请考虑改用java.util.concurrent.ThreadPoolExcecutor()。 No point in re-inventing the wheel when there's a perfectly good wheel right there waiting to be used. 当有一个非常好的轮毂等待使用时,重新设计轮毂毫无意义。


Edit: as fge said, one improvement would be to send back a quick "sorry, try again later" response when new connections are coming in faster than you can handle them. 编辑:正如fge所说,一种改进是当新连接的传入速度快于您处理它们的速度时,可以发送快速的“抱歉,稍后重试”响应。 When the queue has too many pending connections in it (ie, when you hit the limit of a BoundedQueue), that's when you know to bail out and send the "try again later" response. 当队列中有太多待处理的连接时(即,当您达到BoundedQueue的限制时),这就是您知道要纾困并发送“稍后重试”响应的时候。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM