Tomcat原理

来源:互联网 发布:彭雪茹 知乎 编辑:程序博客网 时间:2024/05/24 00:30

转载网址:http://www.cnblogs.com/killbug/archive/2012/11/03/2752921.html

一、Tomcat运行流程

(1)先了解下tomcat的一个核心的组件container

        container从上一个组件connector手上接过解析好的内部request,根据request来进行一系列的逻辑操作,直到调用到请求的servlet,然后组装好response,返回给clint。

       Container分类:

       Engine
       Host
       Context
       Wrapper
       它们各自的实现类分别是StandardEngine, StandardHost, StandardContext, and StandardWrapper,他们都在tomcat的org.apache.catalina.core包下。

(2)Pipeline的机制:

它的结构和实现是非常值得我们学习和借鉴的。

来看看来自网络的流程图(很不错的图):


先要了解的是每一种container都有一个自己的StandardValve
上面四个container对应的四个是:
StandardEngineValve
StandardContextValve
StandardHostValve
StandardWrapperValve

有人把vavle比作filter,而Pipeline比作filter chain,其实已经很恰当了。这里就说下我的理解:

开端:
在CoyoteAdapter的service方法里,由下面这一句就进入Container的。
connector.getContainer().getPipeline().getFirst().invoke(request, response);  
是的,这就是进入container迷宫的大门,欢迎来到Container。

Pipeline(注意上面结构图的pipeline的连线):

Pipeline就像一个工厂中的生产线,负责调配工人(valve)的位置,valve则是生产线上负责不同操作的工人。
一个生产线的完成需要两步:
1,把原料运到工人边上
2,工人完成自己负责的部分

而tomcat的Pipeline实现是这样的:
1,在生产线上的第一个工人拿到生产原料后,二话不说就人给下一个工人,下一个工人模仿第一个工人那样扔给下一个工人,直到最后一个工人,而最后一个工人被安排为上面提过的StandardValve,他要完成的工作居然是把生产资料运给自己包含的container的Pipeline上去。
2,四个container就相当于有四个生产线(Pipeline),四个Pipeline都这么干,直到最后的StandardWrapperValve拿到资源开始调用servlet。完成后返回来,一步一步的valve按照刚才丢生产原料是的顺序的倒序一次执行。如此才完成了tomcat的Pipeline的机制。

结束:

所有的vavle执行完毕后,整个响应的也就结束了。

关于Pipeline我们来看下源码:

一个StandardValve

来自org.apache.catalina.core.StandardEngineValve的invoke方法:

复制代码
public final void invoke(Request request, Response response)        throws IOException, ServletException {        // 拿到自己包含的host        Host host = request.getHost();        if (host == null) {            response.sendError                (HttpServletResponse.SC_BAD_REQUEST,                 sm.getString("standardEngine.noHost",                               request.getServerName()));            return;        }        // 最为自己pipeline上最后一位工人,负责把原料运给系一个pipeline        // 这是把所有pipeline串联起来的关键        host.getPipeline().getFirst().invoke(request, response);    }
复制代码

一个普通的valve

来自org.apache.catalina.valves.ErrorReportValveinvoke方法:

复制代码
public void invoke(Request request, Response response)        throws IOException, ServletException {        // 已经来就把原料丢给下一个vavle执行,这是把所有vavle串联成一个pipeline的关键        getNext().invoke(request, response);        // 等到上面的valve全部执行好,才开始自己的真正工作:        Throwable throwable =            (Throwable) request.getAttribute(Globals.EXCEPTION_ATTR);        if (response.isCommitted()) {            return;        }        if (throwable != null) {            // The response is an error            response.setError();            // Reset the response (if possible)            try {                response.reset();            } catch (IllegalStateException e) {                ;            }            response.sendError                (HttpServletResponse.SC_INTERNAL_SERVER_ERROR);        }        response.setSuspended(false);        try {            report(request, response, throwable);        } catch (Throwable tt) {            ;        }    }
复制代码

 

以上就描述了Pipeline机制,不知道你是不是会疑问(反正我是有了):vavle是在servlet前执行还是后执行,看上面源码的例子好像是在后执行哦? 其实是不一定的,以RemoteAddrValve为例子:

RemoteAddrValve可以根据ip地址限制访问,这个在文章后面附上了一些来自网络的配置步骤。

RemoteAddrValve源码:

复制代码
package org.apache.catalina.valves;import java.io.IOException;import javax.servlet.ServletException;import org.apache.catalina.connector.Request;import org.apache.catalina.connector.Response;public final class RemoteAddrValve    extends RequestFilterValve {    // ----------------------------------------------------- Instance Variables    /**     * The descriptive information related to this implementation.     */    private static final String info =        "org.apache.catalina.valves.RemoteAddrValve/1.0";    // ------------------------------------------------------------- Properties    /**     * Return descriptive information about this Valve implementation.     */    public String getInfo() {        return (info);    }    // --------------------------------------------------------- Public Methods    /**     * 会调用invoke方法     */    public void invoke(Request request, Response response)        throws IOException, ServletException {        // 调用了父类的process方法,我们就去看下父类的情况        process(request.getRequest().getRemoteAddr(), request, response);    }}
复制代码

上面父类的process方法

复制代码
    protected void process(String property,                           Request request, Response response)        throws IOException, ServletException {        // 在移入下一个valve是做了判断 isAllowed(property)这个方法就是用来限制IP地址的        if (isAllowed(property)) {            getNext().invoke(request, response);            return;        }        // 限制了访问,都发Error了        response.sendError(HttpServletResponse.SC_FORBIDDEN);    }
复制代码

 

只有指定的主机或IP地址才可以访问部署在Tomcat下的应用。Tomcat提供了两个参数供你配置:RemoteHostValve 和RemoteAddrValve,前者用于限制主机名,后者用于限制IP地址。

通过配置这两个参数,可以让你过滤来自请求的主机或IP地址,并允许或拒绝哪些主机 IP

一、全局设置,对Tomcat下所有应用生效

server.xml中添加下面一行,重启服务器即可:

<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.*" deny=""/>

注意:此行放在</Host>之前。

例:

1,只允许192.168.1.10访问:

<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.10" deny=""/>

2,只允许192.168.1.*网段访问:

<ValveclassName="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.*" deny=""/>

3,只允许192.168.1.10、192.168.1.30访问:

<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.10,192.168.1.30" deny=""/>

4,根据主机名进行限制:

<Valve className="org.apache.catalina.valves.RemoteHostValve"allow="abc.com" deny=""/>

二、局部设置,仅对具体的应用生效根据项目配置情况进行设置:

1,使用conf目录下xml文件进行配置${tomcat_root}\conf\proj_1.xml
2,直接在server.xml中进行设置${tomcat_root}\conf\server.xml
在上述文件对应项目的</Context>前增加下面一行:

<Valve className="org.apache.catalina.valves.RemoteAddrValve"allow="192.168.1.*" deny=""/>

总结:

1,Pipeline的机制就像可配置方法的队列,类似链表的实现方式。有用,靠谱。

2,valve配置也是tomcat配置中的比部分,具有使用价值。










0 0
原创粉丝点击