Tomcat性能優化

外置tomcat優化html

Tomcat在各位JavaWeb從業者經常就是默認的開發環境,可是Tomcat的默認配置做爲生產環境,尤爲是內存和線程的配置,默認都很低,容易成爲性能瓶頸.web

幸虧Tomcat還有不少的提高空間.下文介紹一下Tomcat優化,能夠分爲內存,線程,IO.spring

一:Tomcat內存優化,啓動時告訴JVM我要一塊大內存(調優內存是最直接的方式)apache

Windows 下的catalina.bat編程

Linux 下的catalina.sh 如:tomcat

JAVA_OPTS='-Xms256m -Xmx512m'
-Xms<size> JVM初始化堆的大小

-Xmx<size> JVM堆的最大值 實際參數大小根據服務器配置或者項目具體設置.

二:Tomcat 線程優化 在server.xml中 如:springboot

<Connector port="80" protocol="HTTP/1.1" maxThreads="600" minSpareThreads="100" maxSpareThreads="500" acceptCount="700"
connectionTimeout="20000"  />

maxThreads="X" 表示最多同時處理X個鏈接 minSpareThreads="X" 初始化X個鏈接 maxSpareThreads="X" 表示若是最多能夠有X個線程,一旦超過X個,則會關閉不在須要的線程 acceptCount="X" 當同時鏈接的人數達到maxThreads時,還能夠排隊,隊列大小爲X.超過X就不處理

三:Tomcat IO優化服務器

1:同步阻塞IO(JAVA BIO) 同步並阻塞,服務器實現模式爲一個鏈接一個線程(one connection one thread 想一想都以爲恐怖,線程但是很是寶貴的資源),固然能夠經過線程池機制改善.架構

2:JAVA NIO:又分爲同步非阻塞IO,異步阻塞IO 與BIO最大的區別one request one thread.能夠複用同一個線程處理多個connection(多路複用).併發

3:,異步非阻塞IO(Java NIO2又叫AIO) 主要與NIO的區別主要是操做系統的底層區別.能夠作個比喻:比做快遞,NIO就是網購後要本身到官網查下快遞是否已經到了(多是屢次),而後本身去取快遞;AIO就是快遞員送貨上門了(不用關注快遞進度)。

BIO方式適用於鏈接數目比較小且固定的架構,這種方式對服務器資源要求比較高,併發侷限於應用中,JDK1.4之前的惟一選擇,但程序直觀簡單易理解.

NIO方式適用於鏈接數目多且鏈接比較短(輕操做)的架構,好比聊天服務器,併發侷限於應用中,編程比較複雜,JDK1.4開始支持.

AIO方式使用於鏈接數目多且鏈接比較長(重操做)的架構,好比相冊服務器,充分調用OS參與併發操做,編程比較複雜,JDK7開始支持.

 

在server.xml中

<Connector port="80" protocol="org.apache.coyote.http11.Http11NioProtocol" 
    connectionTimeout="20000" 
    URIEncoding="UTF-8" 
    useBodyEncodingForURI="true" 
    enableLookups="false" 
    redirectPort="8443" />

實現對Tomcat的IO切換.
四:大殺器APR


APR是從操做系統級別來解決異步的IO問題,大幅度的提升性能. (http://apr.apache.org/).

APR(Apache Portable Runtime)是一個高可移植庫,它是Apache HTTP Server 2.x的核心.能更好地和其它本地web技術集成,整體上讓Java更有效率做爲一個高性能web服務器平臺而不是簡單做爲後臺容器.

在產品環境中,特別是直接使用Tomcat作WEB服務器的時候,應該使用Tomcat Native來提升其性能.若是不配APR,基本上300個線程狠快就會用滿,之後的請求就只好等待.可是配上APR以後,併發的線程數量明顯降低,從原來的300可能會立刻降低到只有幾十,新的請求會毫無阻塞的進來.

在局域網環境測,就算是400個併發,也是一瞬間就處理/傳輸完畢,可是在真實的Internet環境下,頁面處理時間只佔0.1%都不到,絕大部分時間都用來頁面傳輸.若是不用APR,一個線程同一時間只能處理一個用戶,勢必會形成阻塞。因此生產環境下用apr是很是必要的.

安裝Apache Tomcat Native Library,直接啓動就支持apr(http://tomcat.apache.org/native-doc/)它自己是基於APR的. 具體安裝方法能夠參考其餘博客和文章. 排除代碼問題Tomcat優化到這個層次,能夠應對大部分性能需求.

最後一句話"再牛B的服務器,也頂不住一個傻B的代碼".優化的前提條件是良好的代碼質量和設計.

Spring Boot 定製與優化內置的Tomcat容器

 

 

內置的容器有三個分別是Undertow、Jetty、Tomcat,Spring Boot 對這三個容器分別進行了實現,它們上層接口都是EmbeddedServletContainerFactory,該接口也是本文的主要核心.

對於內置容器的定製與優化主要有兩種方式,第一種方式是經過配置文件來配置,另一種是經過碼代碼的方式.接下來主要對上述兩種方式進行實現。

經過配置文件來定製與優化Tomcat

 配置的核心內容參考org.springframework.boot.autoconfigure.web.ServerProperties這個服務屬性類

  server:
      tomcat:
        max-threads: 100
        min-spare-threads: 20
      connection-timeout: 5000
      ssl:
        key-store: classpath:.keystore
        key-store-type: JKS
        key-password: qq123456
        key-alias: tomcat
      port: 8443

經過碼代碼方式實現對內置容器的配置與優化

 

 有兩種方式顯示用代碼進行對內置容器進行優化與定製,第一種實現內置Servlet容器定製器(org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer),並將類交給Spring容器管理,另一種是在Spring容器中配置EmbeddedServletContainerFactory接口實現類,這裏咱們主要針對內置Tomcat,即TomcatEmbeddedServletContainerFactory類

第一種方式實現EmbeddedServletContainerCustomizer接口,並交給Spring容器管理

@Component
public class MyEmbeddedServletContainerCustomizer implements EmbeddedServletContainerCustomizer {
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        //org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory 
        //說明默認是的Tomcat容器
        System.out.println(container.getClass());
        TomcatEmbeddedServletContainerFactory factory = (TomcatEmbeddedServletContainerFactory) container;
        //設置端口
        factory.setPort(8088);
        //設置Tomcat的根目錄
        factory.setBaseDirectory(new File("d:/tmp/tomcat"));
        //設置訪問日誌存放目錄
        factory.addContextValves(getLogAccessLogValue());
        //設置Tomcat線程數和鏈接數
        factory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());
        //初始化servletContext對象
        factory.addInitializers((servletContext) -> {
            System.out.println(" = = = = 獲取服務器信息 = = " + servletContext.getServerInfo());
        });

    }
    private AccessLogValve getLogAccessLogValue() {
        AccessLogValve accessLogValve = new AccessLogValve();
        accessLogValve.setDirectory("d:/tmp/tomcat/logs");
        accessLogValve.setEnabled(true);
        accessLogValve.setPattern(Constants.AccessLog.COMMON_PATTERN);
        accessLogValve.setPrefix("springboot-access-log");
        accessLogValve.setSuffix(".txt");
        return accessLogValve;
    }
}

/**
 * 定製tomcat的鏈接數與線程數
 */
class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
    @Override
    public void customize(Connector connector) {
        //鏈接協議 HTTP/1.1
        System.out.println(connector.getProtocol());
        //鏈接協議處理器 org.apache.coyote.http11.Http11NioProtocol
        System.out.println(connector.getProtocolHandler().getClass());
        //Http11NioProtocol
        Http11NioProtocol protocolHandler = (Http11NioProtocol) connector.getProtocolHandler();
        // 設置最大鏈接數
        protocolHandler.setMaxConnections(2000);
        // 設置最大線程數
        protocolHandler.setMaxThreads(500);
    }
}

在Spring容器中配置EmbeddedServletContainerFactory實現類

@SpringBootConfiguration
public class WebServerConfiguration {
    @Bean
    public EmbeddedServletContainerFactory embeddedServletContainerFactory() {
        TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory();
        //設置端口
        factory.setPort(8089);
        //設置404錯誤界面
        factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/404.html"));
        //設置在容器初始化的時候觸發
        factory.addInitializers((servletContext) -> {
            System.out.println(" = = = = 獲取服務器信息 = = " + servletContext.getServerInfo());
        });
        //設置最大鏈接數和最大線程數
        factory.addConnectorCustomizers((connector) -> {
            Http11NioProtocol protocolHandler = (Http11NioProtocol) connector.getProtocolHandler();
            protocolHandler.setMaxConnections(2000);
            protocolHandler.setMaxThreads(500);
        });
        //設置訪問日誌記錄文件的目錄
        factory.addContextValves(getLogAccessLogValue());
        return factory;
    }

    private AccessLogValve getLogAccessLogValue() {
        AccessLogValve accessLogValve = new AccessLogValve();
        accessLogValve.setDirectory("d:/tmp/logs");
        accessLogValve.setEnabled(true);
        accessLogValve.setPattern(Constants.AccessLog.COMMON_PATTERN);
        accessLogValve.setPrefix("SpringBoot-Access-Log");
        accessLogValve.setSuffix(".txt");
        return accessLogValve;
    }
}
相關文章
相關標籤/搜索