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

[经验分享] Tomcat4 源代码分析 (5) How Tomcat Works 第五章

[复制链接]

尚未签到

发表于 2017-2-6 11:55:04 | 显示全部楼层 |阅读模式
  一个container是一种模块,处理对servlet的请求并且返回response对象给web client端. 一个container由org.apache.catalina.Container接口定义.一共有四种类型的container:engine,host,context和wrapper.本章将会介绍context和wrapper,剩下两种会在13章中介绍.本章开始于对container接口的讨论,接着讲述在container中pipelining的原理.然后具体介绍wrapper和context接口.本章结尾给出了两个应用程序,分别实现了简单的wrapper和context.
   
   1 Container接口
  Container必须实现org.apache.catalina.Container接口。有四种不同概念层次的container,
  Engine:代表整个Catalina servlet引擎。
  Host:代表一个虚拟host,其中包含若干context。
  Context:代表一个web application,其中可包含若干wrapper。
  Wrapper:代表一个独立的servlet。
  在org.apache.catalina.core包中,StandardEngine, StandardHost, StandardContext, StandardWrapper是四个标准实现。
  这四种引擎不都是必须的。比如本章的第一个application只包含一个wrapper。第二个application包含一个context和wrapper。
  一个Container可以有0个或多个低一层的child container。比如,一个context可以有若干个wrapper,host可以有若干context。因为wrapper在最底层,所以是没有child container的。加减一个孩子的方法在接口中有定义,
  public void addChild(Container child)
  public void removeChild(Container child)
  Container接口还可以查找child container,
  public Container findChild(String name)
  public Container[ ] findChildren()
  Container还有一些辅助性的组件,比如Loader, Logger, Manager, Realm, Resources,后面几章会有介绍。Container接口对这些组件有相应的get set方法。
  需要强调的是,Tomcat管理员可以在configuration文件(server.xml)中设置container的性能。这是通过container中的pipeline和一组valve(阀门,管子)来实现的。下一节“Pipelining Tasks”会有讨论。
  2、Pipelining Tasks
  这部分内容讲解了connector调用的container的invoke方法都具体做了什么。这一节会介绍4个相关的接口:Pipeline, Valve, ValveContext, Contained。他们都在包org.apache.catalina中。
  Pipeline包含Container调用的所有的任务。一个valve代表一个具体的任务。在container的pipeline中有一个基本的valve,但是你可以加入任意多的valve。valve的个数是指后加入的个数,并不包括那个基本的valve。有趣的是,valve可以动态的加入到Tomcat的configuration文件(server.xml)中。
  pipeline和valve理解起来很象是servlet filter。pipeline是一个filter chain,每一个valve是一个filter。一个valve的任务完成后,下一个valve的任务会被调用。那个basic valve总是被最后调用。可以想象,这个过程可以用下面的伪代码描述:
  for (int n=0;n<valve.length;n++) {
      valve[n].invoke(...);
  }
  basicValve.invoke(...);
  但是,Tomcat并没有用上述方式,而是引入了org.apache.catalina.ValveContext接口。Container的invoke()方法被调用时,里面调用了pipeline的invoke()方法。pipeline里的invoke方法有着和Container中一样的定义:
  public void invoke(Request request, Response response) throws...
  pipeline作为一个属性关联到一个Container对象。
  现在pipeline需要确定每一个关联到他的valve都被执行一次。pipeline通过ValveContext接口的一个实例来完成这个任务。ValveContext作为pipeline的一个内部类实现,所以他可以访问到pipeline中的每一个属性。
  ValveContext中最重要的方法是:
  public void invokeNext(Request request, Response response) throws....
  Valve中的invoke()方法为:
  public void invoke(Request request, Response response, ValveContext vc)....
  valveContext的invokeNext先调用第一个valve的invoke方法,并且他自己传给valve的invoke方法,然后再由valve的invoke来调用valveContext的invokeNext()。
  简言之,valvecontext, pipeline, valve 类之间的关系如下:
  class pipeline {
      valve[] avalve;
      function invoke(request, response) {
          new valvecontext().invokeNext();
      }
      inner class valvecontext {
          invokeNext(request, response) {
              avalve.invoke(request, response, this);
          }
      }
  }
  class valve {
      invoke(request, response, valvecontext) {
          // TO DO ...
          this.invokeNext(request, response);
      }
  }
  pipeline和container的关系是:pipeline是container的一个成员变量
  用这种方式,多个valve依次被调用。
   
  ==========================================
  PS: 上述3个类  和  container 又有什么关系?
  ==========================================
  3、The Wrapper Application
      这里需要提一下将在12章中介绍的Context接口。这个接口代表了一个web application。他通常含有多个wrapper作为他的孩子。重要的方法包括addWrapper, createWrapper...
      转入正题。
      本章wrapper的一个简单的实现是 ex05.pyrmont.core.SimpleWrapper。包含了一个Pipeline(ex05.pyrmont.core.SimplePipeline), 一个Loader(ex05.pyrmont.core.SimpeLoader)来加载一个servlet。
      这个pipeline中包含了一个基本的valve(SimpleWrapperValve)和两个additional的valve(ClientIPLoggerValve , HeaderLoggerValve)。
      这个loader的工作过程 和 前几章的加载类的过程完全一样。只是功能给抽离出来而已。
      SimpeWrapperValve的重要作用。
      作为基本的valve。SimpleWrapperValve的做用是 调用 servlet的service方法。方法调用的地方是在valve 的 invoke方法中。
      另外两个附加的valve,作用就是在simplewrappervalve调用invoke之前,做点事情。(filter的功能应该就是在这里实现的吧)。
  ex05.pyrmont.startup.Bootstrap1 启动类的实现如下:
      HttpConnector connector = new HttpConnector();//新建连结器
    Wrapper wrapper = new SimpleWrapper();//新建容器,初始化方法会设置basicValve
    wrapper.setServletClass("ModernServlet");//设置容器中的servlet
    Loader loader = new SimpleLoader();//新建类装载器
    Valve valve1 = new HeaderLoggerValve();//新建additional valve
    Valve valve2 = new ClientIPLoggerValve();//新建additional valve
      wrapper.setLoader(loader);// 设置容器的类装载器
    ((Pipeline) wrapper).addValve(valve1);//设置容器的additional valve
    ((Pipeline) wrapper).addValve(valve2);//设置容器的additional valve
      connector.setContainer(wrapper);//设置连结器对应的容器
      try {
      connector.initialize();
      connector.start();
    }
    catch (Exception e) {
    }
  4、 The Context Application
      这个应用程序展示了包含两个wrapper的容器(也即两个servelt)。一个以上的wrapper就需要引入一种新的组件,叫做mapper。mapper组件的作用是根据特定的request来选择哪个wrapper来处理。
      mapper的接口定义如下:
  public interface Mapper {
        public Container getContainer();
  public void setContainer(Container container);
        public String getProtocol();
        public void setProtocol(String protocol);
        public Container map(Request request, boolean update);//用来返回一个child container
  }
      回忆一下,从container层次的角度来说,wrapper在最底层,context在wrapper层之上。
      本章中这个context的例子,用了一个loader和两个valves。这三个组件都和context相关联。这样他们可以被底层的wrapper所共享。这个context被指定为connector的容器。下面列出的是这个容器(即context)的执行顺序:
      a. context有一个pipeline。这个container的invoke方法调用pipeline的invoke方法。
      b. pipeline的invoke方法调用所有context的valve的invoke方法。
      c. 在context层的basic valve中,用mapper找到一个child wrapper来处理特定的请求
      d. 找到了child wrapper后,wrapper的各个valve在被依次调用。
      ex05.pyrmont.core.SimpleContextValve
      ex05.pyrmont.core.SimpleContextMapper
      ex05.pyrmont.core.SimpleContext
      这三个类分别就是 context层的basic valve、context的孩子wrapper的影射、context层  的具体的实现类。
      启动类bootstrap2.java的主要部分如下:
  HttpConnector connector = new HttpConnector();//新建连结器
    Wrapper wrapper1 = new SimpleWrapper();//新建一个wrapper
    wrapper1.setName("Primitive");
    wrapper1.setServletClass("PrimitiveServlet");
    Wrapper wrapper2 = new SimpleWrapper();//新建另一个wrapper
    wrapper2.setName("Modern");
    wrapper2.setServletClass("ModernServlet");
  Context context = new SimpleContext();//新建一个context
    context.addChild(wrapper1);//为context添加一个wrapper
    context.addChild(wrapper2);//为context再添加一个wrapper
  Valve valve1 = new HeaderLoggerValve();//新建valve
    Valve valve2 = new ClientIPLoggerValve();//新建valve
  ((Pipeline) context).addValve(valve1);// context层添加valve
    ((Pipeline) context).addValve(valve2);// context层添加valve
  Mapper mapper = new SimpleContextMapper();//新建一个mapper
    mapper.setProtocol("http");
    context.addMapper(mapper);
    Loader loader = new SimpleLoader();//新建一个loader
    context.setLoader(loader);
    // context.addServletMapping(pattern, name);
    context.addServletMapping("/Primitive", "Primitive");//建立wrapper的mapping
    context.addServletMapping("/Modern", "Modern");
    connector.setContainer(context);// 为连结器设置容器
    try {
      connector.initialize();
      connector.start();
  // make the application wait until we press a key.
      System.in.read();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
   运行启动类后,在ie中请求 http://localhost:8080/Modern   http://localhost:8080/Primitive 。容器context接受请求。在执行完context层的valve后,会根据 /Modern /Primitive 来找到对应的wrapper,来继续执行,进而调用具体的servlet。
   
  -- 完毕 欢迎各位同仁共同交流:)

运维网声明 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-338348-1-1.html 上篇帖子: Tomcat的Session管理(一) 下篇帖子: Coder 爱翻译 How Tomcat Works 第九章 第一部分
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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