设为首页 收藏本站
查看: 306|回复: 0

[经验分享] How tomcat works 第四章学习笔记(1)

[复制链接]

尚未签到

发表于 2017-2-7 06:19:35 | 显示全部楼层 |阅读模式
Tomcat4默认连接器

  tomcat连接器是一个独立的模块,可以插入到一个servlet容器。一个tomcat连接器必须符合以下要求:


  • 必须实现org.apache.catalina.Connector接口
  • 必须创建一个实现org.apache.catalina.Request接口的request对象
  • 必须创建一个实现org.apache.catalina.Response接口的response对象
  
Container接口的invoke方法:


    /**
* Process the specified Request, and generate the corresponding Response,
* according to the design of this particular Container.
*
* @param request Request to be processed
* @param response Response to be produced
*
* @exception IOException if an input/output error occurred while
*  processing
* @exception ServletException if a ServletException was thrown
*  while processing this request
*/
public void invoke(Request request, Response response)
throws IOException, ServletException;
  在invoke方法中,容器加载servlet类、调用service方法、管理session、记录错误信息日志等等。
  使用对象池来降低复杂对象的创建开销。
  *HTTP1.1新特性


  • 持久连接
  在HTTP1.0中,每对Request/Response都使用一个新的连接。
  HTTP 1.1则支持Persistent Connection, 并且默认使用persistent connection.
  connection: keep-alive


  • 块编码
  HTTP1.1支持chunked transfer,所以可以有Transfer-Encoding头部域,HTTP1.0则没有。
  Transfer-Encoding: chunked

1D\r\n
I'm as helpless as a kitten u
9\r\n
p a tree.
0\r\n

  • 状态100的使用
  100 (Continue) 状态代码的使用,允许客户端在发request消息body之前先用request header试探一下server,看server要不要接收request body,再决定要不要发request body。
  客户端在Request头部中包含Expect: 100-continue
  Server看到之后呢如果回100 (Continue) 这个状态代码,客户端就继续发request body。
  HTTP/1.1 100 Continue
  

  HttpConnector类
  (1)如何创建一个server socket?
  (2)如何维护HttpProcessor池?
  (3)如何处理Http请求?
  org.apache.catalina.connetor.http.HttpConnector类,实现了org.apache.catalina.Connector、java.lang.Runnable和org.apache.catalina.LifeCycle接口。LifeCycle接口用来维护每一个实现了此接口的Catalina组件的生命周期。


  • 创建一个server socket

Java代码





  • private ServerSocket open() throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException,  
  •     UnrecoverableKeyException, KeyManagementException {  
  •   
  •     // Acquire the server socket factory for this Connector  
  •     ServerSocketFactory factory = getFactory();  
  •   
  •     // If no address is specified, open a connection on all addresses  
  •     if (address == null) {  
  •         log(sm.getString("httpConnector.allAddresses"));  
  •         try {  
  •             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);  
  •         }  
  •     }  
  •   
  • }  




  • 如何维护HttpProcessor池
  首先采用栈来存储HttpProcessor实例,HttpProcessor池动态扩容,根据三个属性来设置:
  curProcessors: 当前HttpProcessor实例的个数
  minProcessors : int 初始化时,最小的HttpProcessor实例个数
  maxProcessors:最大HttpProcessor实例个数,当小于0时,不做限制
  初始化时创建最小HttpProcessor实例个数代码:

Java代码





  • // Create the specified minimum number of processors  
  •         while (curProcessors < minProcessors) {  
  •             if ((maxProcessors > 0) && (curProcessors >= maxProcessors))  
  •                 break;  
  •             HttpProcessor processor = newProcessor();  
  •             recycle(processor);  
  •         }  



 

  • 处理http请求
  HttpConnector类在自己的run方法中有自己的主要逻辑,直到HttpConnetor停止之前都会一直等待接收http请求。对于每一个http请求,通过调用createProcessor方法获得一个HttpProcessor实例。然而,大多数时间,createProcessor方法并不会创建一个新的HttpProcessor对象,而是从一个HttpProcessor池中获取。如果在这个池中(实际采用的是堆栈来存储)有一个HttpProcessor实例可供使用,执行出栈操作。如果栈为空且仍然没有超过HttpProcessor实例的最大个数,则创建一个新的HttpProcessor实例,否则,createProcessor方法将返回null,此时socket执行关闭操作而不会响应到来的http请求。如果createProcessor方法没有返回null,客户端的socket传递给HttpProcessor的assign方法。

Java代码





  • private HttpProcessor createProcessor() {  
  •   
  •        synchronized (processors) {  
  •            if (processors.size() > 0) {  
  •                return ((HttpProcessor) processors.pop());  
  •            }  
  •            if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {  
  •                return (newProcessor());  
  •            } else {  
  •                if (maxProcessors < 0) {  
  •                    return (newProcessor());  
  •                } else {  
  •                    return (null);  
  •                }  
  •            }  
  •        }  
  •   
  •    }  


  HttpProcessor类

 
在本章,我们最感兴趣的是HttpProcessor类如何使assign方法异步以便HttpConnetor实例可以同时为多个http请求服务。HttpProcessor类中另外一个重要的方法是私有方法process方法,它解析了http请求并且调用了容器的invoke方法。
第三章中,HttpConnetor在自己的线程中运行,然而它必须等待当前处理的http请求结束之后才可以处理下一个请求。
第三章的HttpConnetor类的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);
}
}
 HttpProcessor类的process方法在第三章中是同步方法。因此,它的run方法等待直到process方法处理结束才接收下一个请求。在本章,默认的连接器的HttpProcessor类实现了Runnable接口,因此每一个HttpProcessor实例都运行在自己的线程中,我们称为“processor线程”
 
Lifecycle接口的start和stop方法
 
 

    /**
* Prepare for the beginning of active use of the public methods of this
* component.  This method should be called before any of the public
* methods of this component are utilized.  It should also send a
* LifecycleEvent of type START_EVENT to any registered listeners.
*
* @exception LifecycleException if this component detects a fatal error
*  that prevents this component from being used
*/
public void start() throws LifecycleException;

/**
* Gracefully terminate the active use of the public methods of this
* component.  This method should be the last one called on a given
* instance of this component.  It should also send a LifecycleEvent
* of type STOP_EVENT to any registered listeners.
*
* @exception LifecycleException if this component detects a fatal error
*  that needs to be reported
*/
public void stop() throws LifecycleException;

HttpProcessor实现了Lifecycle接口,因此HttpProcessor类的start方法代码如下:
 

    /**
* 判断HttpProcessor组件是否启动
*/
private boolean started = false;
/**
* 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();
}
 
 
 

    /**
* 开启后台处理线程
*/
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");
}
   HttpProcessor的run方法的while循环执行流程:获得一个socket,处理它,调用connector的recycle(回收)方法把当前的HttpProcessor实例压回栈中。注意到while循环中停止到await方法处,await方法掌握着“processor thread”的控制流,直到它从HttpConnetor获得到一个新的socket对象。换句话说,直到HttpConnetor类调用HttpProcessor实例的assign方法。
 

            HttpProcessor processor = createProcessor();
processor.assign(socket);
 HttpProcessor的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();
}
}
将调用完的HttpProcessor实例压回栈中的代码实现:
 

  void recycle(HttpProcessor processor) {
processors.push(processor);
}
然而,await方法和assign方法运行在不同的线程中,assign方法是在HttpConnetor的run方法中被调用的,即“connector线程”。
那么assign方法是如何告诉await方法它被调用了呢?
  


利用一个布尔型变量available和java.lang.Object类的wait和notifyAll方法。
 
注:Object的wait方法导致当前线程等待直到其他线程对这个对象调用notify或者nitifyAll方法。
 
connetor线程调用的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");
}
 HttpProcessor线程调用的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);
}
 初始时,当“processor thread”刚启动时,available为false,即还没有可用的socket。因此线程在while循环中等待,直到其他线程调用notify或者notifyAll方法。也就是说,调用wait方法导致“processor thread”暂停直到“connector thread”对这个HttpProcessor实例调用notifyAll方法。当一个新的socket被分配,“connector thread”调用HttpProcessor的assign方法。avilable置为true,唤醒“processor thread”。
 
为什么await方法需要使用一个本地变量(socket)且不返回这个socket变量的实例?
 
因为HttpProcessor实例的socket变量在当前socket处理完之前还可以分配给下一个到来的socket。
 
为什么await方法需要调用notifyAll?
为了当另外一个socket到达的时候此时available为true,这时候,“connector thread”将会停止里面的assign方法直到收到“processor thread”的notifyAll方法。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-338451-1-1.html 上篇帖子: [How Tomcat Works]第3章 连接器(二) 下篇帖子: Tomcat启动异常——Unable to load configuration.
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表