我们完成了这个 小型Tomcat 基本功能,但是他处理请求还是 一个一个的执行,并不能做到并行处理。 我们仿照Tomcat的思路来进行 一比一精准优化。
首先看一下我们的JxdHttpConnector 有什么改进,他可以理解成一个快递站领导,统一指挥JxdHttpProcessor 进行 工作。
JxdHttpConnector引入一个队列池,每接到一个请求,就冲队列池中取出来,执行这个请求,执行完之后再放回队列池中,继续等待下一个处理。
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayDeque;
import java.util.Deque;
public class JxdHttpConnector implements Runnable {
int minProcessors = 3;
int maxProcessors = 10;
int curProcessors = 0;
//存放多个processor的池子
Deque<JxdHttpProcessor> processorDeque = new ArrayDeque<>();
@Override
public void run() {
ServerSocket serverSocket = null;
int port = 8080;
try {
serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));
} catch (IOException e) {
e.printStackTrace();
System.exit(1);
}
//初始化池子 最开始三个
initProcessorDeque();
while (true) {
Socket socket = null;
try {
//这是单线程 一个请求一个请求获取socket
socket = serverSocket.accept();
//得到一个新的processor,这个processor从池中获取(池中有可能新建)
JxdHttpProcessor processor = createProcessor();
if (processor == null) {
socket.close();
continue;
}
processor.assign(socket);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void start() {
Thread thread = new Thread(this);
thread.start();
}
//从池子中获取一个processor,如果池子为空且小于最大限制,则新建一个
private JxdHttpProcessor createProcessor() {
synchronized (processorDeque) {
if (processorDeque.size() > 0) {
return processorDeque.pop();
}
if (curProcessors < maxProcessors) {
return newProcessor();
} else {
return null;
}
}
}
private void initProcessorDeque(){
for (int i = 0; i < minProcessors; i++) {
JxdHttpProcessor processor = new JxdHttpProcessor(this);
processor.start();
processorDeque.push(processor);
}
curProcessors = minProcessors;
}
private JxdHttpProcessor newProcessor() {
JxdHttpProcessor jxdHttpProcessor = new JxdHttpProcessor(this);
jxdHttpProcessor.start();
processorDeque.push(jxdHttpProcessor);
curProcessors++;
return processorDeque.pop();
}
public void recycle(JxdHttpProcessor processor) {
processorDeque.push(processor);
}
}
processorDeque这就是队列池,刚开始的时候 会创建三个,随着业务访问了增加 最多增加10个 ,
你们发现了吗 JxdHttpProcessor 为啥要.start()多线程进行处理,因为 你 虽然引入队列池但是还是不能 解决并行处理问题,所以要让每一个真正的执行者(员工)。进行并行处理。
JxdHttpProcessor 代码:
public class JxdHttpProcessor implements Runnable {
boolean available = false;
Socket socket;
JxdHttpConnector connector;
public JxdHttpProcessor(JxdHttpConnector connector) {
this.connector = connector;
}
private void process(Socket socket) { //服务器循环等待请求并处理
try {
Thread.sleep(3000);
} catch (InterruptedException e1) {
e1.printStackTrace();
}
InputStream input = null;
OutputStream output = null;
try {
input = socket.getInputStream();
output = socket.getOutputStream();
// create Request object and parse
JxdRequest request = new JxdRequest(input);
request.parse();
// create Response object
JxdResponse response = new JxdResponse(output);
if (request.getUri().startsWith("/servlet/")) {
//加载动态资源
JxdServletProcessor jxdServletProcessor = new JxdServletProcessor();
jxdServletProcessor.process(request, response);
} else {
//加载静态资源
StaticResourceProcessor staticResourceProcessor = new StaticResourceProcessor();
staticResourceProcessor.process(request, response);
}
//因为是多线程所以只能交给httpProcessor 来关闭
socket.close();
} catch (Exception ea) {
ea.printStackTrace();
}
}
@Override
public void run() {
while (true) {
// 等待socket分配过来
Socket socket = await();
if (socket == null) continue;
// 处理请求
process(socket);
// 回收processor,员工归队
connector.recycle(this);
}
}
public void start() {
Thread thread = new Thread(this);
thread.start();
}
private synchronized Socket await() {
// 等待connector提供一个新的socket
while (!available) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 获得这个新的Socket
Socket socket = this.socket;
//设置标志为false
available = false;
//通知另外的线程
notifyAll();
return (socket);
}
public synchronized void assign(Socket socket) {
// 等待connector提供一个新的socket
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// 获取到这个新的Socket
this.socket = socket;
// 把标志设置回去
available = true;
//通知另外的线程
notifyAll();
}
}
JxdHttpProcessor 真正的执行者 实现了 Runnable 接口 同时引入了 await()和assign()方法和available 标识。
先说await()方法 ,available 默认为false 也就是线程启动的时候进入 等待状态,一直等,等谁呢 他在等待一个指令,就类似于 员工快递站工作,等来了快递 才工作 没有快递 就休息。
在说一下 assign()方法,他的入参是Socket ,表示 快递来了 ,因为available 默认为false所以他会继续走 ,把标志available 默认为true 然后 通知await()中等待的 员工要开始工作了 。
这就是 形成了一套 运行 机制,我通知你干活,你干完活之后 告诉我 ,我们在02那篇文章的时候是这样的,JxdHttpConnector 领导一直紧跟着JxdHttpProcessor 员工,直到员工干完活,一个领导盯着一个员工很累的,现在是 这个领导 统一指挥一个 团队,员工自己干完活之后反馈给领导。
socket.close(); 还记得这个吗。之前是 有JxdHttpConnector 来关闭的,现在由 员工JxdHttpProcessor来关闭了,为啥 因为领导 一致盯着 员工直到 结束,但是现在 盯不住了 ,只能员工自己 才知道什么时候结束 。
备注:其他类不展示 基本每改动。
看一下运行结果吧。
public class JxdHttpServer {
public static final String WEB_ROOT = System.getProperty("user.dir");
public static final String FILE_ROOT = "D:\\";
public static void main(String[] args) {
JxdHttpConnector connector = new JxdHttpConnector();
connector.start();
}
}