Tomcat-Connector源码分析

《看透SpringMVC源码分析与实践》
引用 https://yq.aliyun.com/articles/20177

Connector用于接收请求并将请求封装成Request和Response来具体处理,最底层是使用Socket协议来进行连接的,在封装完成之后交给Container进行处理(即Servlet容器),Container处理完毕之后返回给Connector,最终Connector使用Socket将处理结果返回给客户端。这样整个请求就处理完成了。

Connector中具体是用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型:
三种HTTP协议,客户端与Tomcat服务器直接连接。

  • Http11Protocol : BIO,支持http1.1协议
  • Http11NioProtocol : NIO,支持http1.1协议
  • Http11AprProtocol : ARP(Apache portable runtime),支持http1.1协议 Tomcat 7.0.30版本开始,默认使用此方式。

以及三种带AJP13是定向包协议.

  • AjpProtocol : BIO,支持AJP协议
  • AjpNioProtocol,NIO,支持AJP协议
  • AjpAprProtocol,Apr ,支持AJP协议

什么是AJP
AJPv13协议是面向数据包的。WEB服务器和Servlet容器通过TCP连接来交互;为了节省SOCKET创建的昂贵代价,WEB服务器会尝试维护一个永久TCP连接(长连接)到servlet容器,并且在多个请求和响应周期过程会重用连接。

  • Apache是世界排名第一的Web服务器,它支持处理静态页面,Apache可以支持PHP。
  • Tomcat支持动态页面(servlet, JSP),对静态页面的处理不太理想。
  • Tomcat能够承担性能要求较低的WEB应用。
  • 通常会使用Apache + Tomcat的配置下,Apache处理静态页面,对于JSP则转发交给Tomcat来处理。

AJP就是Apache和Tomcat之间的通讯基本有三种方式之一,另外常见的还有`Mod_Jk、HTTP_Proxy`协议. ![这里写图片描述](https://img-blog.csdn.net/20180815215028689?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2l0X2ZyZXNobWFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 引用:http://blog.chinaunix.net/uid-20662363-id-3012760.html

ProtocolHandler有三个非常重要的组件:Endpoint,Processor和Adapter.

  • Endpoint: 用于处理底层Socket的网络协议。 Endpoint的抽象实现类AbstractEndpoint中定义了Acceptor,AsyncTimeout两个内部类和一个接口Handler
    • Acceptor: 用于监听请求。
    • AsyncTimeout: 用于异步检查request超时。
    • Handler: 用于处理接收到的Socket,在内部调用了Processor
  • Processor: 用于将Endpoint接收到的Socket封装成Request
  • Adapter: 用于将封装好的Request交给Containner进行具体处理。

Connector处理请求过程如下图:
这里写图片描述

源码分析

实例化##

Connector实例化
Connector是在Catalina的load方法中,执行createStartDigester()解析server.xml配置文件的配置信息所创建的。
server.xml-part

<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000" redirectPort="8443" />

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

java创建-流程

//org.apache.catalina.startup.Catalina.createStartDigester()
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());

//org.apache.catalina.startup.ConnectorCreateRule.begin()                         
Connector con = new Connector(attributes.getValue("protocol"));

可见最终Connector的创建流程,执行到了Connector的构造函数中。

//org.apache.catalina.Connector
public Connector(String protocol) {
	  // 将server.xml中配置的protocol “HTTP/1.1”,“AJP/1.3” 转换为上文中的6种protocol;  
      setProtocol(protocol);
      Class<?> clazz = Class.forName(protocolHandlerClassName);
		
	  //调用具体protocol的无参构造函数,实例化。
      this.protocolHandler = (ProtocolHandler) clazz.newInstance();
}

 
public void setProtocol(String protocol) {
	//若支持apr默认为:xxxAprProtocol
     if (AprLifecycleListener.isAprAvailable()) {
         if ("HTTP/1.1".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11AprProtocol");
         } else if ("AJP/1.3".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.ajp.AjpAprProtocol");
         } else if (protocol != null) {
             setProtocolHandlerClassName(protocol);
         } else {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11AprProtocol");
         }
     } else {
      //否则默认为:Http11Protocol 或者 AjpProtocol
         if ("HTTP/1.1".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11Protocol");
         } else if ("AJP/1.3".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.ajp.AjpProtocol");
         } else if (protocol != null) {
             setProtocolHandlerClassName(protocol);
         }
     }

 }

在构造函数中,最主要的就是指定ProtocolHandler的类型,并通过执行newInstance方法,创建具体的ProtocolHandler实例。

ProtocolHandler实例化
ProtocolHandler类图,如下:
这里写图片描述
从上图可以看到,ProtocolHandler有一个抽象实现类AbstractProtocol,AbstractProtocol又有AbstractAjpProtocolAbstractHttp11Protocol抽象子类,以及本文开局提到的最终的六种ProtocolHandler子类。
本文以**Http11NioProtocol**为例,分析ProtocolHandler。

//org.apache.coyote.http11.Http11NioProtocol
public Http11NioProtocol() {
	// 创建NioEndpoint
     endpoint=new NioEndpoint();
     //创建Http11ConnectionHandler
     cHandler = new Http11ConnectionHandler(this);
	//指定NioEndpoint的handler,
     ((NioEndpoint) endpoint).setHandler(cHandler);
     setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
     setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
     setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
 }

构造函数中,最主要是NioEndpoint类型的Endpoint,Http11ConnectionHandler类型的Handler并将其设置到Endpoint中。

Endpoint
Endpoint用于处理具体的连接和传输数据,AbstractEndpoint是所有Endpoint的父类,类图如下:
这里写图片描述

其中NioEndpoint中处理请求的具体流程如下,
这里写图片描述
Poller和SocketProcessor是NioEndpoint的内部类,后续会讲到。


初始化##

整个Connector的初始化过程流程见下图:

Connector初始化
根据之前的经验,Connector的生命周期方法是在Service中调用的,首先调用生命周期LifecycleBase.init(),然后再调用Connector.initInternal()。
这里写图片描述

//org.apache.catalina.Connector
protected void initInternal() throws LifecycleException {
     super.initInternal();

     //创建新的Adapter,并设置到protocolHandler,
     //Adapter的主要作用是通过自身的service方法调用Container管道中的invoke方法来处理强求
     adapter = new CoyoteAdapter(this);

	//将Adapter设置到protocolHandler中
     protocolHandler.setAdapter(adapter);

	 //省略部分代码....
	
	//初始化protocolHandler
    protocolHandler.init();

     //初始化mapperListener
     mapperListener.init();
 }

ProtocolHandler初始化
本文的ProtocolHandler默认使用Http11NioProtocol,Http11NioProtocol的初始化逻辑是调用AbstractProtocol.init() --> Endpoint.init():

Endpoint初始化
Http11NioProtocol中的Endpoint类型为:NioEndpoint,在ProtocolHandler的初始化过程中会调用NioEndpoint初始化方法, 而NioEndpoint的初始化逻辑是调用AbstractEndpoint.init() -->NioEndpoint.bind():

//org.apache.tomcat.util.net.NioEndpoint
public void bind() throws Exception {
	//初始化ServerSocketChannel
	 serverSock = ServerSocketChannel.open();
     socketProperties.setProperties(serverSock.socket());
     InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
     serverSock.socket().bind(addr,getBacklog());
     serverSock.configureBlocking(true); //mimic APR behavior
     serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());
	
	//需要启动acceptor线程的个数,如果是0,则设置为1
	if (acceptorThreadCount == 0) {
       acceptorThreadCount = 1;
     }
	
	//需要设置poller线程个数
     if (pollerThreadCount <= 0) {
         pollerThreadCount = 1;
     }
	//省略ssl....
	
   if (oomParachute>0) reclaimParachute(true);
	 
	//创建一个NioBlockingSelector
    selectorPool.open();
}

启动

启动流程如下图:
这里写图片描述

Endpoint启动

//org.apache.tomcat.util.net.NioEndpoint
 public void startInternal() throws Exception {

        if (!running) {
            running = true;
            paused = false;

            // 创建工作线程池
            if ( getExecutor() == null ) {
                createExecutor();
            }

            initializeConnectionLatch();

            // 启动Poller线程,用来处理Acceptor传递来的socket请求。
            pollers = new Poller[getPollerThreadCount()];
            for (int i=0; i<pollers.length; i++) {
                pollers[i] = new Poller();
                Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
                pollerThread.setPriority(threadPriority);
                pollerThread.setDaemon(true);
                pollerThread.start();
            }
			
			//启动Acceptor线程,用来监听接收请求;
            startAcceptorThreads();
        }
    }

这里首先初始化了一些属性,然后启动了Poller和Acceptor线程,通过这些线程或线程组用来监听请求并处理请求。

MapperListener启动

//org.apache.catalina.connector.MapperListener
public void startInternal() throws LifecycleException {
    Engine engine = (Engine) connector.getService().getContainer();
    addListeners(engine);

    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
        Host host = (Host) conHost;
        if (!LifecycleState.NEW.equals(host.getState())) {
            //注册host至mapper,同时递归的将所有的context,wrapper注册
            registerHost(host);
        }
    }
}

MapperListener启动,将所有的Context,以及Wrapper都注册值mapper属性中,供后续的请求uri匹配。
这里写图片描述


HTTP请求处理

1.Acceptor
org.apache.tomcat.util.net.NioEndpoint.Acceptor是NioEndpoint的一个内部类,它用来接收请求:

//org.apache.tomcat.util.net.NioEndpoint.Acceptor 
 protected class Acceptor extends AbstractEndpoint.Acceptor {
	  @Override
      public void run() {
        int errorDelay = 0;
		while (running) {
			//省略其他判断.....
			SocketChannel socket =  serverSock.accept();
			
			//将socket注册到Poller中的selector中。
			 if (!setSocketOptions(socket)) {
               countDownConnection();
               closeSocket(socket);
             }
		}
      }
 }

setSocketOptions()

// org.apache.tomcat.util.net.NioEndpoint
protected boolean setSocketOptions(SocketChannel socket) {
	//设置socket非阻塞。
	socket.configureBlocking(false);
	Socket sock = socket.socket();
	
	// 设置socket熟悉,如keepAlive,timeout 等信息
	socketProperties.setProperties(sock);
	
	// channel 包含
	NioChannel channel = nioChannels.poll();
	NioBufferHandler bufhandler = new NioBufferHandler(
		socketProperties.getAppReadBufSize(),
		socketProperties.getAppWriteBufSize(),
		socketProperties.getDirectBuffer());
	channel = new NioChannel(socket, bufhandler);
	channel.setIOChannel(socket);
	channel.reset();
	
	// 将chanel注册到Poller线程
	getPoller0().register(channel);

}

2.Poller
org.apache.tomcat.util.net.NioEndpoint.Poller也是NioEndpoint的一个内部类:

//org.apache.tomcat.util.net.NioEndpoint.Poller
public class Poller implements Runnable {
     
   public Poller() throws IOException {
     synchronized (Selector.class) {
	   		//获取当前线程的selector,不是bind方法中selectorPool.open();的selector
        this.selector = Selector.open();
     }
   }
	
	public void register(final NioChannel socket) {
	    socket.setPoller(this);
	    KeyAttachment key = keyCache.poll();
	    
	    /*
	     * 创建一个新的KeyAttachment,入参是socket
	     * KeyAttachment的类结构继承SocketWrapper<NioChannel>
	     */
	    final KeyAttachment ka = key!=null?key:new KeyAttachment(socket);
	    ka.reset(this,socket,getSocketProperties().getSoTimeout());
	    
	    //关注read事件
	  	ka.interestOps(SelectionKey.OP_READ);
	  	
	    PollerEvent r = eventCache.poll();
	    if ( r==null) {
	    	//PollerEvent作用:将SocketChannel注册到selector中
	    	r = new PollerEvent(socket,ka,OP_REGISTER);
	    } else {
	    	r.reset(socket,ka,OP_REGISTER);
	    }
	    //添加event
	    addEvent(r);
	}
   	
   @Override
   public void run() {
     //省略其他.....
     
     keyCount = selector.selectNow();
     if ( keyCount == 0 ) hasEvents = (hasEvents | events());
     
     //轮询准备就绪的Key
     Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;

     while (iterator != null && iterator.hasNext()) {
      SelectionKey sk = iterator.next(); //阻塞...
      KeyAttachment attachment = (KeyAttachment)sk.attachment();
      if (attachment == null) {
          iterator.remove();
      } else {
          attachment.access();
          iterator.remove();
          
          //调用processKey 处理socket
          processKey(sk, attachment);
     }
    }
  }
  
   //处理SelectionKey,使用read,write方法等。
   protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
	  unreg(sk, attachment, sk.readyOps());
		boolean closeSocket = false;
		if (sk.isReadable()) {
			//调用processSocket处理socket
		    if (!processSocket(channel, SocketStatus.OPEN_READ, true)) {
		        closeSocket = true;
		    }
		}
		if (!closeSocket && sk.isWritable()) {
		    if (!processSocket(channel, SocketStatus.OPEN_WRITE, true)) {
		        closeSocket = true;
		    }
		}
		if (closeSocket) {
		    cancelledKey(sk,SocketStatus.DISCONNECT,false);
		}
  }
}

,上述代码的作用

  • 处理Acceptor接收到NioChannel并注册到selector
  • 然后调用后续的processKey()方法,该方法用来处理NioChannel的read,write方法等。
  • 上述方法内部又调用了Endpoint.processSocket()来处理,在本方法内部主要是将请求转交给SocketProcessor
//org.apache.tomcat.util.net.NioEndpoint
 public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
 	KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
 	SocketProcessor sc = processorCache.poll();
 	//构造SocketProcessor
    if ( sc == null ) {
    	sc = new SocketProcessor(socket,status);
    } else {
    	sc.reset(socket,status);
    }
    
    if ( dispatch && getExecutor()!=null ) {
        //通过线程池处理SocketProcessor
    	getExecutor().execute(sc);
    }else {
        //调用SocketProcessor.run()处理请求
    	sc.run();
    }
 }

4.SocketProcessor

protected class SocketProcessor implements Runnable {

    protected NioChannel socket = null;
    protected SocketStatus status = null;

    public SocketProcessor(NioChannel socket, SocketStatus status) {
        reset(socket,status);
    }

    public void reset(NioChannel socket, SocketStatus status) {
        this.socket = socket;
        this.status = status;
    }
    
   public void run() {
	    SelectionKey key = socket.getIOChannel().keyFor(
	            socket.getPoller().getSelector());
	    KeyAttachment ka = (KeyAttachment)key.attachment();
	    //省略其他....
	    
	    doRun(key, ka);
   }
    
   private void doRun(SelectionKey key, KeyAttachment ka) {
    SocketState state = SocketState.OPEN;
   	 /*
   	  * 调用handler处理请求, 
   	  * 此时的handler为在Http11NioProtocol构造函数中设置的Http11ConnectionHandler(this)
   	  */
   	 state = handler.process(ka, SocketStatus.OPEN_READ); 
   }

}

SocketProcessor处理请求,将请求交给Http11ConnectionHandler处理。

5.Handler
Hander的实际类型为Http11ConnectionHandler,它的类图如下:
这里写图片描述
process()方法最终在AbstractProtocol.AbstractConnectionHandler中实现。

//org.apache.coyote.AbstractProtocol.AbstractConnectionHandler
public SocketState process(SocketWrapper<S> wrapper,
                SocketStatus status) {
	S socket = wrapper.getSocket();   
	Processor<S> processor = connections.get(socket);
	if (processor == null) {
		//获取processor实例
	    processor = createProcessor();
	}         
	//调用processor.process()
	SocketState state = processor.process(wrapper);
	
	//省略其他...
	return SocketState.CLOSED;
}

,方法的主要逻辑是

  • 获取创建Processor,此时示例类型是Http11NioProcessor
  • 然后调用Processor处理请求

6.Processor
通过Http11NioProtocol.createProcessor()获取的Processor,代码如下:

//org.apache.coyote.http11.Http11NioProtocol
public Http11NioProcessor createProcessor() {
    Http11NioProcessor processor = new Http11NioProcessor(
            proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
            proto.getMaxTrailerSize(), proto.getMaxExtensionSize());
    processor.setAdapter(proto.adapter);
    processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
    processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
  	//省略其他一些属性配置......
    register(processor);
    return processor;
}

,由上述代码可知Processor的具体类型是Http11NioProcessorprocessor.process()方法处理请求逻辑来自它的父类AbstractHttp11Proccessor

//org.apache.coyote.http11.AbstractHttp11Processor
public SocketState process(SocketWrapper<S> socketWrapper)
       throws IOException {
   RequestInfo rp = request.getRequestProcessor();
   rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);

   // Setting up the I/O
   setSocketWrapper(socketWrapper);
   getInputBuffer().init(socketWrapper, endpoint);
   getOutputBuffer().init(socketWrapper, endpoint);
   
    if (!error) {
      rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
      // 预处理,设置解析请求头,URI,METHOD等等....
      prepareRequest();
   }      
   if (!error) {
     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
     //调用Adapter执行请求
     adapter.service(request, response);
   }
}

在这个方法里面,最终请求交给Adapter处理,注意可以看到此时请求的参数是org.apache.coyote.Request,org.apache.coyote.Response

7.Adapter
Tomcat7中Adapter只有一个实现类CoyoteAdapter,处理逻辑如下:

//org.apache.catalina.connector.CoyoteAdapter
public void service(org.apache.coyote.Request req,
                    org.apache.coyote.Response res){
	
	/*
	 * 将org.apache.coyote.Request , Response
	 * 转换为org.apache.catalina.connector.Request , Response
	 */
    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);
    
    // postParseRequest:设置scheme,serverName,port,sessionId等
    boolean postParseSuccess = postParseRequest(req, request, res, response);
    if (postParseSuccess) {
    	//交给container处理请求	
    	connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
	}

	//判断当前请求类型
      AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
       if (asyncConImpl != null) {
           async = true;
       } else if (!comet) {
     		// 如果是非异步请求,则结束请求
           request.finishRequest();
           response.finishResponse();
           if (postParseSuccess && request.getMappingData().context != null) {
               ((Context) request.getMappingData().context).logAccess( request, response,System.currentTimeMillis() - req.getStartTime(), false);      
           }
           req.action(ActionCode.POST_REQUEST , null);
       }
	
}

service方法的作用,主要是完成如下2个任务:

  • 预处理请求,通过request URI的信息找到属于自己的Context和Wrapper
  • 调用container处理请求。

1).postParseRequest()
a.解析,host,context,wrapper,session等信息。

//org.apache.catalina.connector.CoyoteAdapter
 protected boolean postParseRequest(org.apache.coyote.Request req,
                                       Request request,
                                       org.apache.coyote.Response res,
                                       Response response) {
    String proxyName = connector.getProxyName();
    int proxyPort = connector.getProxyPort();
    if (proxyPort != 0) {
        req.setServerPort(proxyPort);
    }
    if (proxyName != null) {
        req.serverName().setString(proxyName);
    }
    
    //该方法主要作用是解析url中解析出来参数,并放置到request参数列表里面。
    parsePathParameters(req, request);
     
    //略...
     boolean mapRequired = true;
     while (mapRequired) {
	    // 将host,context,wrapper等属性解析存放至 request.getMappingData()
	    connector.getMapper().map(serverName, decodedURI, version,request.getMappingData());
	           
	    //设置匹配的context,wrapper                   
	    request.setContext((Context) request.getMappingData().context);
	    request.setWrapper((Wrapper) request.getMappingData().wrapper);
	          
	    //略....
	    String sessionID = null;
	    if (request.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.URL)) {     
	        //从url参数中设置的sessionId
	        //形如:protocol://host:port/path/servlet.do;jsessionid=xxxxx?username=trump 的url中包含sessionId
	        //url参数列表中session的key为 "jsessionid",定义在org.apache.catalina.util.SessionConfig.DEFAULT_SESSION_PARAMETER_NAME
	        sessionID = request.getPathParameter(SessionConfig.getSessionUriParamName(request.getContext()));
	        if (sessionID != null) {
	        		//设置sessionId,且来自URL
	            request.setRequestedSessionId(sessionID);
	            request.setRequestedSessionURL(true);
	        }
	    }
	    //从Cookie中寻找SessionId,可能会覆盖浏览器中的sessionId
	    parseSessionCookiesId(req, request);
	    parseSessionSslId(request);
		
			//略.....
	  }
      	
}

//从cookie中解析SessionId
protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {
    //如果已禁用当前通过cookie的会话跟踪上下文,不要在cookie中查找cookie中的SessionID
    Context context = (Context) request.getMappingData().context;
    if (context != null && !context.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.COOKIE)) {     
        return;
    }

    // Parse session id from cookies
    Cookies serverCookies = req.getCookies();
    int count = serverCookies.getCookieCount();
    if (count <= 0) {
        return;
    }
	
		//默认值为 "JSESSIONID"
    String sessionCookieName = SessionConfig.getSessionCookieName(context);

    for (int i = 0; i < count; i++) {
        ServerCookie scookie = serverCookies.getCookie(i);
     		//遍历cookie
        if (scookie.getName().equals(sessionCookieName)) {    		
        		//“requestedSessionId”当为空时进入if,否则进入else
            if (!request.isRequestedSessionIdFromCookie()) {
            		//仅接受第一个来自cookie的SessionID
                convertMB(scookie.getValue());
                //设置SessionId,内部逻辑为给“requestedSessionId”赋值
                request.setRequestedSessionId (scookie.getValue().toString());
                //session来自cookie设置true, 来自url设为false
                request.setRequestedSessionCookie(true);
                request.setRequestedSessionURL(false); 
            } else {
            		//判断session是否过期
                if (!request.isRequestedSessionIdValid()) { 
                		//替换sessionId,直到sessionId有效为止
                    convertMB(scookie.getValue());
                    request.setRequestedSessionId(scookie.getValue().toString());        
                }
            }
        }
    }

}

b.servlet获取session的过程
在这里插入图片描述

参考Tomcat中session的管理机制

2).Container处理请求
Containner处理请求的流程如下图:
这里写图片描述

  • StandardEngineValve.invoke():寻找host,并交由host.getPipeline()处理请求
  • StandardHostValve.invoke():设置context,并设置context.getLoader().getClassLoader(),最终交由context.getPipeline()处理请求
  • `StandardContextValve.invoke()``:寻找wrapper,并交由wrapper.getPipeline()处理请求
  • StandardWrapperValve.invoke():通过wrapper定位初&始化、javax.servlet.Servlet,并执行FilterChain执行过程中调用servlet.service()方法
//org.apache.catalina.core.StandardWrapperValve
 public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        
        //定位Servlet,并执行servlet.init()
        servlet = wrapper.allocate();
        
        //创建本次请求的filterChain
        ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); 
        ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);
        
        //省略其他....
        
        //调用filterChain,并在此方法内部执行service.service()
        filterChain.doFilter(request.getRequest(), response.getResponse());
} 
 //org.apache.catalina.core.ApplicationFilterChain
 private void internalDoFilter(ServletRequest request, 
                                  ServletResponse response)
        throws IOException, ServletException {
        ApplicationFilterConfig filterConfig = filters[pos++];
        Filter filter = null;
        filter = filterConfig.getFilter();
         
        // 省略其他.....
        servlet.service(request, response);
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要下载tomcat-redis-session-manager-1.2-tomcat-7.jar,您可以按照以下步骤进行操作: 1. 打开您的网络浏览器,进入您通常使用的搜索引擎网站,如Google或百度。 2. 在搜索框中输入“tomcat-redis-session-manager-1.2-tomcat-7.jar下载”并按下回车键。 3. 检查搜索结果,找到可靠且权威的网站,比如Apache官方网站或者Maven中央库。 4. 点击打开所选网站,并在搜索栏中输入“tomcat-redis-session-manager-1.2-tomcat-7.jar”。 5. 您会看到相关的下载链接或页面,点击下载按钮或链接。 6. 选择一个合适的下载位置或文件夹,单击“保存”或“确认”按钮开始下载。 7. 等待下载完成,这可能需要一些时间,具体取决于您的网络连接速度和该文件的大小。 8. 下载完成后,在指定的下载位置或文件夹中找到下载的jar文件。 现在,您已经成功下载了tomcat-redis-session-manager-1.2-tomcat-7.jar文件,并可以将其用于相应的项目或应用程序中。请确保在使用该jar文件之前,仔细阅读相关的文档和说明,以确保正确配置和使用。 ### 回答2: 要下载tomcat-redis-session-manager-1.2-tomcat-7.jar,您可以按照以下步骤进行操作: 1. 打开您所用的网络浏览器(如Chrome、Firefox等)。 2. 在浏览器的地址栏中输入“tomcat-redis-session-manager-1.2-tomcat-7.jar”进行搜索。 3. 找到一个可信赖的下载来源,例如官方网站、GitHub等,确保文件来源可靠。 4. 点击下载链接或按钮,开始下载jar文件。 5. 下载完成后,您可以选择保存文件到您的电脑的特定目录,以便日后使用。 6. 如果您使用的是Tomcat服务器(版本7),则可以将该jar文件放置在您的Tomcat安装目录(通常是"tomcat7/lib"文件夹)下的“lib”文件夹中。 7. 确保您已经正确配置了Tomcat服务器,以便正确使用tomcat-redis-session-manager-1.2-tomcat-7.jar文件。 8. 现在,您已经成功下载并准备好使用tomcat-redis-session-manager-1.2-tomcat-7.jar文件了。 请注意,下载jar文件是一种常见的操作,确保您从可信赖的来源下载文件,以避免潜在的安全问题。另外,请根据您所使用的Tomcat服务器的版本选择正确的jar文件。 ### 回答3: 要下载 tomcat-redis-session-manager-1.2-tomcat-7.jar,您可以按照以下步骤进行操作: 1. 确定您的计算机上已安装 Java 运行时环境(JRE)和 Apache Tomcat 7。如果没有安装,您需要先下载和安装这些软件。 2. 打开您的网络浏览器,转到可信赖的软件下载网站,例如 Apache Tomcat 的官方网站或 Maven 仓库。 3. 在搜索框中输入 "tomcat-redis-session-manager-1.2-tomcat-7.jar",然后点击搜索按钮。 4. 在搜索结果中找到正确的版本,通常它会有与您正在使用的 Tomcat 版本相匹配的名称。 5. 单击下载按钮或链接,以开始下载该文件。文件大小可能会有所不同,所以可能需要一些时间来完成下载。 6. 一旦下载完成,将 jar 文件保存到您计算机上的合适位置,例如 Tomcat 的 lib 目录。 7. 确保您的 Tomcat 服务器已关闭。如果正在运行,请停止它。 8. 打开 Tomcat 的安装目录,找到和打开 conf 目录。 9. 在 conf 目录中找到 context.xml 文件,然后用文本编辑器打开。 10. 在 context.xml 文件中找到 `<Context>` 标签,并在其内部添加以下内容: ``` <Manager className="de.javakaffee.web.session.RedisSessionManager" host="localhost" port="6379" database="0" maxInactiveInterval="60" /> ``` 根据您的 Redis 服务器设置,可能需要进行其他自定义配置,例如主机名、端口号和数据库。 11. 保存并关闭 context.xml 文件。 12. 重新启动 Tomcat 服务器,以使更改生效。 现在,您已经成功下载了 tomcat-redis-session-manager-1.2-tomcat-7.jar,并将其集成到您的 Tomcat 服务器中。您可以使用 Redis 来存储和管理在 Tomcat 上运行的应用程序的会话。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值