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

[经验分享] Tomcat对HTTP请求的处理(一)

[复制链接]

尚未签到

发表于 2017-12-25 21:23:39 | 显示全部楼层 |阅读模式
  摘要:本文主要介绍了tomcat在启动过程中为了处理HTTP请求之前所做的准备工作
  在之前的文章中介绍了tomcat的启动,关闭过程,后续又陆陆续续介绍了tomcat内部很多机制以及机制的源码。大家都知道在tomcat启动完毕以后就可以对外提供服务了,如果访问 http://localhost:8080 就可以访问tomcat的主页,那么我们今天就来查看下tomcat是如何处理这个请求的,也就是如何处理http请求的。
  之前我们查看了tomcat启动的时候的部分源码(http://www.cnblogs.com/coldridgeValley/p/5631610.html),有的地方没有深入去看。现在我们来重点看下tomcat在启动的过程中如何初始化接收请求的各种组件。

Connector构造函数初始化
  从tomcat的架构中我们知道,Connector组件是用来接收外部请求的,那么我们就从Connector的初始化来说起。
  我们之前的文章说过在启动的过程中会调用Catalina类的load()方法,在load()方法中有解析server.xml的代码如下:
  

//代码段1  
// Create and execute our Digester
  
Digester digester = createStartDigester();
  

  在createStartDigester()方法中关于Connector的具体代码如下:
  

//代码段2  digester.addRule("Server/Service/Connector", new ConnectorCreateRule());
  

  具体的规则就在ConnectorCreateRule类中:
  

//代码段3  
@Override
  
public void begin(String namespace, String name, Attributes attributes)
  throws Exception {
  Service svc = (Service)digester.peek();
  Executor ex = null;
  if ( attributes.getValue("executor")!=null ) {
  ex = svc.getExecutor(attributes.getValue("executor"));
  }
  //11111
  Connector con = new Connector(attributes.getValue("protocol"));
  if ( ex != null )  _setExecutor(con,ex);
  digester.push(con);
  
}
  

  在标注1的地方attributes.getValue("protocol")指的就是server.xml中<Connector>标签配置的属性:
  

    //代码段4  <Connector minSpareThreads="10"  maxThreads="200" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
  

  所以其实这里传递的是HTTP/1.1这个字符串。我们继续查看new Connector()源码。
  

//代码段5  
public Connector(String protocol) {
  //1111
  setProtocol(protocol);
  // Instantiate protocol handler
  try {
  Class<?> clazz =>  //222  初始化protocolHanler变量
  this.protocolHandler = (ProtocolHandler) clazz.newInstance();
  } catch (Exception e) {
  log.error(sm.getString(
  "coyoteConnector.protocolHandlerInstantiationFailed"), e);
  }
  
}
  

  查看标注1的 setProtocol()方法源码:
  

//代码段6  
public void setProtocol(String protocol) {
  

  if (AprLifecycleListener.isAprAvailable()) {
  if ("HTTP/1.1".equals(protocol)) {
  setProtocolHandlerClassName
  ("org.apache.coyote.http11.Http11AprProtocol");
  } else if ("AJP/1.3".equals(protocol)) {
  setProtocolHandlerClassName
  ("org.apache.coyote.ajp.AjpAprProtocol");
  } else if (protocol != null) {
  setProtocolHandlerClassName(protocol);
  } else {
  setProtocolHandlerClassName
  ("org.apache.coyote.http11.Http11AprProtocol");
  }
  } else {
  //1111111111
  if ("HTTP/1.1".equals(protocol)) {
  setProtocolHandlerClassName
  ("org.apache.coyote.http11.Http11Protocol");
  } else if ("AJP/1.3".equals(protocol)) {
  setProtocolHandlerClassName
  ("org.apache.coyote.ajp.AjpProtocol");
  } else if (protocol != null) {
  setProtocolHandlerClassName(protocol);
  }
  }
  

  
}
  

  
public void setProtocolHandlerClassName(String protocolHandlerClassName) {
  this.protocolHandlerClassName = protocolHandlerClassName;
  
}
  

  默认走的是标注1 的地方,所以最后就是把Connector类的protocolHandlerClassName成员变量赋值为org.apache.coyote.http11.Http11Protocol。
  继续查看Connector构造方法标注2的地方
  

//代码段7  
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
  

  也就是说Connector的初始化构造函数中把protocolHandler这个变量初始化为Http11Protocol的一个实例。(仅针对http请求)

Connector的init()方法初始化
  我们在tomcat生命周期管理的文章中部分讲解过关于Connector的init()方法,今天我们重新完整查看下:
  

//代码段8  
@Override
  
protected void initInternal() throws LifecycleException {
  //111 调用父类 注册到jmx
  super.initInternal();
  

  // Initialize adapter
  //222 新建 CoyoteAdapter对象 将新建对象设置到protocolHandler对象中
  adapter = new CoyoteAdapter(this);
  protocolHandler.setAdapter(adapter);
  

  // Make sure parseBodyMethodsSet has a default
  if( null == parseBodyMethodsSet ) {
  setParseBodyMethods(getParseBodyMethods());
  }
  

  if (protocolHandler.isAprRequired() &&
  !AprLifecycleListener.isAprAvailable()) {
  throw new LifecycleException(
  sm.getString("coyoteConnector.protocolHandlerNoApr",
  getProtocolHandlerClassName()));
  }
  

  try {
  //333 调用protocalHander的 init()方法
  protocolHandler.init();
  } catch (Exception e) {
  throw new LifecycleException
  (sm.getString
  ("coyoteConnector.protocolHandlerInitializationFailed"), e);
  }
  

  // Initialize mapper listener
  //4444  调用mapperListener的init() 方法
  mapperListener.init();
  
}
  

  代码逻辑很简单,稍微梳理下。


  • 注册到jmx
  • 新建CoyoteAdapter对象,设置到protocalHandler中。(重要 以后会提到)
  • 调用protocalHandler对象的init()方法
  • 调用mapperListener的init()方法 (没啥好看的,就是注册到jmx,至于这个对象是干嘛的,后面会看到)
  protocalHandler是 Http11Protocol的实例,查看其init()方法之前先看下Http11Protocol的继承体系方便我们查看:
DSC0000.png

  最先在AbstractHttp11JsseProtocol找到了init方法:
  

//代码段9  
@Override
  
public void init() throws Exception {
  // SSL implementation needs to be in place before end point is
  // initialized
  sslImplementation = SSLImplementation.getInstance(sslImplementationName);
  super.init();
  
}
  

  调用了super.init(),所以继续往上找,在AbstractProtocol中找到init()方法:
  

//代码段10  
@Override
  
public void init() throws Exception {
  //略代码
  try {
  //调用endpoint.init()方法
  endpoint.init();
  } catch (Exception ex) {
  getLog().error(sm.getString("abstractProtocolHandler.initError",
  getName()), ex);
  throw ex;
  }
  
}
  

  可以看到调用了endpoint变量的init()方法,而endpoint变量的初始化是在代码段5 初始化变量protocolHandler的时候进行的。
  

//代码段11  
//调用newInstance方法等于调用该类的无参构造函数
  
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
  

  
public Http11Protocol() {
  endpoint = new JIoEndpoint();
  cHandler = new Http11ConnectionHandler(this);
  ((JIoEndpoint) endpoint).setHandler(cHandler);
  setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
  setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
  setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
  
}
  

  所以我们继续查看JIoEndpoint类的init()方法,最后在其父类AbstractEndpoint中找到init()方法:
  

//代码段12  
public final void init() throws Exception {
  testServerCipherSuitesOrderSupport();
  if (bindOnInit) {
  //调用bind()方法,但是因为AbstractEndpoint类的`bind()`方法为抽象方法,所以这个留给子类实现,继续去类JIoEndpoint中查找   (模版设计模式)
  bind();
  bindState = BindState.BOUND_ON_INIT;
  }
  
}
  

  我们继续在JIOEndpoint类中查找bind()方法:
  

//代码段13  @Override
  
public void bind() throws Exception {
  

  // Initialize thread count defaults for acceptor
  //初始化 connector 默认起始链接数
  if (acceptorThreadCount == 0) {
  //如果不配置,默认是0 初始化为1
  acceptorThreadCount = 1;
  }
  // Initialize maxConnections
  // 初始化Connector的最大链接数
  if (getMaxConnections() == 0) {
  // User hasn't set a value - use the default
  setMaxConnections(getMaxThreadsExecutor(true));
  }
  // 初始化 serverSocketFactory 用来处理 socket
  if (serverSocketFactory == null) {
  if (isSSLEnabled()) {
  serverSocketFactory =
  handler.getSslImplementation().getServerSocketFactory(this);
  } else {
  serverSocketFactory = new DefaultServerSocketFactory(this);
  }
  }
  

  if (serverSocket == null) {
  try {
  if (getAddress() == null) {
  //初始化一个默认跟这个JIOEndpoint管理的socket (工厂设计模式)
  serverSocket = serverSocketFactory.createSocket(getPort(),
  getBacklog());
  } else {
  serverSocket = serverSocketFactory.createSocket(getPort(),
  getBacklog(), getAddress());
  }
  } catch (BindException orig) {
  //异常处理 略
  }
  }
  

  
}
  

  到此就是代码段8protocolHandler的init()方法调用完毕,Connector类的init()方法调用完毕。

Connector的start()方法初始化
  tomcat启动的时候在调用init()方法以后,会继续调用start()方法,继续查看,Connector的start()方法。
  

//代码段14  
@Override
  
protected void startInternal() throws LifecycleException {
  

  // Validate settings before starting
  if (getPort() < 0) {
  throw new LifecycleException(sm.getString(
  "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
  }
  

  setState(LifecycleState.STARTING);
  

  try {
  //11111
  protocolHandler.start();
  } catch (Exception e) {
  String errPrefix = "";
  if(this.service != null) {
  errPrefix += "service.getName(): \"" + this.service.getName() + "\"; ";
  }
  

  throw new LifecycleException
  (errPrefix + " " + sm.getString
  ("coyoteConnector.protocolHandlerStartFailed"), e);
  }
  //22222
  mapperListener.start();
  
}
  

  查看Http11Protocol类的start()方法:
  

//代码段15  
@Override
  
public void start() throws Exception {
  if (getLog().isInfoEnabled())
  getLog().info(sm.getString("abstractProtocolHandler.start",
  getName()));
  try {
  //调用  endpoint的start()方法
  endpoint.start();
  } catch (Exception ex) {
  getLog().error(sm.getString("abstractProtocolHandler.startError",
  getName()), ex);
  throw ex;
  }
  
}
  

  查看JIOEndpoint类的start()方法:
  

//代码段16  
public final void start() throws Exception {
  if (bindState == BindState.UNBOUND) {
  bind();
  bindState = BindState.BOUND_ON_START;
  }
  startInternal();
  
}
  

  因为在init()方法中,bindState已经初始化,所以直接调用startInternal()方法,查看JIOEndPoint()类的startInternal()方法:
  

//代码段17  @Override
  
public void startInternal() throws Exception {
  

  if (!running) {
  running = true;
  paused = false;
  

  // Create worker collection
  // 创建内部线程池 executor
  if (getExecutor() == null) {
  //1111
  createExecutor();
  }
  // 初始化 connectionLimitLatch 组件(一个同步组件,给停止的时候释放链接用的)
  initializeConnectionLatch();
  //2222
  startAcceptorThreads();
  

  // Start async timeout thread
  Thread timeoutThread = new Thread(new AsyncTimeout(),
  getName() + "-AsyncTimeout");
  timeoutThread.setPriority(threadPriority);
  timeoutThread.setDaemon(true);
  timeoutThread.start();
  }
  
}
  

  查看createExecutor()方法:
  

//代码段18  
public void createExecutor() {
  internalExecutor = true;
  TaskQueue taskqueue = new TaskQueue();
  //自定义的ThreadFactory 主要目的是为了能自定义线程池名称
  TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
  //新建一个线程池,最小线程数量是 getMinSpareThreads() ,最大是 getMaxThreads(),缓存时间60秒,使用 LinkedBlockingQueue 双端无界队列
  //如果在server.xml中没有配置的话的,默认情况 getMinSpareThreads()返回10,getMaxThreads()返回200,也就是 tomcat内部线程池默认最小线程数量10,最大200
  executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
  taskqueue.setParent( (ThreadPoolExecutor) executor);
  
}
  

  
public>  

  继续查看 startAcceptorThreads()方法:
  

//代码段19  
protected final void startAcceptorThreads() {
  //在代码段13中 acceptorThreadCount 变量被初始化为1
  int count = getAcceptorThreadCount();
  //新建Acceptor 数组
  acceptors = new Acceptor[count];
  //for 循环赋值
  for (int i = 0; i < count; i++) {
  //赋值11111
  acceptors = createAcceptor();
  String threadName = getName() + "-Acceptor-" + i;
  acceptors.setThreadName(threadName);
  //传递任务 启动
  Thread t = new Thread(acceptors, threadName);
  t.setPriority(getAcceptorThreadPriority());
  t.setDaemon(getDaemon());
  t.start();
  }
  
}
  

  
public int getAcceptorThreadCount() { return acceptorThreadCount; }
  

  可以看到 代码段19 主要做的就是新建了一个Acceptor数组并且赋值启动,而从代码中可以知道 createAcceptor()方法返回的是一个Runnable的子类,我们查看createAcceptor()方法:
  

//代码段20 在JIOEndpoint中  
@Override
  
protected AbstractEndpoint.Acceptor createAcceptor() {
  return new Acceptor();
  
}
  

  /**
  * The background thread that listens for incoming TCP/IP connections and
  * hands them off to an appropriate processor.
  

  JIOEndpoint的内部类
  */
  
protected>  

  @Override
  public void run() {
  

  int errorDelay = 0;
  

  // Loop until we receive a shutdown command
  while (running) {
  //各种条件判断略
  

  state = AcceptorState.RUNNING;
  

  try {
  //if we have reached max connections, wait
  countUpOrAwaitConnection();
  

  Socket socket = null;
  try {
  // Accept the next incoming connection from the server
  // socket
  //1111111111
  socket = serverSocketFactory.acceptSocket(serverSocket);
  } catch (IOException ioe) {
  //异常处理 略
  }
  // Successful accept, reset the error delay
  errorDelay = 0;
  

  // Configure the socket
  if (running && !paused && setSocketOptions(socket)) {
  // Hand this socket off to an appropriate processor
  //222222222
  if (!processSocket(socket)) {
  countDownConnection();
  // Close socket right away
  closeSocket(socket);
  }
  } else {
  countDownConnection();
  // Close socket right away
  closeSocket(socket);
  }
  } catch (IOException x) {
  //异常处理 略
  } catch (NullPointerException npe) {
  //异常处理 略
  } catch (Throwable t) {
  //异常处理 略
  }
  }
  state = AcceptorState.ENDED;
  }
  
}
  

  原本的源码比较长,稍微删减了一些并且加上了注释。
  在标注1的代码中,serverSocketFactory和serverSocket变量都在代码段13中进行了初始化,所以我们查看下acceptSocket()方法。
  

//代码段21  
@Override
  
public Socket acceptSocket(ServerSocket socket) throws IOException {
  return socket.accept();
  
}
  

  很简单就是调用了socket的accept()方法,但是众所周知accept()是阻塞的方法,所以代码段19中新建的线程就会阻塞的这里,那这个阻塞是干什么的呢? 其实这个阻塞就是在接收外部的请求,紧接着就是标注2的地方开始处理请求了,具体的处理请求流程我们在下一篇文章中继续。
  到此就是代码段14的Connector的start()方法中protocolHandler.start()方法的源码我们都看完了,我们继续查看start()方法中的mapperListener.start()的源码。
  

//代码段22  /**
  * Mapper.
  */
  protected Mapper mapper = new Mapper();
  /**
  * Mapper listener.
  */
  protected MapperListener mapperListener = new MapperListener(mapper, this);
  

  

  
@Override
  
public void startInternal() throws LifecycleException {
  //设置生命周期状态
  setState(LifecycleState.STARTING);
  

  // Find any components that have already been initialized since the
  // MBean listener won't be notified as those components will have
  // already regis tered their MBeans
  // 设置 defaultHostName 变量
  findDefaultHost();
  //将engine 以及engine以下所有的自容器全部添加监听器  mapperListener
  Engine engine = (Engine) connector.getService().getContainer();
  addListeners(engine);
  

  Container[] conHosts = engine.findChildren();
  for (Container conHost : conHosts) {
  Host host = (Host) conHost;
  if (!LifecycleState.NEW.equals(host.getState())) {
  // Registering the host will register the context and wrappers
  //11111111
  registerHost(host);
  }
  }
  
}
  

  
/**
  
* 获取默认的 host
  
*/
  
private void findDefaultHost() {
  //先获取Engine
  Engine engine = (Engine) connector.getService().getContainer();
  //获取Engine对象中配置的 defaultHost 变量 ( server.xml 中配置的)
  String defaultHost = engine.getDefaultHost();
  

  boolean found = false;
  if (defaultHost != null && defaultHost.length() >0) {
  //获取engine的所有 子容器(Host)
  Container[] containers = engine.findChildren();
  

  for (Container container : containers) {
  Host host = (Host) container;
  //遍历所有Host,如果Engine中默认的host名称和 当前host名称相同,设置found为true
  if (defaultHost.equalsIgnoreCase(host.getName())) {
  found = true;
  break;
  }
  

  String[] aliases = host.findAliases();
  //同上 只是使用host的别名查找
  for (String alias : aliases) {
  if (defaultHost.equalsIgnoreCase(alias)) {
  found = true;
  break;
  }
  }
  }
  }
  

  if(found) {
  //如果 存在就 设置 defaultHostName 变量
  mapper.setDefaultHostName(defaultHost);
  } else {
  log.warn(sm.getString("mapperListener.unknownDefaultHost",
  defaultHost, connector));
  }
  
}
  

  
/**
  * Add this mapper to the container and all child containers
  *
  * @param container
  */
  
private void addListeners(Container container) {
  //把mapperListener添加到该容器的监听器中
  container.addContainerListener(this);
  container.addLifecycleListener(this);
  //遍历子容器
  for (Container child : container.findChildren()) {
  //递归调用该方法,也就是把mapperListener设置到所有的容器中
  addListeners(child);
  }
  
}
  

  可以看到mapperListener的start()方法中简单的部分都直接添加了注释,还有标注1的地方没有说明。标注1的地方registerHost()就不深入查看了,主要原因是内部代码太过繁琐,但是逻辑很清楚,所以这里就直接讲解原理,不查看源码分析了。registerHost()主要作用是把Host,Context,Wrapper容器全部注册到mapperListener中,以供后续方法使用!
  我们查看下MapperListener类的构造:
  

//代码段23  /**
  * Associated mapper.
  */
  
private Mapper mapper = null;
  

  
/**
  * Associated connector
  */
  
private Connector connector = null;
  

  可以看出mapperListener和Connector互相持有对方的引用。继续看Mapper类:
  

/**  * Array containing the virtual hosts definitions.
  */
  
Host[] hosts = new Host[0];
  

  Mapper内部持有Host数组,而这个Host并非我们常说的 那个容器类Host:
  

protected static final>

  public volatile ContextList contextList;
  

  
}
  

  这个Host是Mapper的一个内部类,持有了一个ContextList对象,继续查看:
  

protected static final>

  public final Context[] contexts;
  

  

  ContexetList依旧是Mapper的一个内部类,持有了一个Context数组,继续查看Context类的定义:
  

protected static final>public volatile ContextVersion[] versions;
  

  public Context(String name, ContextVersion firstVersion) {
  super(name, null);
  versions = new ContextVersion[] { firstVersion };
  }
  
}
  

  Context是Mapper的一个内部类,持有了ContextVersion的数组,查看ContextVersion:
  

protected static final>public String path = null;  public int slashCount;
  public String[] welcomeResources = new String[0];
  public javax.naming.Context resources = null;
  public Wrapper defaultWrapper = null;
  public Wrapper[] exactWrappers = new Wrapper[0];
  public Wrapper[] wildcardWrappers = new Wrapper[0];
  public Wrapper[] extensionWrappers = new Wrapper[0];
  public int nesting = 0;
  public boolean mapperContextRootRedirectEnabled = false;
  public boolean mapperDirectoryRedirectEnabled = false;
  private volatile boolean paused;
  

  

  可以看到,ContextVersion中持有了 3个Wrapper的数组。具体说到这里可以看出 MapperListener内部包含了所有的Host以及Host的所有自容器,一直到Wrapper,也许看起来不是很直观,我们debug下看下最终MapperListener的结构:
DSC0001.jpg

  到此为止,整个Connector从构造函数初始化,到启动的时候调用init()方法一直到初始化完毕准备接收外部请求的准备工作的源码已经查看完毕,tomcat处理请求的源码我们下篇文章继续查看。

运维网声明 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-428036-1-1.html 上篇帖子: 众人拾柴火焰高之Tomcat集群 下篇帖子: tomcat配置jndi+spring使用jndi数据源
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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