本文繼續分析HttpProcessor類,該類實現了org.apache.catalina.Lifecycle接口和java.lang.Runnable接口
我們先分析它的構造函數
/**
* The HttpConnector with which this processor is associated.
*/
private
HttpConnector connector =
null
;
/**
* The HTTP request object we will pass to our associated container.
*/
private
HttpRequestImpl request =
null
;
/**
* The HTTP response object we will pass to our associated container.
*/
private
HttpResponseImpl response =
null
;
/**
* Construct a new HttpProcessor associated with the specified connector.
*
*
@param
connector HttpConnector that owns this processor
*
@param
id Identifier of this HttpProcessor (unique per connector)
*/
public
HttpProcessor(HttpConnector connector,
int
id) {
super
();
this
.connector =
connector;
this
.debug =
connector.getDebug();
this
.id =
id;
this
.proxyName =
connector.getProxyName();
this
.proxyPort =
connector.getProxyPort();
this
.request =
(HttpRequestImpl) connector.createRequest();
this
.response =
(HttpResponseImpl) connector.createResponse();
this
.serverPort =
connector.getPort();
this
.threadName =
"HttpProcessor[" + connector.getPort() + "][" + id + "]"
;
}
構造函數用于初始化成員變量HttpConnector connector = null、HttpRequestImpl request = null、HttpResponseImpl response = null等
當調用它的start()方法時,用于啟動處理器線程
/**
* Start the background thread we will use for request processing.
*
*
@exception
LifecycleException if a fatal startup error occurs
*/
public
void
start()
throws
LifecycleException {
if
(started)
throw
new
LifecycleException
(sm.getString(
"httpProcessor.alreadyStarted"
));
lifecycle.fireLifecycleEvent(START_EVENT,
null
);
started
=
true
;
threadStart();
}
調用threadStart()方法
/**
* Start the background processing thread.
*/
private
void
threadStart() {
log(sm.getString(
"httpProcessor.starting"
));
thread
=
new
Thread(
this
, threadName);
thread.setDaemon(
true
);
thread.start();
if
(debug >= 1
)
log(
" Background thread has been started"
);
}
?即啟動處理器線程,下面是run()方法
/**
* The background thread that listens for incoming TCP/IP connections and
* hands them off to an appropriate processor.
*/
public
void
run() {
//
Process requests until we receive a shutdown signal
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
connector.recycle(
this
);
}
//
Tell threadStop() we have shut ourselves down successfully
synchronized
(threadSync) {
threadSync.notifyAll();
}
}
下面關鍵是分析await()方法與assign()方法是怎么同步的了,這里可以理解為經典的生產者與消費者模型
我們先來看assign()方法
/**
* Process an incoming TCP/IP connection on the specified socket. Any
* exception that occurs during processing must be logged and swallowed.
* <b>NOTE</b>: This method is called from our Connector's thread. We
* must assign it to our own thread so that multiple simultaneous
* requests can be handled.
*
*
@param
socket TCP socket to process
*/
synchronized
void
assign(Socket socket) {
//
Wait for the Processor to get the previous Socket
while
(available) {
try
{
wait();
}
catch
(InterruptedException e) {
}
}
//
Store the newly available Socket and notify our thread
this
.socket =
socket;
available
=
true
;
notifyAll();
if
((debug >= 1) && (socket !=
null
))
log(
" An incoming request is being assigned"
);
}
成員變量默認為boolean available = false
下面是await()方法
/**
* Await a newly assigned Socket from our Connector, or <code>null</code>
* if we are supposed to shut down.
*/
private
synchronized
Socket await() {
//
Wait for the Connector to provide a new Socket
while
(!
available) {
try
{
wait();
}
catch
(InterruptedException e) {
}
}
//
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);
}
顯然這里是采用notifyAll()方法與wait()方法相同通信,當await()方法執行notifyAll()并返回socket后,assign()方法又可以繼續接收請求socket對象了
await()方法里面采用的是局部變量,為了不占用成員變量引用(Socket socket = null),返回的socket對象供處理器線程進行處理,又要回到run()方法了
Socket socket = await()這里也是采用了局部變量,與await()方法里面采用局部變量同樣的原因
獲取socket實例后,調用process()方法進行處理,處理完畢后將處理器對象重新入棧,最后是如果收到停止處理器線程命令,則事件通知可以停止線程了
下面關鍵是process()方法,這個方法有點長
/**
* Process an incoming HTTP request on the Socket that has been assigned
* to this Processor. Any exceptions that occur during processing must be
* swallowed and dealt with.
*
*
@param
socket The socket on which we are connected to the client
*/
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
;
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) {
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) {
//
It's very likely to be a socket disconnect on either the
//
client or the server
ok =
false
;
finishResponse
=
false
;
}
catch
(ServletException e) {
ok
=
false
;
try
{
((HttpServletResponse) response.getResponse())
.sendError(HttpServletResponse.SC_BAD_REQUEST);
}
catch
(Exception f) {
;
}
}
catch
(InterruptedIOException e) {
if
(debug > 1
) {
try
{
log(
"process.parse"
, e);
((HttpServletResponse) response.getResponse())
.sendError(HttpServletResponse.SC_BAD_REQUEST);
}
catch
(Exception f) {
;
}
}
ok
=
false
;
}
catch
(Exception e) {
try
{
log(
"process.parse"
, e);
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_BAD_REQUEST);
}
catch
(Exception f) {
;
}
ok
=
false
;
}
//
Ask our Container to process this request
try
{
((HttpServletResponse) response).setHeader
(
"Date"
, FastHttpDateFormat.getCurrentDate());
if
(ok) {
connector.getContainer().invoke(request, response);
}
}
catch
(ServletException e) {
log(
"process.invoke"
, e);
try
{
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
catch
(Exception f) {
;
}
ok
=
false
;
}
catch
(InterruptedIOException e) {
ok
=
false
;
}
catch
(Throwable e) {
log(
"process.invoke"
, e);
try
{
((HttpServletResponse) response.getResponse()).sendError
(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
}
catch
(Exception f) {
;
}
ok
=
false
;
}
//
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.recycle();
response.recycle();
}
try
{
shutdownInput(input);
socket.close();
}
catch
(IOException e) {
;
}
catch
(Throwable e) {
log(
"process.invoke"
, e);
}
socket
=
null
;
}
上面方法中,首先根據參數socket的輸入流與輸出流初始化request對象與response對象,接著是解析輸入流并填充request對象和 response對象 ,接下來調用容器對象的void invoke(Request request, Response response)方法具體進行處理
接下來清理并回收request對象和response對象
注:keepAlive表示是否是持久連接
最后是檢查輸入流是否有未讀完的字節并跳過這些字節和關閉socket實例。
下面是一個簡單的容器,實現了org.apache.catalina.Container接口,關鍵代碼如下
public
class
SimpleContainer
implements
Container {
public
static
final
String WEB_ROOT =
System.getProperty(
"user.dir") + File.separator + "webroot"
;
public
SimpleContainer() {
}
public
Loader getLoader() {
return
null
;
}
public
void
setLoader(Loader loader) {
}
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());
}
}
}
該方法與前面文章的ServletProcessor類的process()方法類似,不再具體分析
---------------------------------------------------------------------------?
本系列How Tomcat Works系本人原創?
轉載請注明出處 博客園 刺猬的溫馴?
本人郵箱: chenying998179 # 163.com ( #改為@ )
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

