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

[经验分享] Tomcat容器结构及Pipeline机制

[复制链接]

尚未签到

发表于 2015-8-6 11:38:46 | 显示全部楼层 |阅读模式
我们到底能走多远系列(13)
  扯淡:
    有机会有时间的话,我是会看那个职来职往的电视节目,个人觉得比其他一些娱乐节目对自己有帮助一些,我主要关注的还是现在人们的价值观,因为只有了解别人的价值观,也就是别人想要的,才可以更好的和他们沟通交流,从而达到目的。想必大家都听过,励志牛人说过:想要别人给你想要的,谈论他想要的。
  有时候,“慎言”是很好的习惯,多聆听,多观察,就像《闻香识女人》中的台词一样:day we stop looking, is the day we die.
  
主题:
    直接学习tomcat的valve好像有点突兀,所以还是先去了解下tomcat的一个核心的组件container
container从上一个组件connector手上接过解析好的内部request,根据request来进行一系列的逻辑操作,直到调用到请求的servlet,然后组装好response,返回给clint。
  整个大流程还是清晰明了的。毕竟我们大概了解了tomcat的输入和输出也能猜出它里面的一些必要的操作。
  先来看看container的分类吧:
Engine
Host
Context
Wrapper
它们各自的实现类分别是StandardEngine, StandardHost, StandardContext, and StandardWrapper,他们都在tomcat的org.apache.catalina.core包下。
  它们之间的关系,可以查看tomcat的server.xml也能明白(根据节点父子关系),这么比喻吧:除了Wrapper最小,不能包含其他container外,Context内可以有零或多个Wrapper,Host可以拥有零或多个Host,Engine可以有零到多个Host。
  在tomcat6里这些Standard 的 container都是直接继承抽象类:org.apache.catalina.core.ContainerBase:
DSC0000.jpg
  
  从ContainerBase这个类中的一些方法名我们可以看到各个container之间包含和被包含的操作是如何实现的,这些都是他提供的方法:



public void addChild(Container child)
public Container findChild(String name)
public Container[] findChildren()
public ObjectName[] getChildren()
public Container getParent()
public ObjectName getParentName()
public void removeChild(Container child)
  当然一个container的实现是复杂的,先跳过,未来定有机会回来学习的。
  Pipeline的机制:
  它的结构和实现是非常值得我们学习和借鉴的。
  来看看来自网络的流程图(很不错的图):
DSC0001.png
  多好的图啊,单看图你就可以了解大概的tomcat运行的情况了,哈哈。
  首先要了解的是每一种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中添加下面一行,重启服务器即可:




  注意:此行放在之前。
  例:
  1,只允许192.168.1.10访问:




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




  3,只允许192.168.1.10、192.168.1.30访问:




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




  二、局部设置,仅对具体的应用生效根据项目配置情况进行设置:
  1,使用conf目录下xml文件进行配置${tomcat_root}\conf\proj_1.xml
2,直接在server.xml中进行设置${tomcat_root}\conf\server.xml
在上述文件对应项目的前增加下面一行:




  总结:
  1,Pipeline的机制就像可配置方法的队列,类似链表的实现方式。有用,靠谱。
  2,valve配置也是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-94777-1-1.html 上篇帖子: Eclipse下配置及使用Tomcat 6.0数据库连接池连接sqlserver2000 下篇帖子: How Tomcat Works 学习-我们到底能走多远系列(8)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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