Tomcat源码分析(二)--连接处理

?目标:在这篇文章希望搞明白http请求到tomcat后是怎么由连接器转交到容器的?

在上一节里已经启动了一个HttpConnector线程,并且也启动了固定数量的HttpProcessor线程。HttpConnector用来等待http连接,得到http连接后交给其中的一个HttpProcessor线程来处理。接下里具体看一下HttpConnector是怎么得到连接得,以及HttpProcessor是怎么处理的。当启动了HttpConnector线程后(在上一节已经知道怎么启动了),便在它的run方法里面循环等待:

[java] view plaincopyprint?

  1. public void run() {
  2. // Loop until we receive a shutdown command
  3. while (!stopped) {
  4. // Accept the next incoming connection from the server socket
  5. Socket socket = null;
  6. try {
  7. socket = serverSocket.accept();
  8. if (connectionTimeout > 0)
  9. socket.setSoTimeout(connectionTimeout);
  10. socket.setTcpNoDelay(tcpNoDelay);
  11. } catch (AccessControlException ace) {
  12. log("socket accept security exception", ace);
  13. continue;
  14. } catch (IOException e) {
  15. try {
  16. // If reopening fails, exit
  17. synchronized (threadSync) {
  18. if (started && !stopped)
  19. log("accept error: ", e);
  20. if (!stopped) {
  21. serverSocket.close();
  22. serverSocket = open();
  23. }
  24. }
  25. } catch (IOException ioe) {
  26. log("socket reopen, io problem: ", ioe);
  27. break;
  28. } catch (KeyStoreException kse) {
  29. log("socket reopen, keystore problem: ", kse);
  30. break;
  31. } catch (NoSuchAlgorithmException nsae) {
  32. log("socket reopen, keystore algorithm problem: ", nsae);
  33. break;
  34. } catch (CertificateException ce) {
  35. log("socket reopen, certificate problem: ", ce);
  36. break;
  37. } catch (UnrecoverableKeyException uke) {
  38. log("socket reopen, unrecoverable key: ", uke);
  39. break;
  40. } catch (KeyManagementException kme) {
  41. log("socket reopen, key management problem: ", kme);
  42. break;
  43. }
  44. continue;
  45. }
  46. // Hand this socket off to an appropriate processor
  47. HttpProcessor processor = createProcessor();
  48. if (processor == null) {
  49. try {
  50. log(sm.getString("httpConnector.noProcessor"));
  51. socket.close();
  52. } catch (IOException e) {
  53. ;
  54. }
  55. continue;
  56. }
  57. processor.assign(socket);
  58. }
  59. // Notify the threadStop() method that we have shut ourselves down
  60. synchronized (threadSync) {
  61. threadSync.notifyAll();
  62. }
  63. }

这里很关键的就是socket = serverSocket.accept();和processor.assign(socket); 在循环里面内,serverSocket.accept();负责接收http请求然后赋值给socket,最后交给其中一个processor处理。这里processor并不是等到需要的时候再实例化,而是在HttpConnector初始化的时候已经有了若干个processor,在httpConnector里有这样一个声明:

[java] view plaincopyprint?

  1. private Stack processors = new Stack();

表明httpConnector里面持有一个包含HttpProcessor对象的栈,需要的时候拿出来就是了。看一下createProcessor函数就能比较明白了:

[java] view plaincopyprint?

  1. private HttpProcessor createProcessor() {
  2. synchronized (processors) {
  3. if (processors.size() > 0) {
  4. return ((HttpProcessor) processors.pop()); //从processors栈中弹出一个processor
  5. }
  6. if ((maxProcessors > 0) && (curProcessors < maxProcessors)) {
  7. return (newProcessor());
  8. } else {
  9. if (maxProcessors < 0) {
  10. return (newProcessor());
  11. } else {
  12. return (null);
  13. }
  14. }
  15. }
  16. }

接下来由processor.assign(socket); 记住这个方法是异步的,不需要等待HttpProcessor来处理完成,所以HttpConnector才能不间断的传入Http请求,在HttpProcessor里有两个方法比较重要,这两个方法协调处理了由HttpConnector传来的socket:

[java] view plaincopyprint?

  1. synchronized void assign(Socket socket) {
  2. // Wait for the Processor to get the previous Socket
  3. while (available) {
  4. try {
  5. wait();
  6. } catch (InterruptedException e) {
  7. }
  8. }
  9. // Store the newly available Socket and notify our thread
  10. this.socket = socket;
  11. available = true;
  12. notifyAll();
  13. if ((debug >= 1) && (socket != null))
  14. log(" An incoming request is being assigned");
  15. }
  16. private synchronized Socket await() {
  17. // Wait for the Connector to provide a new Socket
  18. while (!available) {
  19. try {
  20. wait();
  21. } catch (InterruptedException e) {
  22. }
  23. }
  24. // Notify the Connector that we have received this Socket
  25. Socket socket = this.socket;
  26. available = false;
  27. notifyAll();
  28. if ((debug >= 1) && (socket != null))
  29. log("  The incoming request has been awaited");
  30. return (socket);
  31. }

看一下HttpProcessor的run方法:

[java] view plaincopyprint?

  1. public void run() {
  2. // Process requests until we receive a shutdown signal
  3. while (!stopped) {
  4. // Wait for the next socket to be assigned
  5. Socket socket = await();
  6. if (socket == null)
  7. continue;
  8. // Process the request from this socket
  9. try {
  10. process(socket);
  11. } catch (Throwable t) {
  12. log("process.invoke", t);
  13. }
  14. // Finish up this request
  15. connector.recycle(this);
  16. }
  17. // Tell threadStop() we have shut ourselves down successfully
  18. synchronized (threadSync) {
  19. threadSync.notifyAll();
  20. }
  21. }

很明显,在它的run方法一开始便是调用上面的await方法来等待(因为一开始available变量为false),所以HttpProcessor会一直阻塞,直到有线程来唤醒它。当从HttpConnector中调用processor.assign(socket),会把socket传给此HttpProcessor对象,并设置available为true,调用notifyAll()唤醒该processor线程以处理socket。同时,在await方法中又把available设置成false,因此又回到初始状态,即可以重新接受socket。

这里处理socket的方法是process(socket),主要作用有两点,1:解析这个socket,即解析http请求,包括请求方法,请求协议等,以填充request,response对象(是不是很熟悉,在servlet和jsp开发经常用到的request,response对象就是从这里来的)。2:传入request,response对象给和HttpConnector绑定的容器,让容器来调用invoke方法进行处理。process方法主要的代码如下:

[java] view plaincopyprint?

  1. private void process(Socket socket) {
  2. input = new SocketInputStream(socket.getInputStream(),
  3. connector.getBufferSize());
  4. //解析一下连接的地址,端口什么的
  5. parseConnection(socket);
  6. //解析请求头的第一行,即:方法,协议,uri
  7. parseRequest(input, output);
  8. if (!request.getRequest().getProtocol()
  9. .startsWith("HTTP/0"))
  10. parseHeaders(input);//解析http协议的头部
  11. ..............................................
  12. connector.getContainer().invoke(request, response);
  13. .............................................
  14. }

在那些parse××方法里面会对request,response对象进行初始化,然后调用容器的invoke方法进行处理,至此,http请求过来的连接已经完美的转交给容器处理,容器剩下的问题就是要最终转交给哪个servlet或者jsp的问题。前面我们知道,一个连接会跟一个容器相连,一个级别大的容器会有一个或者多个子容器,最小的容器是Wrapper,对应一个servlet,在这里我们只要知道请求的路径决定了最终会选择哪个wrapper,wrapper最终会调用servlet的。至少一开始提出来的问题已经明白了。这里又有一个问题,在调用invoke方法是有这样的connector.getContainer的代码,即通过连接器得到跟它关联的容器,这个连接器是什么时候跟容器关联上的?详见下篇:Tomcat源码分析(三)--连接器是如何与容器关联的?

时间: 2024-10-12 08:25:58

Tomcat源码分析(二)--连接处理的相关文章

Tomcat源码分析二:先看看Tomcat的整体架构

Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Connector.Container等组件,接下来我们一起去大致的看看这些组件的作用和他们之间的相互联系.在这之前,我们先补充一个知识点,也就是Tomcat它实现的功能点是什么呢?通过查找一些资料,这里参考下极客时间<深入拆解Tomcat_Jetty>中的总结,即Tomcat 要实现 2 个核心功能:

Tomcat 源码分析(转)

Tomcat源码分析(一)--服务启动 1. Tomcat主要有两个组件,连接器和容器,所谓连接器就是一个http请求过来了,连接器负责接收这个请求,然后转发给容器.容器即servlet容器,容器有很多层,分别是Engine,     Host,Context,Wrapper.最大的容器Engine,代表一个servlet引擎,接下来是Host,代表一个虚拟机,然后是Context,代表一个应用,Wrapper对应一个servlet.从连接器     传过来连接后,容器便会顺序经过上面的容器,最

Tomcat源码分析

前言: 本文是我阅读了TOMCAT源码后的一些心得. 主要是讲解TOMCAT的系统框架, 以及启动流程.若有错漏之处,敬请批评指教! 建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动过程. 如果有不明白的地方, 再来查阅本文, 看是否能得到帮助. 我相信这样效果以及学习速度都会好很多! 1. Tomcat的整体框架结构 Tomcat的基本框架,

Tomcat源码分析——启动与停止服务

前言 熟悉Tomcat的工程师们,肯定都知道Tomcat是如何启动与停止的.对于startup.sh.startup.bat.shutdown.sh.shutdown.bat等脚本或者批处理命令,大家一定知道改如何使用它,但是它们究竟是如何实现的,尤其是shutdown.sh脚本(或者shutdown.bat)究竟是如何和Tomcat进程通信的呢?本文将通过对Tomcat7.0的源码阅读,深入剖析这一过程. 由于在生产环境中,Tomcat一般部署在Linux系统下,所以本文将以startup.s

Tomcat源码分析之—具体启动流程分析

从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息.Catalina.base信息,在initClassLoaders方法中初始化类加载器,然后通过反射初始化org.apache.catalina.startup.Catalina作为catalina守护进程: 一.load Bootstrap中load流程: 反射调用Catalina的load方法

netty 源码分析二

以服务端启动,接收客户端连接整个过程为例分析, 简略分为 五个过程: 1.NioServerSocketChannel 管道生成, 2.NioServerSocketChannel 管道完成初始化, 3.NioServerSocketChannel注册至Selector选择器, 4.NioServerSocketChannel管道绑定到指定端口,启动服务 5.NioServerSocketChannel接受客户端的连接,进行相应IO操作 Ps:netty内部过程远比这复杂,简略记录下方便以后回忆

TOMCAT源码分析(启动框架)

建议: 毕竟TOMCAT的框架还是比较复杂的, 单是从文字上理解, 是不那么容易掌握TOMCAT的框架的. 所以得实践.实践.再实践. 建议下载一份TOMCAT的源码, 调试通过, 然后单步跟踪其启动过程. 如果有不明白的地方, 再来查阅本文, 看是否能得到帮助. 我相信这样效果以及学习速度都会好很多! 1. Tomcat的整体框架结构 Tomcat的基本框架, 分为4个层次. Top Level Elements: Server Service Connector HTTP AJP Conta

spring boot实战(第十五篇)嵌入tomcat源码分析

嵌入tomcat源码分析 在启动spring boot工程时利用@SpringBootApplication注解,该注解启动@EnableAutoConfiguration自动配置,加载META-INF/spring.factories文件 # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=org.springframework.boot.autoconfigure.admin.Spri

Tomcat源码分析——Session管理分析(上)

前言 对于广大java开发者而已,对于J2EE规范中的Session应该并不陌生,我们可以使用Session管理用户的会话信息,最常见的就是拿Session用来存放用户登录.身份.权限及状态等信息.对于使用Tomcat作为Web容器的大部分开发人员而言,Tomcat是如何实现Session标记用户和管理Session信息的呢? 概要 Session Tomcat内部定义了Session和HttpSession这两个会话相关的接口,其类继承体系如图1所示. 图1 Session类继承体系 图1中额