Tomcat中的Valve以及自定义Valve

Tomcat中的Valve以及自定义Valve

写这篇博客的灵感来自于最近做的一个网关系统,需要把响应流量按时间序列记录到数据库中。当我准备开始写这篇博客的时候,就在想如何以简洁的话来描述Valve以及它有什么使用场景呢?

它的作用个人总结为:org.apache.catalina.Valve是Tomcat中各个连接某些org.apache.catalina.Contained实例的责任链抽象接口

它的使用场景:能在更高层次(Filter甚至Host之前)处理Request和Response对象

1,Tomcat架构简介

我们先来看看Tomcat的架构图和server.xml文件

1

2

3

4

7

8

9

10

11

12

13

14

17

18

21

22 type="org.apache.catalina.UserDatabase"

23 description="User database that can be updated and saved"

24 factory="org.apache.catalina.users.MemoryUserDatabaseFactory"

25 pathname="conf/tomcat-users.xml" />

26

27

28

33

34

35

36

40

41

42

49

50 connectionTimeout="20000"

51 redirectPort="8443" />

52

53

59

67

76

82

94

95

96

102

103

108

109

112

113

114

117

120

121

123

124

128

129 resourceName="UserDatabase"/>

130

131

132

133 unpackWARs="true" autoDeploy="true">

134

135

137

140

141

144

145 prefix="localhost_access_log" suffix=".txt"

146 pattern="%h %l %u %t "%r" %s %b" />

147

148

149

150

151

这里我们看xml文件,这里我们看到Server节点,Service节点,Engine节点和Host节点。四个节点是依次是包含的关系,也对应了架构图中的Server,Service,Engine和Host四个板块的包含关系。从架构图我们能看到Engine,Host和Context之间有个Valve。

2,Valve接口

我们来看下org.apache.catalina.Valve接口的源码

package org.apache.catalina;

import java.io.IOException;

import javax.servlet.ServletException;

import org.apache.catalina.connector.Request;

import org.apache.catalina.connector.Response;

/**

*

A Valve is a request processing component associated with a

* particular Container. A series of Valves are generally associated with

* each other into a Pipeline. The detailed contract for a Valve is included

* in the description of the invoke() method below.

*

* HISTORICAL NOTE: The "Valve" name was assigned to this concept

* because a valve is what you use in a real world pipeline to control and/or

* modify flows through it.

*

* @author Craig R. McClanahan

* @author Gunnar Rjnning

* @author Peter Donald

*/

public interface Valve {

//-------------------------------------------------------------- Properties

/**

* @return the next Valve in the pipeline containing this Valve, if any.

*/

public Valve getNext();

/**

* Set the next Valve in the pipeline containing this Valve.

*

* @param valve The new next valve, or null if none

*/

public void setNext(Valve valve);

//---------------------------------------------------------- Public Methods

/**

* Execute a periodic task, such as reloading, etc. This method will be

* invoked inside the classloading context of this container. Unexpected

* throwables will be caught and logged.

*/

public void backgroundProcess();

/**

*

Perform request processing as required by this Valve.

*

*

An individual Valve MAY perform the following actions, in

* the specified order:

*

    *

  • Examine and/or modify the properties of the specified Request and

    * Response.

    *

  • Examine the properties of the specified Request, completely generate

    * the corresponding Response, and return control to the caller.

    *

  • Examine the properties of the specified Request and Response, wrap

    * either or both of these objects to supplement their functionality,

    * and pass them on.

    *

  • If the corresponding Response was not generated (and control was not

    * returned, call the next Valve in the pipeline (if there is one) by

    * executing getNext().invoke().

    *

  • Examine, but not modify, the properties of the resulting Response

    * (which was created by a subsequently invoked Valve or Container).

    *

*

*

A Valve MUST NOT do any of the following things:

*

    *

  • Change request properties that have already been used to direct

    * the flow of processing control for this request (for instance,

    * trying to change the virtual host to which a Request should be

    * sent from a pipeline attached to a Host or Context in the

    * standard implementation).

    *

  • Create a completed Response AND pass this

    * Request and Response on to the next Valve in the pipeline.

    *

  • Consume bytes from the input stream associated with the Request,

    * unless it is completely generating the response, or wrapping the

    * request before passing it on.

    *

  • Modify the HTTP headers included with the Response after the

    * getNext().invoke() method has returned.

    *

  • Perform any actions on the output stream associated with the

    * specified Response after the getNext().invoke() method has

    * returned.

    *

*

* @param request The servlet request to be processed

* @param response The servlet response to be created

*

* @exception IOException if an input/output error occurs, or is thrown

* by a subsequently invoked Valve, Filter, or Servlet

* @exception ServletException if a servlet error occurs, or is thrown

* by a subsequently invoked Valve, Filter, or Servlet

*/

public void invoke(Request request, Response response)

throws IOException, ServletException;

public boolean isAsyncSupported();

}

getNext和setNext方法主要是获取/设置当前这个责任链节点的上一个节点;处理逻辑的是方法invoke;isAsyncSupported方法是表示是否支持异步。例如org.apache.catalina.valves.AccessLogValve日志记录组件就是异步的,该组件是用来记录访问日志。

3,自定义Valve

这里我们使用两种方式来自定义Valve

1)原生Tomcat使用自定义Valve

2)SpringBoot使用自定义Valve

自定义Valve代码如下:

package net.sunmonkey.valves;

import org.apache.catalina.Valve;

import org.apache.catalina.connector.Request;

import org.apache.catalina.connector.Response;

import javax.servlet.ServletException;

import java.io.IOException;

/**

* @author Wenqin Cheng

* @date 2020/12/30

*/

public class MyValve implements Valve {

private Valve next;

@Override

public Valve getNext() {

return next;

}

@Override

public void setNext(Valve valve) {

next = valve;

}

@Override

public void backgroundProcess() {

}

@Override

public void invoke(Request request, Response response) throws IOException, ServletException {

System.out.println("=====================start===================");

System.out.println("#getNext().getClass().getName(): "+getNext().getClass().getName());

System.out.println(this.getClass().getName()+"#invoke");

System.out.println("request: "+request);

System.out.println("response: "+response);

System.out.println("request.getServletPath():"+request.getServletPath());

System.out.println("request.getQueryString():"+request.getQueryString());

//例如这里可以获取请求体长度,用来记录请求流量

System.out.println("request.getContentLength(): "+request.getContentLength());

//例如获取响应的流量

System.out.println("response.getBytesWritten(false): "+response.getBytesWritten(false));

System.out.println("==================end======================");

getNext().invoke(request, response);

}

@Override

public boolean isAsyncSupported() {

return true;

}

}

3.1,原生Tomcat使用自定义Valve

我们只需要在server.xml配置文件中配置就可以。如下

3.2,SpringBoot使用自定义Valve

后续更新...

至此,我们访问Tomcat的任何一个页面。可以在控制台找那个看到日志

相关推荐

淘宝商品视频怎么上传?千牛怎么上传商品视频
mobile365体育

淘宝商品视频怎么上传?千牛怎么上传商品视频

📅 08-05 👁️ 4147
东海鳌虾的产地在哪?鳌虾和龙虾有哪些区别?
best365官网体育投注

东海鳌虾的产地在哪?鳌虾和龙虾有哪些区别?

📅 07-06 👁️ 8196
2024年微球制剂概述:机制、优势、材料分类、技术挑战
人类为何出现在宇宙中 地球为何适宜人类生存
best365官网体育投注

人类为何出现在宇宙中 地球为何适宜人类生存

📅 07-20 👁️ 223
逆水寒游戏改名全攻略:获取方法与免费改名卡获取技巧
best365官网体育投注

逆水寒游戏改名全攻略:获取方法与免费改名卡获取技巧

📅 07-18 👁️ 9239
23456789打一个成语是什么__2345678猜一个成语
bt365网站

23456789打一个成语是什么__2345678猜一个成语

📅 07-02 👁️ 6569