How tomcat works(深入剖析tomcat)閱讀筆記1-4章
How tomcat works
chapter 1 簡單的web伺服器
這一張的主要內容就是實現一個簡單的靜態資源伺服器,socket程式設計,利用java提供的socket和serverSocket程式設計
整體過程如下:
HttpServer通過serverSocket監聽埠,通過阻塞的accept方法接收請求,然後建立resquest和response物件,
//伺服器啟動主方法 public static void main(String[] args) { HttpServer server = new HttpServer(); server.await(); } //await方法,幾乎所有的任務都在這裡完成 public void await() { 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); } // Loop waiting for a request //shutdown為一個成員變數,這樣的伺服器的較為優雅的停止方式為接收一個uri為"/SHUTDOWN_COMMAND"的請求 while (!shutdown) { Socket socket = null; InputStream input = null; OutputStream output = null; try { socket = serverSocket.accept(); input = socket.getInputStream(); output = socket.getOutputStream(); //建立request物件,把inputstream給他 // create Request object and parse Request request = new Request(input); request.parse(); // create Response object Response response = new Response(output); response.setRequest(request); response.sendStaticResource(); // Close the socket socket.close(); //check if the previous URI is a shutdown command shutdown = request.getUri().equals(SHUTDOWN_COMMAND); } catch (Exception e) { e.printStackTrace(); continue; } } }
request物件主要就做一件事情,從inputstream中解析uri
parse()
從inputstream中讀取資料轉成字串,然後呼叫parseUri
方法
public void parse() { // Read a set of characters from the socket StringBuffer request = new StringBuffer(2048); int i; byte[] buffer = new byte[2048]; try { i = input.read(buffer); } catch (IOException e) { e.printStackTrace(); i = -1; } for (int j=0; j<i; j++) { request.append((char) buffer[j]); } System.out.print(request.toString()); uri = parseUri(request.toString()); }
parseUri(String requestString)
private String parseUri(String requestString) { int index1, index2; index1 = requestString.indexOf(' '); if (index1 != -1) { index2 = requestString.indexOf(' ', index1 + 1); if (index2 > index1) //切割出uri return requestString.substring(index1 + 1, index2); } return null; }
response物件,拿到request和outputstream,按照request中的uri找到對應的資源,然後塞入outputstream中就完事兒
sendStaticResource()
public void sendStaticResource() throws IOException {
byte[] bytes = new byte[BUFFER_SIZE];
FileInputStream fis = null;
try {
File file = new File(HttpServer.WEB_ROOT, request.getUri());
if (file.exists()) {
fis = new FileInputStream(file);
int ch = fis.read(bytes, 0, BUFFER_SIZE);
while (ch!=-1) {
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
}
else {
// file not found
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
}
catch (Exception e) {
// thrown if cannot instantiate a File object
System.out.println(e.toString() );
}
finally {
if (fis!=null)
fis.close();
}
}
tips:由於這個響應沒有任何響應頭,所以chrome現在的版本不會接受它,但是firefox可以,但也只是以純文字形式
chapter 2 一個簡單的servlet容器
第一章利用了java的socket程式設計構建了一個只能響應只能響應靜態資源的http伺服器,這一章實現一個簡單的servlet容器,它只能處理簡單的servlet和響應靜態資源
熟悉servlet介面
servlet介面中聲明瞭5個方法
//初始化方法
public void init(ServletConfig config)throws ServletException;
//主要的方法,接受一個request物件和response物件,然後進行處理
public void service(ServletRequest request,ServletResponse response)throws ServletException,Java.io.IOException;
//在將servlet例項從服務中移除時,servlet容器會呼叫servlet例項的destory()方法,清理一些資源
public void destory();
public ServletConfig getServletConfig();
public java.lang.String getServletInfo();
一個功能齊全的servlet容器處理流程
- 第一次呼叫某servlet時,將它載入,然後執行init()方法
- 針對每個request請求,建立ServletRequest例項和ServletResponse例項,傳遞給service()方法
- 關閉servlet時,呼叫destroy()方法,並且解除安裝該類
本章是一個簡單的實現,就不呼叫init方法和destroy方法,建立好ServletRequest和ServletResponse然後作為引數傳遞給service方法即可
tips:在這樣的servlet容器中,每次對於該servlet的請求,都會導致載入相應的servlet類,可想而知,效率是非常低的
主要類以及流程分析
本章主要包含了以下幾個類:
- HttpServer1
- Request
- Response
- StaticResourceProcessor
- ServletProcessor1
- Constants
該servlet容器的UML如下
處理流程
入口類,HttpServer1,監聽埠8080,構建request,response,根據是請求靜態資源還是servlet分別處理
//伺服器主方法
public static void main(String[] args) {
HttpServer1 server = new HttpServer1();
server.await();
}
//await
public void await() {
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);
}
// Loop waiting for a request
while (!shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept();
input = socket.getInputStream();
output = socket.getOutputStream();
// create Request object and parse
Request request = new Request(input);
request.parse();
// create Response object
Response response = new Response(output);
response.setRequest(request);
// check if this is a request for a servlet or a static resource
// a request for a servlet begins with "/servlet/"
if (request.getUri().startsWith("/servlet/")) {
ServletProcessor1 processor = new ServletProcessor1();
processor.process(request, response);
}
else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}
// Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
request.parse()
和第一章一樣解析uri
public void parse() {
// Read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try {
i = input.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j=0; j<i; j++) {
request.append((char) buffer[j]);
}
System.out.print(request.toString());
uri = parseUri(request.toString());
}
//uri = parseUri(request.toString());
private String parseUri(String requestString) {
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1) {
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
}
processor.process(request, response);
請求servlet分支,接受request,response作為引數,然後呼叫ServletProcessor1的process方法,載入對應的servlet然後,例項化該servlet並且真正地處理請求
public void process(Request request, Response response) {
String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
// the forming of repository is taken from the createClassLoader method in
// org.apache.catalina.startup.ClassLoaderFactory
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
// the code for forming the URL is taken from the addRepository method in
// org.apache.catalina.loader.StandardClassLoader class.
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
//載入servlet
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
System.out.println(e.toString());
}
Servlet servlet = null;
try {
//例項化servlet並處理請求
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) request, (ServletResponse) response);
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
}
}
processor.process(request, response);
傳送靜態資源分支,這裡只是呼叫了以下response的sendStaticResource方法
public void process(Request request, Response response) {
try {
response.sendStaticResource();
}
catch (IOException e) {
e.printStackTrace();
}
}
chapter 3 聯結器(Connector)
建立一個聯結器,用來增強第二章的功能,主要就是用一種更好的方式建立request和response物件
本章應用包含三個模組:聯結器模組、啟動模組和核心模組
啟動模組只包含了一個類:BootStrap
聯結器模組可分為以下5個型別:
- 聯結器及其支援類(HttpConnector和HttpProcessor)
- 表示Http請求的類(HttpRequest)及其支援類
- 表示Http響應的類(HttpResponse)及其支援類
- 外觀類(HttpRequestFacade和HttpResponseFacade)
- 常量類(Constants)
核心模組包含兩個類,servletProcessor和StaticResourceProcessor,具體UML如下
主要類以及流程分析
啟動類BootStrap,new一個connector然後呼叫connector.start(),不難猜到connector的start另啟了一個執行緒
public static void main(String[] args) {
HttpConnector connector = new HttpConnector();
connector.start();
}
HttpConnector
聯結器模組主要類,負責監聽埠,接收請求,然後將自己傳遞給HttpProcessor,並且將socket作為引數,傳遞給HttpProcessor的process方法
run()
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);
}
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
socket = serverSocket.accept();
}
catch (Exception e) {
continue;
}
// Hand this socket off to an HttpProcessor
HttpProcessor processor = new HttpProcessor(this);
processor.process(socket);
}
}
public void start() {
Thread thread = new Thread(this);
thread.start();
}
HttpProcessor
負責生成resquest以及response,然後交給ServletProcessor或者StaticResourceProcessor處理
public void process(Socket socket)
public void process(Socket socket) {
//SocketInputStream是InputStream的包裝類,主要提供了readRequestLine方法和readHeader方法
SocketInputStream input = null;
OutputStream output = null;
try {
//2048為指定的緩衝陣列大小
input = new SocketInputStream(socket.getInputStream(), 2048);
output = socket.getOutputStream();
// create HttpRequest object and parse
request = new HttpRequest(input);
// create HttpResponse object
response = new HttpResponse(output);
response.setRequest(request);
response.setHeader("Server", "Pyrmont Servlet Container");
//解析請求行
parseRequest(input, output);
//解析首部行
parseHeaders(input);
//然後就可以根據請求的uri決定交給什麼processor了
//這兩者就和第二章的一樣了
//check if this is a request for a servlet or a static resource
//a request for a servlet begins with "/servlet/"
if (request.getRequestURI().startsWith("/servlet/")) {
ServletProcessor processor = new ServletProcessor();
processor.process(request, response);
}
else {
StaticResourceProcessor processor = new StaticResourceProcessor();
processor.process(request, response);
}
// Close the socket
socket.close();
// no shutdown for this application
}
catch (Exception e) {
e.printStackTrace();
}
}
解析請求行parseRequest
private void parseRequest(SocketInputStream input, OutputStream output)
private void parseRequest(SocketInputStream input, OutputStream output)
throws IOException, ServletException {
// Parse the incoming request line
//HttpRequestLine為請求行物件,利用readRequestLine方法填充HttpProcessor的HttpRequestLine成員變數
input.readRequestLine(requestLine);
//經過上面的處理,這裡就能拿到方法名了
String method =
new String(requestLine.method, 0, requestLine.methodEnd);
//uri暫時還拿不到,因為可能帶參,此時的uri字元陣列儲存的是帶參的uri
String uri = null;
String protocol = new String(requestLine.protocol, 0, requestLine.protocolEnd);
// Validate the incoming request line
if (method.length() < 1) {
throw new ServletException("Missing HTTP request method");
}
else if (requestLine.uriEnd < 1) {
throw new ServletException("Missing HTTP request URI");
}
// Parse any query parameters out of the request URI
//處理帶參uri
int question = requestLine.indexOf("?");
if (question >= 0) {
//包請求引數塞進queryString,回頭需要的時候解析
request.setQueryString(new String(requestLine.uri, question + 1,
requestLine.uriEnd - question - 1));
uri = new String(requestLine.uri, 0, question);
}
else {
request.setQueryString(null);
uri = new String(requestLine.uri, 0, requestLine.uriEnd);
}
//檢查,可略過
// Checking for an absolute URI (with the HTTP protocol)
if (!uri.startsWith("/")) {
int pos = uri.indexOf("://");
// Parsing out protocol and host name
if (pos != -1) {
pos = uri.indexOf('/', pos + 3);
if (pos == -1) {
uri = "";
}
else {
uri = uri.substring(pos);
}
}
}
//處理jessionId
// Parse any requested session ID out of the request URI
String match = ";jsessionid=";
int semicolon = uri.indexOf(match);
if (semicolon >= 0) {
String rest = uri.substring(semicolon + match.length());
int semicolon2 = rest.indexOf(';');
if (semicolon2 >= 0) {
request.setRequestedSessionId(rest.substring(0, semicolon2));
rest = rest.substring(semicolon2);
}
else {
request.setRequestedSessionId(rest);
rest = "";
}
request.setRequestedSessionURL(true);
uri = uri.substring(0, semicolon) + rest;
}
else {
request.setRequestedSessionId(null);
request.setRequestedSessionURL(false);
}
// Normalize URI (using String operations at the moment)
String normalizedUri = normalize(uri);
// Set the corresponding request properties
//把請求行的屬性都塞進去,至此請求行的資料解析完畢
((HttpRequest) request).setMethod(method);
request.setProtocol(protocol);
if (normalizedUri != null) {
((HttpRequest) request).setRequestURI(normalizedUri);
}
else {
((HttpRequest) request).setRequestURI(uri);
}
if (normalizedUri == null) {
throw new ServletException("Invalid URI: " + uri + "'");
}
}
HttpRequestLine
HttpRequestLine為請求行物件,用字元陣列來儲存請求行的資料,程式碼一瞥
public HttpRequestLine(char[] method, int methodEnd,
char[] uri, int uriEnd,
char[] protocol, int protocolEnd) {
this.method = method;
this.methodEnd = methodEnd;
this.uri = uri;
this.uriEnd = uriEnd;
this.protocol = protocol;
this.protocolEnd = protocolEnd;
}
public char[] method;
public int methodEnd;
public char[] uri;
public int uriEnd;
public char[] protocol;
public int protocolEnd;
SocketInputStream的readRequestLine方法
public void readRequestLine(HttpRequestLine requestLine)
public void readRequestLine(HttpRequestLine requestLine)
throws IOException {
// Recycling check
if (requestLine.methodEnd != 0)
requestLine.recycle();
// Checking for a blank line
//跳過空行
int chr = 0;
do { // Skipping CR or LF
try {
//呼叫read方法,
chr = read();
} catch (IOException e) {
chr = -1;
}
} while ((chr == CR) || (chr == LF));
if (chr == -1)
throw new EOFException
(sm.getString("requestStream.readline.error"));
//由於read方法中將pos指向了下一個位元組了,需要回退一步
pos--;
// Reading the method name
int maxRead = requestLine.method.length;
int readStart = pos;
int readCount = 0;
boolean space = false;
while (!space) {
//超過method字元陣列的最大長度了
// if the buffer is full, extend it
if (readCount >= maxRead) {
if ((2 * maxRead) <= HttpRequestLine.MAX_METHOD_SIZE) {
char[] newBuffer = new char[2 * maxRead];
System.arraycopy(requestLine.method, 0, newBuffer, 0,
maxRead);
requestLine.method = newBuffer;
maxRead = requestLine.method.length;
} else {
throw new IOException
(sm.getString("requestStream.readline.toolong"));
}
}
// We're at the end of the internal buffer
//如果已經讀取到inputstream讀取出來的buffer的最後一個位置了
if (pos >= count) {
int val = read();
if (val == -1) {
throw new IOException
(sm.getString("requestStream.readline.error"));
}
pos = 0;
readStart = 0;
}
//正常都走到這一步
if (buf[pos] == SP) {
space = true;
}
//這裡method字元陣列是以空格節未的,並沒有在buf[pos]=SP的時候break
requestLine.method[readCount] = (char) buf[pos];
readCount++;
pos++;
}
//以空格結尾,readCount去掉空格
requestLine.methodEnd = readCount - 1;
//下面就是解析uri和protocol了
}
SocketInputStream的read、fill方法
public int read()
throws IOException {
//起始值pos為0,count也為0
if (pos >= count) {
//修改count為本次讀入的位元組長度
fill();
if (pos >= count)
return -1;
}
//返回buf[pos]的那個位元組
return buf[pos++] & 0xff;
}
protected void fill()
throws IOException {
pos = 0;
count = 0;
//
int nRead = is.read(buf, 0, buf.length);
if (nRead > 0) {
count = nRead;
}
}
解析首部行parseHeaders
private void parseHeaders(SocketInputStream input)
private void parseHeaders(SocketInputStream input)
throws IOException, ServletException {
//迴圈解析每個請求頭
while (true) {
HttpHeader header = new HttpHeader();;
// Read the next header
//socketInputStream提供的readHeader方法,上面已經詳細解讀過readRequestLine方法了,這裡不詳細解讀,填充header
//HttpHeader和HttpRequestLine結構也差不多
input.readHeader(header);
if (header.nameEnd == 0) {
if (header.valueEnd == 0) {
return;
}
else {
throw new ServletException
(sm.getString("httpProcessor.parseHeaders.colon"));
}
}
String name = new String(header.name, 0, header.nameEnd);
String value = new String(header.value, 0, header.valueEnd);
request.addHeader(name, value);
//對於cookie和content-length兩個header需要特殊處理一下
// do something for some headers, ignore others.
if (name.equals("cookie")) {
Cookie cookies[] = RequestUtil.parseCookieHeader(value);
for (int i = 0; i < cookies.length; i++) {
if (cookies[i].getName().equals("jsessionid")) {
// Override anything requested in the URL
if (!request.isRequestedSessionIdFromCookie()) {
// Accept only the first session id cookie
request.setRequestedSessionId(cookies[i].getValue());
request.setRequestedSessionCookie(true);
request.setRequestedSessionURL(false);
}
}
request.addCookie(cookies[i]);
}
}
else if (name.equals("content-length")) {
int n = -1;
try {
n = Integer.parseInt(value);
}
catch (Exception e) {
throw new ServletException(sm.getString("httpProcessor.parseHeaders.contentLength"));
}
request.setContentLength(n);
}
else if (name.equals("content-type")) {
request.setContentType(value);
}
} //end while
}
懶載入的parameter
請求引數在被呼叫之前是不會解析的,在request的getParameter、getParameterMap、getParameterNames等方法被呼叫時,首先會呼叫parseParameter方法
需要注意的一點是,ParameterMap是不支援修改的,它被鎖住了,很簡單,用一個lock變數,修改的直接return
protected void parseParameters()
protected void parseParameters() {
if (parsed)
return;
ParameterMap results = parameters;
if (results == null)
results = new ParameterMap();
//先開鎖
results.setLocked(false);
String encoding = getCharacterEncoding();
if (encoding == null)
encoding = "ISO-8859-1";
//parse請求頭中的引數
// Parse any parameters specified in the query string
String queryString = getQueryString();
try {
RequestUtil.parseParameters(results, queryString, encoding);
}
catch (UnsupportedEncodingException e) {
;
}
//parse請求體中的引數
// Parse any parameters specified in the input stream
String contentType = getContentType();
if (contentType == null)
contentType = "";
int semicolon = contentType.indexOf(';');
if (semicolon >= 0) {
contentType = contentType.substring(0, semicolon).trim();
}
else {
contentType = contentType.trim();
}
if ("POST".equals(getMethod()) && (getContentLength() > 0)
&& "application/x-www-form-urlencoded".equals(contentType)) {
try {
int max = getContentLength();
int len = 0;
byte buf[] = new byte[getContentLength()];
ServletInputStream is = getInputStream();
while (len < max) {
int next = is.read(buf, len, max - len);
if (next < 0 ) {
break;
}
len += next;
}
is.close();
if (len < max) {
throw new RuntimeException("Content length mismatch");
}
RequestUtil.parseParameters(results, buf, encoding);
}
catch (UnsupportedEncodingException ue) {
;
}
catch (IOException e) {
throw new RuntimeException("Content read fail");
}
}
// Store the final results
//再把parameterMap鎖上,並且將parsed置為true避免重複parse
results.setLocked(true);
parsed = true;
parameters = results;
}
chapter 4 Tomcat預設的聯結器
總體介紹
第三章實現了簡單的聯結器,第四章來看看Tomcat預設的聯結器,雖然該聯結器已經被棄用,換成了另一個執行速率更快的聯結器——Coyote——替代,但是還是值得一看的,相較於第三章簡單的聯結器,Tomcat4的預設聯結器有以下優化:
- 更為全面地支援Http協議,包括從0.9到1.1,也支援1.1的新特性
- 每一個connector不再只繫結一個HttpProcessor,將會利用多執行緒支援多Http連線
- 大量利用字元陣列,減少了代價較高的字串操作
- 利用了池的技術,快取了HttpProcessor物件,避免每次來都需要建立,節省了物件建立的開銷
但仍然還存在著上來就直接parse所有的Header不管有沒有使用,效率比較低的問題
它的主要工作原理和第三章的聯結器基本類似,細微之處不同稍後原始碼討論
預設的聯結器UML圖如下:
首先需要注意的一點是,於第三章不同,HttpConnector來負責Request、Response的建立了,而HttpProcessor專注於parse資訊,也就是填充request和response
Http1.1的新特性
持久連線
減少了頻繁建立TCP連線帶來的系統消耗,也減少了三次握手的次數,提高了速度,在Http1.1中,預設採用持久連線,也可以顯式使用,在request首部行加上
Connection: Keep-alive
塊編碼
有了持久連線之後,一個TCP連線可能被共享,那麼多個物件的資料可能混在一起,那麼如何把它們區分開來呢?利用transfer-encoding的特殊請求頭,它用來指明位元組流會分塊傳送,對每一個塊,塊的長度後面會有一個CRLF,然後才是資料,一個事務以一個長度為0的塊標記,“0\r\n”表示事務已經結束
//e.g利用兩個塊傳送 I'am as helpess as a kitten up a tree.
//傳送內容如下
1D\r\n
I'am as helpess as a kitten u
9\r\n
p a tree.
0\r\n
狀態碼100
當瀏覽器想傳送一個比較大的請求體的時候,不知道伺服器能不能接收時,可以先發送一個帶有如下請求頭的請求
Except: 100-continue
期待服務的迴應為100-continue,收到這個迴應說明可以接收,這個時候瀏覽器才會真正地發出這個比較大的請求,避免浪費頻寬、系統資源
如果伺服器收到帶有上述請求頭的請求,並且支援的話,就傳送如下的回覆
HTTP/1.1 100 continue
這樣瀏覽器就會發送真正的請求了
主要類以及流程分析
建立ServerSocket
首先呼叫initialize方法
void initialize()
public void initialize()
throws LifecycleException {
if (initialized)
throw new LifecycleException (
sm.getString("httpConnector.alreadyInitialized"));
this.initialized=true;
Exception eRethrow = null;
// Establish a server socket on the specified port
try {
//呼叫open方法
serverSocket = open();
} catch (IOException ioe) {
...
}
if ( eRethrow != null )
throw new LifecycleException(threadName + ".open", eRethrow);
}
ServerSocket open()
private ServerSocket open()
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException
{
// Acquire the server socket factory for this Connector
ServerSocketFactory factory = getFactory();
//根據是否有限定的IP地址選擇不同的工廠方法
// If no address is specified, open a connection on all addresses
if (address == null) {
log(sm.getString("httpConnector.allAddresses"));
try {
//不限定埠地址,也就是說監聽任何IP的請求
return (factory.createSocket(port, acceptCount));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
// Open a server socket on the specified address
try {
InetAddress is = InetAddress.getByName(address);
log(sm.getString("httpConnector.anAddress", address));
try {
return (factory.createSocket(port, acceptCount, is));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + address +
":" + port);
}
} catch (Exception e) {
log(sm.getString("httpConnector.noAddress", address));
try {
return (factory.createSocket(port, acceptCount));
} catch (BindException be) {
throw new BindException(be.getMessage() + ":" + port);
}
}
}
factory.createSocket(port, acceptCount)
//ServerSocketFactory中定義,在DefalutServerSocketFactory中實現
public ServerSocket createSocket (int port, int backlog)
throws IOException, KeyStoreException, NoSuchAlgorithmException,
CertificateException, UnrecoverableKeyException,
KeyManagementException {
return (new ServerSocket(port, backlog));
}
至此建立了ServerSocket並與HttpConnector的serverSocket成員變數繫結
監聽請求
start()
public void start() throws LifecycleException {
// Validate and update our current state
if (started)
throw new LifecycleException
(sm.getString("httpConnector.alreadyStarted"));
threadName = "HttpConnector[" + port + "]";
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start our background thread
//new了一個thread 並且呼叫了thread的start方法
threadStart();
// Create the specified minimum number of processors
while (curProcessors < minProcessors) {
if ((maxProcessors > 0) && (curProcessors >= maxProcessors))
break;
HttpProcessor processor = newProcessor();
recycle(processor);
}
}
void run()
start之後進入run方法
public void run() {
// Loop until we receive a shutdown command
//stopped成員變數
while (!stopped) {
// Accept the next incoming connection from the server socket
Socket socket = null;
try {
//接收請求
socket = serverSocket.accept();
//設定超時時長
if (connectionTimeout > 0)
socket.setSoTimeout(connectionTimeout);
} catch (AccessControlException ace) {
log("socket accept security exception: " + ace.getMessage());
continue;
} catch (IOException e) {
if (started && !stopped)
log("accept: ", e);
break;
}
// Hand this socket off to an appropriate processor
//從成員變數processors,這個processor池中拿一個HttpProcessor出來
//如果池中沒有,則建立一個
HttpProcessor processor = createProcessor();
if (processor == null) {
try {
log(sm.getString("httpConnector.noProcessor"));
//如果不允許建立了,則直接把socket close掉,也就是直接拋棄這個請求
socket.close();
} catch (IOException e) {
;
}
continue;
}
//把socket交給processor
processor.assign(socket);
// The processor will recycle itself when it finishes
}
// Notify the threadStop() method that we have shut ourselves down
synchronized (threadSync) {
threadSync.notifyAll();
}
}
HttpProcessor createProcessor()
private HttpProcessor createProcessor() {
synchronized (processors) {
if (processors.size() > 0)
return ((HttpProcessor) processors.pop());
if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
//stack中沒有空閒的的processor了,並且當前Processor數量還沒超過允許的最大Processor數量,則建立一個新的並返回
return (newProcessor());
} else {
//如果maxProcessors<0 說明processor數量不受限制,可以無限制建立
if (maxProcessors < 0) {
return (newProcessor());
} else {
//說明curProcessors > maxProcessors 沒有空閒的processor了,也不允許建立
return (null);
}
}
}
}
HttpProcessor newProcessor()
private HttpProcessor newProcessor() {
//建立processor並且繫結connector
HttpProcessor processor = new HttpProcessor(this, curProcessors++);
if (processor instanceof Lifecycle) {
try {
//注意!!! 這裡啟動了HttpProcessor執行緒
((Lifecycle) processor).start();
} catch (LifecycleException e) {
log("newProcessor", e);
return (null);
}
}
created.addElement(processor);
return (processor);
}
processor.assign(socket)
connector把socket交付給HttpProcessor就結束了,它並不像之前那樣還需要等待HttpProcessor的執行結果,它可以去繼續監聽其他請求了,它這裡就只負責這裡就負責喚醒了一下在run方法中由於沒有socket阻塞的processor
synchronized void assign(Socket socket) {
// Wait for the Processor to get the previous Socket
//available預設值為false,代表是否有新的socket過來
while (available) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket;
available = true;
//notifyAll喚醒了誰呢?喚醒了HttpProcessor的主執行緒
//它在newProcessor方法中啟動,然後在await方法中等待
notifyAll();
if ((debug >= 1) && (socket != null))
log(" An incoming request is being assigned");
}
處理請求
HttpProcessor
run()
public void run() {
// Process requests until we receive a shutdown signal
//stopped成員變數
while (!stopped) {
// Wait for the next socket to be assigned
Socket socket = await();
if (socket == null)
continue;
// Process the request from this socket
try {
process(socket);
} catch (Throwable t) {
log("process.invoke", t);
}
// Finish up this request
//把processor重新又壓回connector的棧內重複利用
connector.recycle(this);
}
// Tell threadStop() we have shut ourselves down successfully
synchronized (threadSync) {
threadSync.notifyAll();
}
}
await()
private synchronized Socket await() {
// Wait for the Connector to provide a new Socket
while (!available) {
try {
wait();
} catch (InterruptedException e) {
}
}
//被喚醒之後availabe被修改成了true了,將socket賦值給區域性變數,並且將availabe修改為false
// Notify the Connector that we have received this Socket
Socket socket = this.socket;
available = false;
notifyAll();
if ((debug >= 1) && (socket != null))
log(" The incoming request has been awaited");
return (socket);
}
這裡引用一下原文的解釋
為什麼這裡需要返回一個區域性變數?
這樣做的話,就可以在當前socket被處理完成之前,接收下一個socket???黑人問號,這個process方法不是單執行緒的嘛?它不是必須完成解析才能被回收嘛?怎麼同時接收兩個socket的
為什麼還需要notifyAll呢?
防止connector在執行到processor.assign()
方法阻塞,此時available為true,connector一直阻塞,知道processor喚醒它
recycle(HttpProcessor processor)
void recycle(HttpProcessor processor) {
// if (debug >= 2)
// log("recycle: Recycling processor " + processor);
processors.push(processor);
}
處理請求的主方法
**private void process(Socket socket) **
private void process(Socket socket) {
boolean ok = true;
boolean finishResponse = true;
SocketInputStream input = null;
OutputStream output = null;
// Construct and initialize the objects we will need
try {
input = new SocketInputStream(socket.getInputStream(),
connector.getBufferSize());
} catch (Exception e) {
log("process.create", e);
ok = false;
}
keepAlive = true;
//原來宣揚了半天的持久連線新特性就是個keepAlive變數
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;
}
// Parse the incoming request
try {
if (ok) {
//如同第三章那樣,這裡負責處理填充Request,如果想追究如何處理Http1.1的新特性,可以檢視,這裡不細說
parseConnection(socket);
parseRequest(input, output);
if (!request.getRequest().getProtocol()
.startsWith("HTTP/0"))
parseHeaders(input);
if (http11) {
// Sending a request acknowledge back to the client if
// requested.
ackRequest(output);
// If the protocol is HTTP/1.1, chunking is allowed.
if (connector.isChunkingAllowed())
response.setAllowChunking(true);
}
}
} catch (EOFException e) {
...
}
// Ask our Container to process this request
try {
((HttpServletResponse) response).setHeader
("Date", FastHttpDateFormat.getCurrentDate());
if (ok) {
//從processor繫結的connector中獲取contianer然後把parse好的request和response傳遞給他,處理請求
connector.getContainer().invoke(request, response);
}
} catch (ServletException e) {
...
}
// Finish up the handling of the request
if (finishResponse) {
try {
//
response.finishResponse();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
request.finishRequest();
} catch (IOException e) {
ok = false;
} catch (Throwable e) {
log("process.invoke", e);
ok = false;
}
try {
if (output != null)
output.flush();
} catch (IOException e) {
ok = false;
}
}
// We have to check if the connection closure has been requested
// by the application or the response stream (in case of HTTP/1.0
// and keep-alive).
if ( "close".equals(response.getHeader("Connection")) ) {
keepAlive = false;
}
// End of request processing
status = Constants.PROCESSOR_IDLE;
// Recycling the request and the response objects
//request和response物件也是需要回收的,在newProcessor的時候呼叫HttpConnector的createxxx方法建立作為processor的成員變數
request.recycle();
response.recycle();
}
try {
shutdownInput(input);
socket.close();
} catch (IOException e) {
;
} catch (Throwable e) {
log("process.invoke", e);
}
socket = null;
}
處理請求(Container)
核心方法invoke,也就是之前的servletProcessor做的事情,通過類載入器找到對應的servlet然後讓他處理
invoke方法
public void invoke(Request request, Response response)
throws IOException, ServletException {
String servletName = ( (HttpServletRequest) request).getRequestURI();
servletName = servletName.substring(servletName.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(WEB_ROOT);
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
System.out.println(e.toString());
}
Servlet servlet = null;
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((HttpServletRequest) request, (HttpServletResponse) response);
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
}
}