《看透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两个内部类和一个接口HandlerAcceptor:
用于监听请求。AsyncTimeout:
用于异步检查request超时。Handler:
用于处理接收到的Socket,在内部调用了Processor
Processor
: 用于将Endpoint接收到的Socket封装成RequestAdapter
: 用于将封装好的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又有AbstractAjpProtocol
和AbstractHttp11Protocol
抽象子类,以及本文开局提到的最终的六种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的具体类型是Http11NioProcessor
,processor.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的过程
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);
}