Java-第十五部分-源码解读-Tomcat-架构关系和init/start/invoke流程

183 阅读6分钟

源码解读全文

Tomcat

  • 下载源码
  • 配置pom文件
  • 启动org.apache.catalina.startup.BootStrap下的main方法
  • JNDI Java Naming and Directory Interface,在一个中心注册一个东西,只需要配置这个东西的属性参数,即可使用,类似properties文件的配置

架构

image.png

conf下的server.xml配置文件

  • server服务器
  1. Listener
  2. GlobalNamingResources指定一些资源的配置信息,@Resource,提供了JNDI的实现
  3. Service,服务,一个服务器可以有多个服务,真正处理服务,不同的服务,端口不同,功能不同
  • Service,其中Catalina用来处理Http请求
  1. Connector,连接器,绑定监听的端口,进行接受数据,处理请求
  2. Engine,引擎,服务交给引擎处理
  • Engine
  1. Realm,认证配置
  2. Host,主机,域名,一个引擎可以有多个host

接口关系

  • 核心组件,都继承了LifecycleLifecycleBase,模板方法,定义好步骤,核心步骤留给子类实现 image.png
  1. 初始化init()
  2. 启动start()
  3. 停止stop()
  4. 销毁destory()
  5. 当前组件状态LifecycleState getState()
  • Container容器特点
  1. addChild(Container child);
  2. getParent();拥有父类
  3. getPipeline();拥有管道
  • Pipeline管道特点,数据交给引擎后,每流过一层,都要经历每一层容器的管道的所有阀门
  1. getValves();获取所有阀门,阀门机制,用来进行对数据干预的,AccessLogValve访问日志的阀门
  2. getBasic();获取基本阀门 image.png
  • Valve阀门,责任链模式
  1. getNext();获取下一个
  2. void invoke(Request request, Response response)阀门执行

启动流程

  • Bootstrap.main,调用bootstrap.init();
  • 解析命令,默认是startString command = "start";
  • daemon.load(args);加载命令行参数信息,其中解析server.xml,并且进行服务器初始化
  1. 为配置文件的每一个标签创建一个对应的类,封装好标签配置的属性内容
  2. 通过NIO - ServerSocketChannel绑定端口,准备接受数据
  • daemon.start();启动服务器,反射调用Catalinastart

bootstrap.init

  • 初始化类加载器initClassLoaders();
  1. 加载应用jar
  2. 加载lib下的jar
  3. 加载自己写的代码
  • 加载Catalina启动类,Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
  • 配置Catalina类加载器
  • catalinaDaemon = startupInstance;当前Catalina实例是守护线程

daemon.load(args)

  • 实际上,通过反射调用catalinaDaemonload方法
Method method =
    catalinaDaemon.getClass().getMethod(methodName, paramTypes);
  • 解析服务器的server.xml文件,parseServerXml(true);
  • 此时只包括xml文件中的内容Server s = getServer(); image.png
  • 服务器初始化getServer().init();

parseServerXml

  • 加载资源ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(Bootstrap.getCatalinaBaseFile(), getConfigFile()));

getConfigFile()"conf/server.xml";

  • 获取解析器Digester digester = start ? createStartDigester() : createStopDigester();
  • 解析xml文件,digester.parse(inputSource);,通过读取器解析getXMLReader().parse(input);
  • 按照层级架构封装成Catalina对象 image.png

getServer().init()

  • 调用父类LifecycleBase的模版方法,其中initInternal();由子类实现
  • 调用子标签GlobalNamingResources JNDI初始化,globalNamingResources.init();
  • 初始化service
for (Service service : services) {
    service.init();
}

daemon.start()

  • 反射调用CatalinastartMethod method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
  • 服务器启动 getServer().start();

层层启动组件,并且通过父类LifecycleBase的模版方法,调用子类真正的启动方法,逻辑与初始化一致

  • Catalina启动完成后,调用getServer().await();,阻塞服务器,接受网络请求,监听8005的端口
  • 设置服务器套接字,并进行等待awaitSocket = new ServerSocket(getPortWithOffset(), 1, InetAddress.getByName(address));
  • 服务器进行循环,socket = serverSocket.accept();开启网卡端口(跟实际服务的端口不一致),接受netscoket

ServerSocket serverSocket = awaitSocket; image.png

StandardServer.startInternal()

  • globalNamingResources.start();
  • service.start();
for (Service service : services) {
    service.start();
}

service.init()

  • 过程中通过NIO绑定了端口,准备好socket
  • 调用父类LifecycleBase的模版方法,其中initInternal();由子类实现
  • 引擎初始化,engine.init();
  • 线程池初始化,executor.init();
  • 监听器初始化,mapperListener.init();
  • 连接器初始化,connector.init();

engine.init()

  • 调用父类LifecycleBase的模版方法,其中initInternal();由子类实现
  • 配置RealmgetRealm();

connector.init()

  • 过程中通过NIO绑定了端口,准备好socket
  • 调用父类LifecycleBase的模版方法,其中initInternal();由子类实现
  • 给协议处理器设置适配器adapter = new CoyoteAdapter(this); protocolHandler.setAdapter(adapter);
  • 协议处理器初始化protocolHandler.init();
  1. endpoint.init();端口初始化

endpoint.init()

  • bindWithCleanup();绑定端口,其中调用bind();,底层NIO - ServerSocketChannel绑定端口 image.png

service.start();

  • engine.start();
  • executor.start();
  • mapperListener.start();
  • connector.start();

engine.start()

  • 部署项目
  • ((Lifecycle) cluster).start();集群启动
  • ((Lifecycle) realm).start();
  • 找到所有子容器Host/Context/Wrapper,封装成StartChild
  1. StartChild实现了Callable<Void>,其中call方法进行了start,交给线程池执行,实现递归后台启动所有子容器 image.png
  • ((Lifecycle) pipeline).start(); 管道启动

host.start

  • 获取所有阀门Valve[] valves = getPipeline().getValves();,检查所有的阀门

errorValve.equals(valve.getClass().getName())

  • 添加ErrorReportValve阀门

Context.start()

  • 处理工作目录postWorkDirectory();
  • 发送生命周期事件fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
  1. ContextConfig监听到,调用webConfig();
  2. wrapper.setServletClass(servlet.getServletClass());设置servletClass,为接受到请求,创建其他servlet作准备
  • 创建启动时加载的servletloadOnStartup(findChildren()),其中调用wrapper.load(),对servlet进行加载和初始化

wrapper保存servlet信息,对于MVC中,此时初始化DispatcherServlet

pipeline.start()

  • 设置基础阀门current = basic;
  • 启动所有阀门

过程中进行init()初始化,并设置状态值setState(LifecycleState.STARTING);

while (current != null) {
    if (current instanceof Lifecycle) {
        ((Lifecycle) current).start();
    }
    current = current.getNext();
}

connector.start()

  • 协议处理器启动protocolHandler.start();Http1.1 NIO image.png image.png

protocolHandler.start()

  • 端点启动endpoint.start();
  • 创建work线程,真正处理请求的线程createExecutor();
  1. 类似Netty中的workerGroup
  2. 默认10个,最多200,队列无边界(Integer.MAX_VALUE),executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
  • 连接限流initializeConnectionLatch();

connectionLimitLatch = new LimitLatch(getMaxConnections());,最大8*1024个连接

  • 启动poller = new Poller();,并直接单线程启动pollerThread.start(); image.png
  • 启动接受者线程startAcceptorThread();
protected void startAcceptorThread() {
    acceptor = new Acceptor<>(this);
    String threadName = getName() + "-Acceptor";
    acceptor.setThreadName(threadName);
    Thread t = new Thread(acceptor, threadName);
    t.setPriority(getAcceptorThreadPriority());
    t.setDaemon(getDaemon());
    t.start();
}

Tomcat中的线程

  • NIO的处理流程
  • acceptor

Acceptor

  • 监听8080端口,接受连接socket = endpoint.serverSocketAccept();

serverSock.accept();,实际上为serverSockChannel.accept()

  • 尝试设置socket/socketChannel!endpoint.setSocketOptions(socket)
  1. 封装socketNioSocketWrapper
  2. 注册这个事件poller.register(socketWrapper);
  • poller.register(socketWrapper);
  1. 设置为读模式socketWrapper.interestOps(SelectionKey.OP_READ);
  2. 创建事件,PollerEvent event = null;,并添加到同步事件队列,addEvent(event);,队列为SynchronizedQueue<PollerEvent> events

Poller

  • 一直轮训是否时间队列中有事件,交给word线程处理
  • while (true)循环,接收事件hasEvents = events();

当事件队列有事件时,就为true,(pe = events.poll()) != null

  • SelectionKey选择器轮询时间,并进行处理
while (iterator != null && iterator.hasNext()) {
    SelectionKey sk = iterator.next();
    iterator.remove();
    NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
    // Attachment may be null if another thread has called
    // cancelledKey()
    // 处理事件
    if (socketWrapper != null) {
        processKey(sk, socketWrapper);
    }
}
  • 根据不同的事件,进行不同处理,processSocket(socketWrapper, SocketEvent.OPEN_READ, true) image.png image.png
  • 包装socketWrappereventSocketProcessorBase<S> sc = createSocketProcessor(socketWrapper, event);
  • 获取work线程池,Executor executor = getExecutor();,并让线程池执行executor.execute(sc); image.png

SocketProcessorBase

  • 实现了Runnable,会被线程池执行
  • 通过ConnectionHandler处理,state = getHandler().process(socketWrapper, event); image.png
  • 获取处理器,没有则创建processor = getProtocol().createProcessor();,并注册 image.png
  • 进行处理state = processor.process(wrapper, status);
  • 根据当前时间,进行不同的处理 image.png
  • 开始处理state = service(socketWrapper);
  1. 解析请求首行
  2. 解析协议prepareRequestProtocol();
  • CoyoteAdapter处理请求头和请求体getAdapter().service(request, response); image.png

CoyoteAdapter.service真正的执行流程

  • 创建HttpServlet请求和响应,并将coyote的设置进去
Request request = (Request) req.getNote(ADAPTER_NOTES);
Response response = (Response) res.getNote(ADAPTER_NOTES);

image.png

  • 后置处理,特定配置postParseSuccess = postParseRequest(req, request, res, response);
  • 开始责任链执行过程connector.getService().getContainer().getPipeline().getFirst().invoke( request, response);
  1. 获得service
  2. 获取engine
  3. 获取pipeline
  4. 获取第一个阀门 image.png
  • 接下来是责任链执行流程,valve阀门,类似执行器

Engine.invoke

  • 获取主机信息 Host host = request.getHost();
  • 通过Host的阀门进行执行host.getPipeline().getFirst().invoke(request, response);
  1. AccessLogValve
  2. ErrorReportValve
  3. StandardHostValve image.png

Host.invoke

  • host.getPipeline().getFirst().invoke(request, response);
  1. AccessLogValve
  2. ErrorReportValve
  3. StandardHostValve
  • 执行到StandardHostValve.invoke
  • 其中调用context应用,context.getPipeline().getFirst().invoke(request, response);
  1. NonLoginAuthenticator,处理认证信息
  2. StandardContextValve

Context.invoke

  • context.getPipeline().getFirst().invoke(request, response);
  1. NonLoginAuthenticator,处理认证信息
  2. StandardContextValve
  • 其中StandardContextValve.invoke判断保护资源 image.png
  • wrapper.getPipeline().getFirst().invoke(request, response);

wrapper封装了servlet信息

Wrapper.invoke

image.png

  • 创建servlet对象,servlet = wrapper.allocate();,其中加载instance = loadServlet();,定义了servlet生命周期
  1. 创建servlet = (Servlet) instanceManager.newInstance(servletClass);
  2. 初始化initServlet(servlet);,调用servlet.init(facade);
  3. servletClassContext.start()时被注册
  • 为当前请求创建Filter链,ApplicationFilterChain filterChain = ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
  • 执行Filter链,filterChain.doFilter(request.getRequest(), response.getResponse());,执行doFilter
  • Filter链执行完成后,最终执行servlet.service(request, response);
  1. 对应到SpringMVC
  2. 执行HttpServletservice
  3. 执行FrameworkServletdo*方法
  4. 执行DispatcherServletdoService,调用doDispatch(request, response)