Connector 组件是 Tomcat 中两个核心组件之一,它的主要任务是负责接收浏览器的发过来的 tcp 连接请求,创建一个 Request 和 Response 对象分别用于和请求端交换数据,然后会产生一个线程来处理这个请求并把产生的 Request 和 Response 对象传给处理这个请求的线程,处理这个请求的线程就是 Container 组件要做的事了。
由于这个过程比较复杂,大体的流程可以用下面的顺序图来解释:
图 6. Connector 处理一次请求顺序图
(查看清晰大图)
Tomcat5 中默认的 Connector 是 Coyote,这个 Connector 是可以选择替换的。Connector 最重要的功能就是接收连接请求然后分配线程让 Container 来处理这个请求,所以这必然是多线程的,多线程的处理是 Connector 设计的核心。Tomcat5 将这个过程更加细化,它将 Connector 划分成 Connector、Processor、Protocol, 另外 Coyote 也定义自己的 Request 和 Response 对象。
下面主要看一下 Tomcat 中如何处理多线程的连接请求,先看一下 Connector 的主要类图:
图 7. Connector 的主要类图
(查看清晰大图)
看一下 HttpConnector 的 Start 方法:
清单 6. HttpConnector.Start
public void start() throws LifecycleException { if (started) throw new LifecycleException (sm.getString("httpConnector.alreadyStarted")); threadName = "HttpConnector[" + port + "]"; lifecycle.fireLifecycleEvent(START_EVENT, null); started = true; threadStart(); while (curProcessors < minProcessors) { if ((maxProcessors > 0) && (curProcessors >= maxProcessors)) break; HttpProcessor processor = newProcessor(); recycle(processor); } }
threadStart() 执行就会进入等待请求的状态,直到一个新的请求到来才会激活它继续执行,这个激活是在 HttpProcessor 的 assign 方法中,这个方法是代码如下 :
清单 7. HttpProcessor.assign
synchronized void assign(Socket socket) { while (available) { try { wait(); } catch (InterruptedException e) { } } this.socket = socket; available = true; notifyAll(); if ((debug >= 1) && (socket != null)) log(" An incoming request is being assigned"); }
创建 HttpProcessor 对象是会把 available 设为 false,所以当请求到来时不会进入 while 循环,将请求的 socket 赋给当期处理的 socket,并将 available 设为 true,当 available 设为 true 是 HttpProcessor 的 run 方法将被激活,接下去将会处理这次请求。
Run 方法代码如下:
清单 8. HttpProcessor.Run
public void run() { while (!stopped) { Socket socket = await(); if (socket == null) continue; try { process(socket); } catch (Throwable t) { log("process.invoke", t); } connector.recycle(this); } synchronized (threadSync) { threadSync.notifyAll(); } }
解析 socket 的过程在 process 方法中,process 方法的代码片段如下:
清单 9. HttpProcessor.process
private void process(Socket socket) { boolean ok = true; boolean finishResponse = true; SocketInputStream input = null; OutputStream output = null; try { input = new SocketInputStream(socket.getInputStream(),connector.getBufferSize()); } catch (Exception e) { log("process.create", e); ok = false; } keepAlive = true; while (!stopped && ok && keepAlive) { finishResponse = true; try { request.setStream(input); request.setResponse(response); output = socket.getOutputStream(); response.setStream(output); response.setRequest(request); ((HttpServletResponse) response.getResponse()) .setHeader("Server", SERVER_INFO); } catch (Exception e) { log("process.create", e); ok = false; } try { if (ok) { parseConnection(socket); parseRequest(input, output); if (!request.getRequest().getProtocol().startsWith("HTTP/0")) parseHeaders(input); if (http11) { ackRequest(output); if (connector.isChunkingAllowed()) response.setAllowChunking(true); } } 。。。。。。 try { ((HttpServletResponse) response).setHeader ("Date", FastHttpDateFormat.getCurrentDate()); if (ok) { connector.getContainer().invoke(request, response); } 。。。。。。 } try { shutdownInput(input); socket.close(); } catch (IOException e) { ; } catch (Throwable e) { log("process.invoke", e); } socket = null; }
当 Connector 将 socket 连接封装成 request 和 response 对象后接下来的事情就交给 Container 来处理了。