Tomcat進(jìn)階學(xué)習(xí)下篇

4、Tomcat源碼分析

4.1源碼構(gòu)建

下載

下載地址 https://tomcat.apache.org/download-80.cgi 下載src源碼然后解壓

配置文件

將配置文件轉(zhuǎn)移新的文件夾下避免沖突:

在 apache-tomcat-8.5.50-src ?錄中創(chuàng)建 source ?件夾

將 conf、webapps ?錄移動(dòng)到剛剛創(chuàng)建的 source ?件夾中

配置JSP初始化器

ContextConfig類中的configureStart?法中增加??代碼將 Jsp 引擎初始化。

webConfig();

//初始化jsp引擎

context.addServletContainerInitializer(new JasperInitializer(), null);

啟動(dòng)參數(shù)

-Dcatalina.home=D:/Study/tomcat-source/source

-Dcatalina.base=D:/Study/tomcat-source/source

-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager

-Djava.util.logging.config.file=D:/Study/tomcat-source/source/conf/logging.properties

D:/Study/tomcat-source/source這個(gè)就是剛剛咱們創(chuàng)建的文件夾 到時(shí)候更換成自己的文件夾

添加PomXml,將tomcat交給maven管理

POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>apache-tomcat-8.5.50-src</artifactId>
    <name>Tomcat8.5</name>
    <version>8.5</version>
    <build>
        <!--指定源?錄-->
        <finalName>Tomcat8.5</finalName>
        <sourceDirectory>java</sourceDirectory>
        <resources>
            <resource>
                <directory>java</directory>
            </resource>
        </resources>
        <plugins>
            <!--引?編譯插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>11</source>
                    <target>11</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <!--tomcat 依賴的基礎(chǔ)包-->
    <dependencies>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.7.0</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.soap</groupId>
            <artifactId>javax.xml.soap-api</artifactId>
            <version>1.4.0</version>
        </dependency>
    </dependencies>
</project>

注意這個(gè)Pom需要自己創(chuàng)建。

運(yùn)行

直接運(yùn)行Bootstrap中的main函數(shù)即可。

Tomcat進(jìn)階學(xué)習(xí)下篇

4.2Tomcat啟動(dòng)流程源碼分析

4.2.1Tomcat啟動(dòng)時(shí)序圖

通過(guò)時(shí)序圖觀察,通過(guò)Bootstrap中的main方法啟動(dòng),先進(jìn)行初始化,在通過(guò)load方法逐級(jí)的往下加載,加載完成之后通過(guò)start逐級(jí)的往下啟動(dòng)。

所有的組件接口都繼承了Lifecycle頂級(jí)接口,Lifecycle頂級(jí)接口指定了啟動(dòng)、銷毀、停止等規(guī)范,相當(dāng)于Spring中的BeanFactory;Tomcat通過(guò)Lifecycle統(tǒng)一規(guī)定了各個(gè)組件的生命周期。

Tomcat進(jìn)階學(xué)習(xí)下篇

4.2.2啟動(dòng)源碼追蹤

1、加載流程

對(duì)應(yīng)時(shí)序圖中1~13步

Bootstrap

public static void main(String args[]) {
    //添加了一把鎖 
    synchronized (daemonLock) {
        if (daemon == null) {
            // 創(chuàng)建一個(gè)BootStreap實(shí)例
            Bootstrap bootstrap = new Bootstrap();
            try {
               //調(diào)用bootrap的實(shí)例化方法
                bootstrap.init();
            } catch (Throwable t) {
 ...
                return;
            }
 daemon = bootstrap;
        } else {
            // When running as a service the call to stop will be on a new
            // thread so make sure the correct class loader is used to
            // prevent a range of class not found exceptions.
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
    }

    try {
        String command = "start";
       ... 
       if (command.equals("start")) {
 daemon.setAwait(true);
            //加載流程
 daemon.load(args);
           //啟動(dòng)流程
 daemon.start();
            if (null == daemon.getServer()) {
                System.exit(1);
            }
        }
     ...
    } catch (Throwable t) {
      ...
    }
}

BootStrap#init方法

public void init() throws Exception {
    //初始化classloader  沒(méi)有使用系統(tǒng)的使用自定義的classLoader后邊在講解
    initClassLoaders();
    //將自定義的classloader綁定到線程上在使用的時(shí)候可以直接拿來(lái)使用 
    Thread.currentThread().setContextClassLoader(catalinaLoader);

    //將自定義的classLoader設(shè)置到安全的classLoader上
    SecurityClassLoad.securityClassLoad(catalinaLoader);
    //通過(guò)自定義的classLoader加載Catalina Class 
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    //進(jìn)行實(shí)例化
    Object startupInstance = startupClass.getConstructor().newInstance();

   //獲取到父類的類加載器方法-- 將自定義的類加載器設(shè)置到父類中
    String methodName = "setParentClassLoader";
    Class<?> paramTypes[] = new Class[1];
    paramTypes[0] = Class.forName("java.lang.ClassLoader");
    Object paramValues[] = new Object[1];
    paramValues[0] = sharedLoader;
    Method method =
        startupInstance.getClass().getMethod(methodName, paramTypes);
    method.invoke(startupInstance, paramValues);
     //將Catalina實(shí)例賦值到 catalinaDaemon屬性上
    catalinaDaemon = startupInstance;
}

BootStrap#load方法

private void load(String[] arguments) throws Exception {
    // Call the load() method
    String methodName = "load";
    Object param[];
    Class<?> paramTypes[];
    if (arguments==null || arguments.length==0) {
        paramTypes = null;
        param = null;
    } else {
        paramTypes = new Class[1];
        paramTypes[0] = arguments.getClass();
        param = new Object[1];
        param[0] = arguments;
    }
    //【通過(guò)反射的方法調(diào)用Catalina的load方法】
    Method method =
        catalinaDaemon.getClass().getMethod(methodName, paramTypes);
      method.invoke(catalinaDaemon, param);
}

Catalina#load方法

public void load() {
   //判斷是否加載過(guò) 默認(rèn)為false
    if (loaded) {
        return;
    }
    loaded = true;

    long t1 = System.nanoTime();
    //初始化文件夾
    initDirs();

    // 初始名字Before digester - it may be needed
    initNaming();

    //保存Tomcat所需要的組件和監(jiān)聽(tīng)器的ClassName,比如StandardServer、LifecycleListener... 
    Digester digester = createStartDigester();

    InputSource inputSource = null;
    InputStream inputStream = null;
    File file = null;
    try {
        try {
            //讀取conf/server.xml配置文件
            file = configFile();
            inputStream = new FileInputStream(file);
            inputSource = new InputSource(file.toURI().toURL().toString());
        } catch (Exception e) {}
       //創(chuàng)建輸入流
        if (inputStream == null) {
            try {
                inputStream = getClass().getClassLoader()
                    .getResourceAsStream(getConfigFile());
                inputSource = new InputSource
                    (getClass().getClassLoader()
                     .getResource(getConfigFile()).toString());
            } catch (Exception e) {}
        }

        // 如果inputstream還不存在繼續(xù)創(chuàng)建
        if (inputStream == null) {
            try {
                inputStream = getClass().getClassLoader()
                        .getResourceAsStream("server-embed.xml");
                inputSource = new InputSource
                (getClass().getClassLoader()
                        .getResource("server-embed.xml").toString());
            } catch (Exception e) {}
        }

        //如果還存在就返回吧 【省略了一些日志打印】
        if (inputStream == null || inputSource == null) { return; }

        try {
            inputSource.setByteStream(inputStream);
    //將當(dāng)前對(duì)象推入到digester
            digester.push(this);
            //解析server.xml配置文件
            digester.parse(inputSource);
        } catch (Exception e) {return; }
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {}
        }
    }

   //給server設(shè)置一些屬性
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    // Stream redirection 給System設(shè)置Out和ErrSystemLogHandler、SystemLogHandler 
    initStreams();

    // Start the new server
    try {

        getServer().init();
    } catch (LifecycleException e) {
        if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
            throw new java.lang.Error(e);
        } else {
 log.error("Catalina.start", e);
        }
    }
}

LifecycleBase的init方法和initInternal方法

public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.initFail", toString());
    }
}

protected abstract void initInternal() throws LifecycleException;

使用典型的模板設(shè)計(jì)模式!

調(diào)用StandardServer中的initInternal方法

protected void initInternal() throws LifecycleException {
   //調(diào)用父類LifecycleBase的initInternal初始化屬性 
    super.initInternal();

    // Register global String cache
    // 初始化緩存組件    
    onameStringCache = register(new StringCache(), "type=StringCache");

    // 注冊(cè) MBeanFactory
    MBeanFactory factory = new MBeanFactory();
    factory.setContainer(this);
    onameMBeanFactory = register(factory, "type=MBeanFactory");

    //初始化全局naming 
    globalNamingResources.init();

    // Populate the extension validator with JARs from common and shared
    //填充一些拓展jar包校驗(yàn) 
    // class loaders
    if (getCatalina() != null) {
        ClassLoader cl = getCatalina().getParentClassLoader();
        // Walk the class loader hierarchy. Stop at the system class loader.
        // This will add the shared (if present) and common class loaders
        while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
            if (cl instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) cl).getURLs();
                for (URL url : urls) {
                    if (url.getProtocol().equals("file")) {
                        try {
                            File f = new File (url.toURI());
                            if (f.isFile() &&
                                    f.getName().endsWith(".jar")) {
                                ExtensionValidator.addSystemResource(f);
                            }
                        } catch (URISyntaxException e) {
                            // Ignore
                        } catch (IOException e) {
                            // Ignore
                        }
                    }
                }
            }
            cl = cl.getParent();
        }
    }
    // 初始化service
    for (int i = 0; i < services.length; i++) {
        services[i].init();
    }
}

services[i].init();實(shí)際上還是走的LifecycleBase的init方法通過(guò)模板調(diào)用StandardService中的initIternal方法。

StandardService#initInternal方法

protected void initInternal() throws LifecycleException {

    super.initInternal();
    //初始化engine  里邊會(huì)初始化Engine、Host、Context
    if (engine != null) {
        engine.init();
    }

    // Initialize any Executors 線程池
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }

    // Initialize mapperlistener
    mapperListener.init();

    // Initialize our defined Connectors
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            try {
               //初始化connector
                connector.init();
            } catch (Exception e) {
               if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                    throw new LifecycleException(message);
            }
        }
    }
}

初始化Engine、Executor就追蹤了直接觀察初始化Connector方法。

Connector#initInernal方法

protected void initInternal() throws LifecycleException {

    super.initInternal();

    // 創(chuàng)建一個(gè)CoyoteAdapter的適配器
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);

    // 確保 parseBodyMethodsSet has a default
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }
    //判斷AprLifecycleListener不可用拋出異常
    if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
                getProtocolHandlerClassName()));
    }

    if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
            protocolHandler instanceof AbstractHttp11JsseProtocol) {
        AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                (AbstractHttp11JsseProtocol<?>) protocolHandler;
        if (jsseProtocolHandler.isSSLEnabled() &&
                jsseProtocolHandler.getSslImplementationName() == null) {
            // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
            jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
        }
    }

    try {
       //重點(diǎn)來(lái)看protocolHandler初始化方法
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
 sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}

調(diào)用AbstracProtocol中的初始化方法

public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
    }

    if (oname == null) {
        // Component not pre-registered so register it
        oname = createObjectName();
        if (oname != null) {
            Registry.getRegistry(null, null).registerComponent(this, oname, null);
        }
    }

    if (this.domain != null) {
        rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
        Registry.getRegistry(null, null).registerComponent(
                getHandler().getGlobal(), rgOname, null);
    }

    String endpointName = getName();
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);
    //初始化endpoint的init方法
    endpoint.init();
}

調(diào)用父類的AbstractEndpoing的init方法

public void init() throws Exception {
    if (bindOnInit) {
        //綁定端口
        bind();
        bindState = BindState.BOUND_ON_INIT;
    }
  //注冊(cè)一些socketProperties
    if (this.domain != null) {
        // Register endpoint (as ThreadPool - historical name)
        oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);

        ObjectName socketPropertiesOname = new ObjectName(domain +
                ":type=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
        socketProperties.setObjectName(socketPropertiesOname);
        Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

        for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
            registerJmx(sslHostConfig);
        }
    }
}

默認(rèn)調(diào)用NIOEndpoint的bind方法

public void bind() throws Exception {
   //配置Socket的端口號(hào)和一些屬性
    if (!getUseInheritedChannel()) {
       //創(chuàng)建一個(gè)serverSocket
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        InetSocketAddress addr = (getAddress()!=null?
    new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        //綁定端口號(hào) 
        serverSock.socket().bind(addr,getAcceptCount());
    } else {
        // Retrieve the channel provided by the OS
        Channel ic = System.inheritedChannel();
        if (ic instanceof ServerSocketChannel) {
            serverSock = (ServerSocketChannel) ic;
        }
        if (serverSock == null) {
            throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));
        }
    }
    serverSock.configureBlocking(true); //mimic APR behavior

    // Initialize thread count defaults for acceptor, poller
    if (acceptorThreadCount == 0) {
        // FIXME: Doesn't seem to work that well with multiple accept threads
 acceptorThreadCount = 1;
    }
    if (pollerThreadCount <= 0) {
        //minimum one poller thread
        pollerThreadCount = 1;
    }
    setStopLatch(new CountDownLatch(pollerThreadCount));

    // Initialize SSL if needed
    initialiseSsl();

    selectorPool.open();
}

總結(jié) :加載流程將所有的各個(gè)組件都進(jìn)行創(chuàng)建和初始化配置。

2、啟動(dòng)流程

BootStrap.Start方法

public void start() throws Exception {
   //如果Catalina實(shí)例為null在從新創(chuàng)建
    if (catalinaDaemon == null) {
        init();
    }
    //通過(guò)反射的方法調(diào)用Catalina的start方法
    Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
    method.invoke(catalinaDaemon, (Object [])null);
}

Catalina#Start方法

public void start() {
    //校驗(yàn)server為null從新賦值
    if (getServer() == null) {
        load();
    }
    if (getServer() == null) {
         return;
    }
    //計(jì)時(shí)
    long t1 = System.nanoTime();

    // Start the new server
    try {
        //調(diào)用StandardServer的start方法 
        getServer().start();
    } catch (LifecycleException e) {
 log.fatal(sm.getString("catalina.serverStartFail"), e);
        try {
            //出現(xiàn)異常優(yōu)雅關(guān)閉
            getServer().destroy();
        } catch (LifecycleException e1) {
 log.debug("destroy() failed for failed Server ", e1);
        }
        return;
    }
    //打印計(jì)時(shí)
    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
 log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
    }

    // Register shutdown hook  注冊(cè)一個(gè)關(guān)機(jī)鉤子  進(jìn)行優(yōu)雅關(guān)機(jī)
    if (useShutdownHook) {
        if (shutdownHook == null) {
            shutdownHook = new CatalinaShutdownHook();
        }
        Runtime.getRuntime().addShutdownHook(shutdownHook);

        // If JULI is being used, disable JULI's shutdown hook since
        // shutdown hooks run in parallel and log messages may be lost
        // if JULI's hook completes before the CatalinaShutdownHook()
        LogManager logManager = LogManager.getLogManager();
        if (logManager instanceof ClassLoaderLogManager) {
            ((ClassLoaderLogManager) logManager).setUseShutdownHook(
                    false);
        }
    }
    //等候  默認(rèn)為false 當(dāng)調(diào)用關(guān)機(jī)的時(shí)候會(huì)執(zhí)行這些方法進(jìn)行優(yōu)雅關(guān)機(jī)
    if (await) {
        //調(diào)用的是StanardServer#Await 將socket進(jìn)行監(jiān)聽(tīng)
        await();
        //關(guān)閉所有進(jìn)程并且銷毀容器
        stop();
    }
}

StandardServer#startInternal方法

protected void startInternal() throws LifecycleException {
    //出發(fā)監(jiān)聽(tīng)的事件
    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);
    //啟動(dòng)一些namingResource
    globalNamingResources.start();

    // Start our defined Services
    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
    //調(diào)用StandardService的startInternal方法
            services[i].start();
        }
    }
}

StandardService#startInternal

protected void startInternal() throws LifecycleException {
    setState(LifecycleState.STARTING);
    // Start our defined Container first
    if (engine != null) {
        synchronized (engine) {
    //engineStart方法  會(huì)調(diào)用StandardEngine#startInternal方法 在方法內(nèi)部調(diào)用父類的ContainerBase#startInternal
            ①engine.start();
        }
    }

    synchronized (executors) {
        for (Executor executor: executors) {
    //啟動(dòng)線程池這一步在初始化的時(shí)候已經(jīng)啟動(dòng)了在這里不會(huì)被啟動(dòng)了
            executor.start();
        }
    }

    mapperListener.start();

    // Start our defined Connectors second
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            try {
                // If it has already failed, don't try and start it
                if (connector.getState() != LifecycleState.FAILED) {
           //啟動(dòng)連接器
                    ②connector.start();
                }
            } catch (Exception e) {}
        }
    }
}

①engine.start()

會(huì)調(diào)StandardEngine的startInernal里邊會(huì)調(diào)用父類ContainerBase#startInternal的方法

父類ContainerBase#startInternal的方法

protected synchronized void startInternal() throws LifecycleException {

    // Start our subordinate components, if any
    logger = null;
    getLogger();
   //獲取集群模式下進(jìn)行啟動(dòng)
    Cluster cluster = getClusterInternal();
    if (cluster instanceof Lifecycle) {
        ((Lifecycle) cluster).start();
    }
    //啟動(dòng)Realm 認(rèn)證用戶的作用
    Realm realm = getRealmInternal();
    if (realm instanceof Lifecycle) {
        ((Lifecycle) realm).start();
    }

    // 找到所有的子類也就是StanardHost
    Container children[] = findChildren();
    List<Future<Void>> results = new ArrayList<>();
    for (int i = 0; i < children.length; i++) {
      //通過(guò)線程池異步啟動(dòng)子類Host Context Wrapper
      /**
       *  StartChild里邊找到所有子類也就是Host去啟動(dòng),調(diào)用StandardHost中的startInternal方法啟動(dòng)之后在調(diào)用父類的
       *    startInternal,進(jìn)一步找子類進(jìn)行啟動(dòng)一直找到所有的Servlet被啟動(dòng)
       */
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }

    MultiThrowable multiThrowable = null;

     //對(duì)啟動(dòng)的結(jié)果進(jìn)行判斷
    for (Future<Void> result : results) {
        try {
            result.get();
        } catch (Throwable e) {
 log.error(sm.getString("containerBase.threadedStartFailed"), e);
            if (multiThrowable == null) {
                multiThrowable = new MultiThrowable();
            }
            multiThrowable.add(e);
        }

    }
    if (multiThrowable != null) {
        throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"),
                multiThrowable.getThrowable());
    }

    // Start the Valves in our pipeline (including the basic), if any
    if (pipeline instanceof Lifecycle) {
        ((Lifecycle) pipeline).start();
    }

    //設(shè)置啟動(dòng)狀態(tài)
    setState(LifecycleState.STARTING);

    // Start our thread
    threadStart();
}

②connector.start

調(diào)用的測(cè)試Connector#startInternal方法

protected void startInternal() throws LifecycleException {

    // Validate settings before starting
    if (getPort() < 0) {
        throw new LifecycleException(sm.getString(
                "coyoteConnector.invalidPort", Integer.valueOf(getPort())));
    }

    setState(LifecycleState.STARTING);

    try {
        protocolHandler.start();
    } catch (Exception e) {
        throw new LifecycleException(
 sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
    }
}

調(diào)用AbstractProtocol的start方法

public void start() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.start", getName()));
    }
    //啟動(dòng)endpoint
    endpoint.start();

    // Start timeout thread
    asyncTimeout = new AsyncTimeout();
    Thread timeoutThread = new Thread(asyncTimeout, getNameInternal() + "-AsyncTimeout");
    int priority = endpoint.getThreadPriority();
    if (priority < Thread.MIN_PRIORITY || priority > Thread.MAX_PRIORITY) {
        priority = Thread.NORM_PRIORITY;
    }
    timeoutThread.setPriority(priority);
    timeoutThread.setDaemon(true);
    timeoutThread.start();
}

endpoint.start默認(rèn)調(diào)用的是NioEndpoint#startInternal方法

public void startInternal() throws Exception {

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

        processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                socketProperties.getProcessorCache());
        eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                        socketProperties.getEventCache());
        nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                socketProperties.getBufferPool());

        // Create worker collection
        if ( getExecutor() == null ) {
            createExecutor();
        }

        initializeConnectionLatch();

        // Start poller threads  PollerThread是個(gè)實(shí)現(xiàn)了Runnable的線程類用來(lái)處理過(guò)來(lái)的用戶請(qǐng)求
        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();
        }
        //開(kāi)啟NIO模式線程監(jiān)聽(tīng)
        startAcceptorThreads();
    }
}

總結(jié) :?jiǎn)?dòng)流程和加載流程模式差不多,只不過(guò)是設(shè)置了必要的啟動(dòng)的一些參數(shù)。

4.3Tomcat請(qǐng)求流程分析

Tomcat請(qǐng)求處理流程。 瀏覽器請(qǐng)求

Tomcat進(jìn)階學(xué)習(xí)下篇

Tomcat中采用Mapper組件來(lái)映射Engine和Host之間的關(guān)系。

4.3.1Mapper組件

在Mapper類中有四個(gè)內(nèi)部類,

Tomcat進(jìn)階學(xué)習(xí)下篇

MapElement作為基類其中只包含name、Object屬性供其他三個(gè)類使用。整個(gè)Mapper組件就是起了一個(gè)映射的作用,能夠通過(guò)Engine層層往下查找。

4.3.2請(qǐng)求處理流程

Tomcat進(jìn)階學(xué)習(xí)下篇

4.3.3準(zhǔn)備

創(chuàng)建一個(gè)helloworldWeb項(xiàng)目pom中引入servlet并編寫(xiě)Hello然后將編譯好的項(xiàng)目復(fù)制到源碼中。進(jìn)行啟動(dòng)并訪問(wèn)。

public class HelloServlet extends HttpServlet {
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
    throws ServletException, IOException {        
    resp.getWriter().write("<p1>hello web!</p1>");
    }
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) 
    throws ServletException, IOException {
        resp.getWriter().write("<p1>hello web!</p1>");
    }
}

放入到源碼工程中。

Tomcat進(jìn)階學(xué)習(xí)下篇

啟動(dòng)源碼的tomcat訪問(wèn)是否能夠看到自己的helloWorld程序

4.3.4請(qǐng)求流程源碼分析

1、入口

在NioEndpoint的startInternal方法中,調(diào)用父類startAcceptorThreads開(kāi)啟了線程監(jiān)聽(tīng)。

父類AbstractEndpoint#startAcceptorThreads 方法

protected final void startAcceptorThreads() {
    int count = getAcceptorThreadCount();
    acceptors = new Acceptor[count];

    for (int i = 0; i < count; i++) {
        //創(chuàng)建一個(gè)socket監(jiān)聽(tīng)器
        acceptors[i] = createAcceptor();
        String threadName = getName() + "-Acceptor-" + i;
        acceptors[i].setThreadName(threadName);
        Thread t = new Thread(acceptors[i], threadName);
        t.setPriority(getAcceptorThreadPriority());
        t.setDaemon(getDaemon());
        t.start();
    }
}

NioEndpoint的createAcceptor方法

protected AbstractEndpoint.Acceptor createAcceptor() {
    return new Acceptor();
}

Acceptor繼承圖

Tomcat進(jìn)階學(xué)習(xí)下篇

可以看出來(lái)Acceptor屬于一個(gè)線程類,在父類的startAcceptorThreads方法中最后調(diào)用了start直接會(huì)執(zhí)行Acceptor中的run方法。

Acceptor#run方法

public void run() {
    int errorDelay = 0;
    // 一直循環(huán)一直到我們關(guān)閉容器會(huì)停止。
    while (running) {
       ...
       try {
            //如果到達(dá)了請(qǐng)求連接數(shù)就需要等待。
            countUpOrAwaitConnection();

            SocketChannel socket = null;
            try {
                // socket 接收Socket請(qǐng)求
                socket = serverSock.accept();
            } catch (IOException ioe) {
    ...
    }

            // Successful accept, reset the error delay
            errorDelay = 0;

            // Configure the socket
            if (running && !paused) {
                // setSocketOptions() will hand the socket off to
                // 將獲取到請(qǐng)求塞入到當(dāng)前隊(duì)列中  返回false為失敗 會(huì)進(jìn)行重試。
                if (!setSocketOptions(socket)) {
           //close本次socket
                    closeSocket(socket);
                }
            }
    ...
    }
    state = AcceptorState.ENDED;
}

setSocketOptions方法

protected boolean setSocketOptions(SocketChannel socket) {
    // Process the connection
    try {
        //設(shè)置堵塞為flase 因?yàn)槭峭椒亲枞腘IO
        socket.configureBlocking(false);
        Socket sock = socket.socket();
        socketProperties.setProperties(sock);

       //從SynchronizedStack nioChannels獲取到一個(gè)nioChannel 
        NioChannel channel = nioChannels.pop();

       //channel為null的時(shí)候從新創(chuàng)建一個(gè)channel
        if (channel == null) {
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                    socketProperties.getAppReadBufSize(),
                    socketProperties.getAppWriteBufSize(),
                    socketProperties.getDirectBuffer());
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
            } else {
                channel = new NioChannel(socket, bufhandler);
            }
        } else {
    //將socket放入到ioChannel
            channel.setIOChannel(socket);
            channel.reset();//重置順序
        }
        //從poller吃中獲取到一個(gè)Poller 并將channel進(jìn)行通知 
        getPoller0().register(channel);
    } catch (Throwable t) {
        ...
        return false;
    }
    return true;
}

public void register(final NioChannel socket) {
    socket.setPoller(this);
    NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
    socket.setSocketWrapper(ka);
    ka.setPoller(this);
    ka.setReadTimeout(getSocketProperties().getSoTimeout());
    ka.setWriteTimeout(getSocketProperties().getSoTimeout());
    ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
    ka.setSecure(isSSLEnabled());
    ka.setReadTimeout(getConnectionTimeout());
    ka.setWriteTimeout(getConnectionTimeout());
    PollerEvent r = eventCache.pop();
    ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
    if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
    else r.reset(socket,ka,OP_REGISTER);

    //上邊是包裝一些時(shí)間,這一步添加到事件中 
    addEvent(r);
}
private final SynchronizedQueue<PollerEvent> events =
        new SynchronizedQueue<>();

//添加到事件中
private void addEvent(PollerEvent event) {
    events.offer(event);
    if ( wakeupCounter.incrementAndGet() == 0 ) selector.wakeup();
}

Poller的run方法

一直在循環(huán)去檢查有沒(méi)有事件

public void run() {
    // Loop until destroy() is called
    while (true) {
        try {
            if (!close) {
                hasEvents = events();
        ...
             }
            if (close) {
                ...
    }
        } catch (Throwable x) {
            ...
    continue;
        }
        //判斷是否有事件 events是查看隊(duì)列中是否存在事件
        if ( keyCount == 0 ) hasEvents = (hasEvents | events());

        Iterator<SelectionKey> iterator =
            keyCount > 0 ? selector.selectedKeys().iterator() : null;
        // Walk through the collection of ready keys and dispatch
        // any active event.
        while (iterator != null && iterator.hasNext()) {
            if (attachment == null) {
                iterator.remove();
            } else {
                iterator.remove();
        //真正開(kāi)始執(zhí)行的邏輯
                processKey(sk, attachment);
            }
        } 
    } 
}

2、從Endpoin查找到Servlet

Tomcat進(jìn)階學(xué)習(xí)下篇

從是processKey開(kāi)始分析。因?yàn)榇a比較繁瑣直接采用時(shí)序圖來(lái)分析。

總結(jié):Tomcat在執(zhí)行的過(guò)程中運(yùn)用了比較多的模版設(shè)計(jì)模式,方便拓展,也一層一層的往下去委派給下一層,其中最為核心的就是在Mapper組件11-12步將請(qǐng)求參數(shù)解析成Request對(duì)象,并且找好了對(duì)應(yīng)的Engine、Host、Context、Wrapper。有空可以在多多看看源碼學(xué)習(xí)人家的設(shè)計(jì)思路。

5、Tomcat類加載機(jī)制

5.1Jvm類加載機(jī)制回顧

什么是類加載機(jī)制?通俗的說(shuō)將編譯好的class文件加載到j(luò)vm內(nèi)存中的這個(gè)過(guò)程稱之為類加載過(guò)程。這個(gè)機(jī)制就是類加載機(jī)制。執(zhí)行這個(gè)操作的稱之為類加載器(Class Loader)。

5.1.1Jvm內(nèi)置的類加載器

jvm內(nèi)置的類加載器有:引導(dǎo)類加載器、擴(kuò)展類加載器、系統(tǒng)類加載器。

Tomcat進(jìn)階學(xué)習(xí)下篇

類加載器作用引導(dǎo)啟動(dòng)類加載器BootstrapClassLoaderc++編寫(xiě),加載java核?庫(kù) java.,?如rt.jar中的類,構(gòu)造ExtClassLoader和AppClassLoader擴(kuò)展類加載器 ExtClassLoaderjava編寫(xiě),加載擴(kuò)展庫(kù) JAVA_HOME/lib/ext?錄下的jar中的類,如classpath中的jre ,javax.或者java.ext.dir指定位置中的類系統(tǒng)類加載器SystemClassLoader/AppClassLoader默認(rèn)的類加載器,搜索環(huán)境變量 classpath 中指明的路徑

當(dāng)然用戶也可以自定義類加載器,直接加載指定路徑下的class文件。

1) ?戶??的類加載器,把加載請(qǐng)求傳給?加載器,?加載器再傳給其?加載器,?直到加載器樹(shù)的頂層。

2)最頂層的類加載器?先針對(duì)其特定的位置加載,如果加載不到就轉(zhuǎn)交給?類。

3)如果?直到底層的類加載都沒(méi)有加載到,那么就會(huì)拋出異常 ClassNotFoundException。

5.1.2雙親委派機(jī)制

什么是雙親委派機(jī)制?

說(shuō)白類就是在加載某個(gè)類時(shí),類加載器?先把這個(gè)任務(wù)委托給他的上級(jí)類加載器,遞歸這個(gè)操作,一直到最頂層,如果上級(jí)的類加載器沒(méi)有加載,??才會(huì)去加載這個(gè)類。

雙親委派機(jī)制的作用?

1)防?重復(fù)加載同?個(gè).class。通過(guò)委托去向上?問(wèn)?問(wèn),加載過(guò)了,就不?再加載?遍。保證數(shù)據(jù)安全。

2)保證核?.class文件【一般指的是jdk自定的】不能被篡改。通過(guò)委托?式,不會(huì)去篡改核?.class,即使篡改也不會(huì)去加載,即使加載也不會(huì)是同?個(gè).class對(duì)象了。不同的加載器加載同?個(gè).class也不是同?個(gè).class對(duì)象。這樣保證了class執(zhí)?安全(如果?類加載器先加載,那么我們可以寫(xiě)?些與java.lang包中基礎(chǔ)類同名

的類, 然后再定義?個(gè)?類加載器,這樣整個(gè)應(yīng)?使?的基礎(chǔ)類就都變成我們??定義的類了。)

5.2Tomcat類加載機(jī)制

5.2.1Tomcat為啥自定義實(shí)現(xiàn)類加載

Tomcat自定義實(shí)現(xiàn)了類加載器,并未嚴(yán)格按照雙親委派模式的方式進(jìn)行加載。那么思考下如果Tomcat嚴(yán)格按照雙親委派機(jī)制加載類的話會(huì)有啥問(wèn)題?

比如 Tomcat中引入了兩個(gè)項(xiàng)目只有版本不同比如,hellowolrd-1.0.jar 和 helloworld-2.0.jar那么兩個(gè)項(xiàng)目中都存在com.demo.ClassA,兩個(gè)類的因?yàn)榘姹静煌詢?nèi)容可能不同,如果采取雙親委派機(jī)制使用SystemClassLoader的話加載1.0版本之后在去加載2.0版本的時(shí)候發(fā)現(xiàn)已經(jīng)加載過(guò)了,就不會(huì)進(jìn)行加載了這樣子就會(huì)有問(wèn)題。

5.2.2Tomcat類加載器繼承體系

Tomcat進(jìn)階學(xué)習(xí)下篇

說(shuō)明:

1)其中引導(dǎo)類加載器和擴(kuò)展類加載器作用不變

2)系統(tǒng)類加載器正常情況下加載的是 CLASSPATH 下的類,但是 Tomcat 的啟動(dòng)腳本并未使?該變量,?是通過(guò)系統(tǒng)類加載器加載tomcat啟動(dòng)的類,?如bootstrap.jar,通常在catalina.bat或者catalina.sh中指定。位于bin目錄下。

3)Common 通?類加載器加載Tomcat依賴的jar包以及應(yīng)?通?的?些類,位于lib目錄下,?如servlet-api.jar

4)Catalina ClassLoader ?于加載服務(wù)器內(nèi)部可?類,這些類應(yīng)?程序不能訪問(wèn)。就是編寫(xiě)Tomcat的類。

5)Shared ClassLoader ?于加載應(yīng)?程序共享類,這些類服務(wù)器不會(huì)依賴。

6)Webapp ClassLoader,給每個(gè)應(yīng)?程序都會(huì)配置?個(gè)獨(dú)???的Webapp ClassLoader,他?來(lái)單獨(dú)加載本應(yīng)?程序 /WEB-INF/classes 和 /WEB-INF/lib 下的類。

Tomcat加載部署的項(xiàng)目加載順序

第一步:從 Bootstrap Classloader加載指定的類。

第二步:如果未加載到,則從 /WEB-INF/classes加載

第三步:如果未加載到,則從 /WEB-INF/lib/*.jar 加載

第四步:如果未加載到,則依次從 System、Common、Shared 加載(在這最后?步,遵從雙親委派機(jī)制)

6、Tomcat對(duì)Https的支持

6.1Https簡(jiǎn)介

百度百科給出的https解釋,

Tomcat進(jìn)階學(xué)習(xí)下篇

簡(jiǎn)單來(lái)說(shuō),http屬于超文本傳輸協(xié)議,是明文傳輸?shù)?,傳輸不安全,https在傳輸數(shù)據(jù)的時(shí)候會(huì)對(duì)數(shù)據(jù)進(jìn)行一層加密。采用加密的協(xié)議為SSL(Secure Socket Layer)?,F(xiàn)在也衍生出了TLS(Transport Layer Security)協(xié)議。

Http和Https主要的區(qū)別?

  1. HTTPS協(xié)議使?時(shí)需要到電?商務(wù)認(rèn)證授權(quán)機(jī)構(gòu)(CA)申請(qǐng)SSL證書(shū)
  2. HTTP默認(rèn)使?8080端?,HTTPS默認(rèn)使?(8)443端?
  3. HTTPS則是具有SSL加密的安全性傳輸協(xié)議,對(duì)數(shù)據(jù)的傳輸進(jìn)?加密,效果上相當(dāng)于HTTP的升級(jí)版
  4. HTTP的連接是?狀態(tài)的,不安全的;HTTPS協(xié)議是由SSL+HTTP協(xié)議構(gòu)建的可進(jìn)?加密傳輸、身份認(rèn)證的?絡(luò)協(xié)議,?HTTP協(xié)議安全

6.2Https工作原理

Tomcat進(jìn)階學(xué)習(xí)下篇

簡(jiǎn)單來(lái)說(shuō),第一次握手會(huì)找到適合當(dāng)前瀏覽器的加密算法,并返回一個(gè)公鑰給瀏覽器;第二次握手瀏覽器先校驗(yàn)公鑰證書(shū),然后生成一串隨機(jī)數(shù) 拿著公鑰進(jìn)行加密,第三握手服務(wù)器通過(guò)私鑰進(jìn)行解密,并拿著私鑰在進(jìn)行加密返回給客戶端,客戶端拿著服務(wù)器返回的信息進(jìn)行hash比對(duì),如果統(tǒng)一的話就進(jìn)行數(shù)據(jù)傳輸。

6.3Tomcat配置Https

使用jdk中的keytool生成一個(gè)自定義的證書(shū)(商用的話找準(zhǔn)認(rèn)證機(jī)構(gòu))

打開(kāi)終端或者Command輸入下邊命令。

keytool -genkey -alias helloworld -keyalg RSA -keystore helloworld.keystore

Tomcat進(jìn)階學(xué)習(xí)下篇

在conf/server.xml中配置

<Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
           maxThreads="150" schema="https" secure="true" SSLEnabled="true">
    <SSLHostConfig>
        <Certificate certificateKeystoreFile="D:/helloworld.keystore" <!--自己的證書(shū)的目錄-->
                     certificateKeystorePassword="helloworld" type="RSA"/>
    </SSLHostConfig>
</Connector>

啟動(dòng)Tomcat訪問(wèn)https:localhost:8443即可訪問(wèn)。

7、Tomcat性能優(yōu)化

調(diào)優(yōu)只提供思路,不同配置調(diào)優(yōu)的數(shù)值是不固定的。

了解一些基礎(chǔ)概念:

1)響應(yīng)時(shí)間:執(zhí)?某個(gè)操作的耗時(shí);

2)吞吐量:系統(tǒng)在給定時(shí)間內(nèi)能夠?持的事務(wù)數(shù)量,單位為T(mén)PS(Transactions PerSecond的縮寫(xiě), 也就是事務(wù)數(shù)/秒,?個(gè)事務(wù)是指?個(gè)客戶機(jī)向服務(wù)器發(fā)送請(qǐng)求然后服務(wù)器做出反應(yīng)的過(guò)程。

7.1Jvm性能調(diào)優(yōu)

Java 虛擬機(jī)的運(yùn)?優(yōu)化主要是內(nèi)存分配和垃圾回收策略的優(yōu)化:

1、內(nèi)存直接影響服務(wù)的運(yùn)?效率和吞吐量

2、垃圾回收機(jī)制會(huì)不同程度地導(dǎo)致程序運(yùn)?中斷(垃圾回收策略不同,垃圾回收次數(shù)和回收效率都是不同的)

1、Java 虛擬機(jī)內(nèi)存相關(guān)參數(shù)

參數(shù)參數(shù)作?優(yōu)化建議-server啟動(dòng)Server,以服務(wù)端模式運(yùn)?服務(wù)端模式建議開(kāi)啟-Xms最?堆內(nèi)存建議與-Xmx設(shè)置相同-Xmx最?堆內(nèi)存建議設(shè)置為可?內(nèi)存的80%-XX:MetaspaceSize元空間初始值一般不會(huì)修改-XX:MaxMetaspaceSize元空間最?內(nèi)存默認(rèn)?限-XX:NewRatio年輕代和?年代???值,取值為整數(shù),默認(rèn)為2[2:1]不需要修改-XX:SurvivorRatioEden區(qū)與Survivor區(qū)??的?值,取值為整數(shù),默認(rèn)為8[8:1:1]不需要修改

Tomcat進(jìn)階學(xué)習(xí)下篇

上圖為CMS和G1的JVM內(nèi)存空間分配。

調(diào)整Demo:

JAVA_OPTS="-server -Xms2048m -Xmx2048m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m"

直接配置到bin/catalina.bat或者catalina.sh目錄下即可。

查看:使用jdk自帶的工具查看

jhsdb jmap -heap -pid xxx【jdk9 之后使用jhsdb】jmap -heap <pid>  打印堆的使用情況

2、垃圾回收策略

垃圾回收性能指標(biāo)

1、吞吐量:?作時(shí)間(排除GC時(shí)間)占總時(shí)間的百分?, ?作時(shí)間并不僅是程序運(yùn)?的時(shí)間,還包含內(nèi)存分配時(shí)間。

2、暫停時(shí)間:由垃圾回收導(dǎo)致的應(yīng)?程序停?響應(yīng)次數(shù)/時(shí)間

垃圾回收器

1、串?收集器(Serial Collector):?jiǎn)尉€程執(zhí)?所有的垃圾回收?作, 適?于單核CPU服務(wù)器

?作進(jìn)程-----|(單線程)垃圾回收線程進(jìn)?垃圾收集|---?作進(jìn)程繼續(xù)

2、并?收集器(Parallel Collector):?稱為吞吐量收集器(關(guān)注吞吐量), 以并?的?式執(zhí)?年輕代的垃圾回收, 該?式可以顯著降低垃圾回收的開(kāi)銷(指多條垃圾收集線程并??作,但此時(shí)?戶線程仍然處于等待狀態(tài))。適?于多處理器或多線程硬件上運(yùn)?的數(shù)據(jù)量較?的應(yīng)?。

?作進(jìn)程-----|(多線程)垃圾回收線程進(jìn)?垃圾收集|---?作進(jìn)程繼續(xù)

3、并發(fā)收集器(Concurrent Collector):以并發(fā)的?式執(zhí)??部分垃圾回收?作,以縮短垃圾回收的暫停時(shí)間。適?于那些響應(yīng)時(shí)間優(yōu)先于吞吐量的應(yīng)?, 因?yàn)樵撌占麟m然最?化了暫停時(shí)間(指?戶線程與垃圾收集線程同時(shí)執(zhí)?,但不?定是并?的,可能會(huì)交替進(jìn)?), 但是會(huì)降低應(yīng)?程序的性能。

4、CMS收集器(Concurrent Mark Sweep Collector):并發(fā)標(biāo)記清除收集器, 適?于那些更愿意縮短垃圾回收暫停時(shí)間并且負(fù)擔(dān)的起與垃圾回收共享處理器資源的應(yīng)?。

5、G1收集器(Garbage-First Garbage Collector):適?于?容量?jī)?nèi)存的多核服務(wù)器, 可以在滿?垃圾回收暫停時(shí)間?標(biāo)的同時(shí), 以最?可能性實(shí)現(xiàn)高吞吐量(JDK1.7之后)。

配置啟動(dòng)參數(shù)

參數(shù)描述-XX:+UseSerialGC啟?串?收集器-XX:+UseParallelGC啟?并?垃圾收集器,配置了該選項(xiàng),那么 -XX:+UseParallelOldGC默認(rèn)啟?。-XX:+UseParNewGC年輕代采?并?收集器,如果設(shè)置了 -XX:+UseConcMarkSweepGC選項(xiàng),?動(dòng)啟?。-XX:ParallelGCThreads年輕代及?年代垃圾回收使?的線程數(shù)。默認(rèn)值依賴于JVM使?的CPU個(gè)數(shù)。-XX:+UseConcMarkSweepGC(CMS)對(duì)于?年代,啟?CMS垃圾收集器。 當(dāng)并?收集器?法滿?應(yīng)?的延遲需求是,推薦使?CMS或G1收集器。啟?該選項(xiàng)后, -XX:+UseParNewGC?動(dòng)啟?。-XX:+UseG1GC?G1收集器。 G1是服務(wù)器類型的收集器, ?于多核、?內(nèi)存的機(jī)器。它在保持?吞吐量的情況下,?概率滿?GC暫停時(shí)間的?標(biāo)。

在bin/catalina.sh的腳本中 , 追加如下配置 :

JAVA_OPTS="-XX:+UseConcMarkSweepGC"

可以通過(guò)jconsle工具的配置概況查看todo。

7.2Tomcat本身配置調(diào)優(yōu)

1、調(diào)整tomcat線程池

Tomcat進(jìn)階學(xué)習(xí)下篇

2、調(diào)整tomcat的連接器

調(diào)整tomcat/conf/server.xml 中關(guān)于鏈接器的配置可以提升應(yīng)?服務(wù)器的性能。

參數(shù)說(shuō)明maxConnections最?連接數(shù),當(dāng)?shù)竭_(dá)該值后,服務(wù)器接收但不會(huì)處理更多的請(qǐng)求, 額外的請(qǐng)求將會(huì)阻塞直到連接數(shù)低于maxConnections 。可通過(guò)ulimit -a 查看服務(wù)器限制。對(duì)于CPU要求更?(計(jì)算密集型)時(shí),建議不要配置過(guò)? ; 對(duì)于CPU要求不是特別?時(shí),建議配置在2000左右(受服務(wù)器性能影響)。 當(dāng)然這個(gè)需要服務(wù)器硬件的?持maxThreads最?線程數(shù),需要根據(jù)服務(wù)器的硬件情況,進(jìn)??個(gè)合理的設(shè)置acceptCount最?排隊(duì)等待數(shù),當(dāng)服務(wù)器接收的請(qǐng)求數(shù)量到達(dá)maxConnections ,此時(shí)Tomcat會(huì)將后?的請(qǐng)求,存放在任務(wù)隊(duì)列中進(jìn)?排序, acceptCount指的就是任務(wù)隊(duì)列中排隊(duì)等待的請(qǐng)求數(shù) 。 ?臺(tái)Tomcat的最?的請(qǐng)求處理數(shù)量,是maxConnections+acceptCount

3、禁? AJP 連接器

Tomcat進(jìn)階學(xué)習(xí)下篇

4、調(diào)整IO模型

Tomcat8之前的版本默認(rèn)使?BIO(阻塞式IO),對(duì)于每?個(gè)請(qǐng)求都要?jiǎng)?chuàng)建?個(gè)線程來(lái)處理,不適

合?并發(fā);Tomcat8以后的版本默認(rèn)使?NIO模式(?阻塞式IO)

Tomcat進(jìn)階學(xué)習(xí)下篇

當(dāng)Tomcat并發(fā)性能有較?要求或者出現(xiàn)瓶頸時(shí),我們可以嘗試使?APR模式,APR(Apache Portable

Runtime)是從操作系統(tǒng)級(jí)別解決異步IO問(wèn)題,使?時(shí)需要在操作系統(tǒng)上安裝APR和Native(因?yàn)锳PR

原理是使?使?JNI技術(shù)調(diào)?操作系統(tǒng)底層的IO接?)。

5、動(dòng)靜分離

可以使?Nginx+Tomcat相結(jié)合的部署?案,Nginx負(fù)責(zé)靜態(tài)資源訪問(wèn),Tomcat負(fù)責(zé)Jsp等動(dòng)態(tài)資

源訪問(wèn)處理(因?yàn)門(mén)omcat不擅?處理靜態(tài)資源)。

</article>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容