基于不同任务执行方式的 Web 服务器
在任务执行策略一文中,简单介绍了 Java 常见的三种任务执行策略及其优缺点。本文将介绍分别基于这三种任务执行策略的服务器的特点,以说明在特定的场景中,合适的任务执行策略的重要性。
单线程 Web 服务器
该例程是一个串行的 Web 服务器,该服务器每次只能接受一个请求,主线程在接受连接和处理相关请求等操作间不断地交替运行,该策略使得服务器资源利用率低,无法提供高吞吐率或快速响应性。
public class SingleThreadWebServer {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(80);
while (true) {
Socket conncetion = serverSocket.accept();
handleRequest(conncetion);
}
}
private static void handleRequest(Socket connection) { }
}
多线程 Web 服务器
和 SingleThreadWebServer 相比,该服务器程序为每个请求产生一个新的线程,以支持并行处理多个请求。
负载在服务器可承受的前提下,该策略可以得到更快的响应性和更高的吞吐量。但这种无限创建线程的方式依然有许多缺陷:
- 线程生命周期的开销非常高
- 线程的创建和销毁需要一定开销,如果请求的到达率非常高而请求的处理过程是轻量级的(大多数服务器都是这样), 那么线程的生命周期将消耗大量计算资源。
- 资源消耗严重:活跃线程会消耗系统资源,尤其是内存。若可运行的线程数量多于可用的处理器数量,那么有些线程将被闲置,而闲置线程依然会占用内存资源,给 GC 更大的压力。且大量线程竞争 CPU 时,也会带来额外开销。线程数量只要能保证 CPU 一直处于忙碌状态即可,更多的线程反而会降低性能。
- 影响服务器稳定性:无限膨胀的线程数极可能突破平台的限制(JVM、操作系统等),并引发难以恢复的异常,会影响程序的稳定性。
public class ThreadPerTaskWebServer {
public static void main(String[] args) throws IOException {
ServerSocket socket = new ServerSocket(80);
while (true) {
final Socket connection = socket.accept();
Runnable runnable = new Runnable() {
@Override
public void run() {
handleRequest(connection);
}
};
new Thread(runnable).start();
}
}
private static void handleRequest(Socket connection) {}
}
基于 Executor 的 Web 服务器
本服务器使用 Executor 将用户请求任务的提交和任务的实际执行解耦开来,且只需要使用另一种 Executor 实现就可以改变任务执行策略。将 Executor 的实现替换成 ThreadPerTaskWebServer、SingleThreadWebServer,很容易模拟ThreadPerTaskWebServer、SingleThreadWebServer 的行为。
使用基于 newFixedThreadPool 实现的 Executor 作为 Web 服务器任务执行的策略,可以在节约线程创建、销毁带来的开销的同时,避免服务器产生过多的线程从而影响服务器性能。
public class TaskExecutionWebServer {
private static final int NTHREADS = 100;
private static final Executor exec = Executors.newFixedThreadPool(NTHREADS);
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(80);
while (true) {
final Socket connection = serverSocket.accept();
Runnable task = new Runnable() {
@Override
public void run() {
handleRequest(connection);
}
};
exec.execute(task);
}
}
private static void handleRequest(Socket connection) { }
}
/**
* 服务器行为类似于 ThreadPerTaskWebServer,为每个请求分配一个线程
*/
class ThreadPerTaskExecutor implements Executor {
public void execute(Runnable r) {
new Thread(r).start();
}
}
/**
* 服务器行为类似于 SingleThreadWebServer,以同步的方式执行任务
*/
class WithinThreadExecutor implements Executor {
public void execute(Runnable r) {
r.run();
}
}