暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Valve的配置与自定义Valve(Valve系列之二)

中间件技术讨论圈 2016-08-07
1883

我们前面讲解过,Tomcat的Valve其实一共可以分成三类:

第一类:自定义的Valve(就是本文要讲的内容)

第二类:各个核心容器组件的BaseValve(例如之于StandardContext就是StandardContextValve

第三类:Tomcat内置的一些功能级的Valve,例如AccesslogValve,RemoteAddrValve等

本文主要带你浏览一下ValveBase的主要功能,并为Tomcat自定义一个具有实用意义的Valve;

1.ValveBase


ValveBase是所有Valve的基类,如果你要自己进行自定义Valve的话,那么就需要继承这个类;

LifecycleMBeanBase代表该Valve是MBean监控的,Contained代表其实例化的类是容器对象,通过Tomcat的生命周期Lifecycle进行启停,而Valve实际上就是阀门的接口;

对于Valve接口来说,首先其是一个责任链,所以需要实现setNext,getNext接口


来完成一个责任链中的Valve的传递,因此这两个方法是需要实现的,

关于这两个方法,在ValveBase的基类中已经实现,持有一个next的属性:


另外,一个比较重要的方法invoke,这个就是自定义Valve必须实现的;

我们来看看Tomcat的核心Valve的这个invoke方法干了什么:


作为StandardEngine的BaseValve来说,其主要是通过request找到虚拟主机,然后执行虚拟主机Host中配置的第一个Valve,也就是下图中的箭头所指:


总结一下,ValveBase是自定义Valve需要实现的基类,而你只需要实现其invoke方法即可;


2.自定义Valve

我们自己可以定义一个Valve,我们来分析网络上比较热的一个用户经常自定义的RequestDumperValve 

这个RequestDumperValve 主要的作用是打印request,response的dump信息,虽然在Tomcat配置了一个AccessLogValve,但还是不能去自定义化,按照自己的思路随心所欲的进行定制,这个时候,你就可以利用这个Valve的机制,单单实现ValveBase的基类,然后重写invoke方法即可:

public void invoke(Request request, Response response) 

throws IOException, ServletException { 

Log log = container.getLogger(); 

// Log pre-service information 

log.info("REQUEST URI =" + request.getRequestURI()); 

...... 

log.info(" queryString=" + request.getQueryString()); 

...... 

log.info("-------------------------------------------------------"); 

// 调用下一个valve,前面是request,后面是response

getNext().invoke(request, response); 

// Log post-service information 

log.info("-------------------------------------------------------"); 

...... 

log.info(" contentType=" + response.getContentType()); 

Cookie rcookies[] = response.getCookies(); 

for (int i = 0; i < rcookies.length; i++) { 

    log.info(" cookie=" + rcookies[i].getName() + "=" + 

rcookies[i].getValue() + "; domain=" + 

rcookies[i].getDomain() + "; path=" + rcookies[i].getPath()); 

String rhnames[] = response.getHeaderNames(); 

for (int i = 0; i < rhnames.length; i++) { 

    String rhvalues[] = response.getHeaderValues(rhnames[i]); 

    for (int j = 0; j < rhvalues.length; j++) 

log.info(" header=" + rhnames[i] + "=" + rhvalues[j]); 

}

log.info(" message=" + response.getMessage()); 

log.info("========================================================"); 

}

在这个程序中,我们需要注意的是,红色部分的位置,因为我们的这个Valve一半的功能是要dump request,另外一半是要dump response,所以getNext().invoke(request, response); 这句话的位置是很考究的;


对于这个 RequestDumperValve 的配置,因为在Tomcat的容器核心组件都有Pipeline,所以我们可以配置在任意的环节上,例如Context,Engine上都行。

例如,如果需求是要dump一下某个虚拟主机下所有应用的Valve的话,那么我们就需要进行如下配置:

<Host name="yuantong"  ....>

      <Valve className="com.yuantong.RequestDumperValve"/>

      <Context ...>          

      </Context>

      <Context ....>          

      </Context>

</Host>

这样监视的内容就是虚拟主机yuantong下面的所有的Context应用。

Valve的配置,只需要你声明你的自定义的Valve的className属性,这个className属性是容器组件必备的,其余的属性可以根据你的Valve进行定制,例如Tomcat的Access_Log_Valve 中就有很多的属性,只需完成其get,set,属性就会自动进行装入;而对于上面定义的RequestDumperValve 是没有什么需要配置的属性罢了;



总结:

自定义Valve需要继承ValveBase,并实现invoke方法,配置在你所希望配置的容器组件下即可;


后续会回到Wrapper这个话题中,对于Valve的核心Valve,和Tomcat自定义的10几个Valve,后面会逐个进行分析,敬请期待!


文章转载自中间件技术讨论圈,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论