35-tomcat

java動態(tài)網(wǎng)頁技術(shù)

servlet

本質(zhì)就是一段Java程序

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorld extends HttpServlet {
    private String message;
    public void init() throws ServletException
    {
        message = "Hello World";
    }
    public void doGet(HttpServletRequest request,
                    HttpServletResponse response)
            throws ServletException, IOException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<h1>" + message + "</h1>");
    }
    public void destroy()
    {
    }
}

在Servlet中最大的問題是,HTML輸出和Java代碼混在一起瓶您,如果網(wǎng)頁布局要調(diào)整衙荐,就是個噩夢。

jsp(Java Server Pages)

提供一個HTML滩届,把它變成一個模板,也就是在網(wǎng)頁中預(yù)留以后填充的空被啼,以后就變成了填空了帜消。

<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>jsp例子</title>
</head>
    
<body>
后面的內(nèi)容是服務(wù)器端動態(tài)生成字符串,最后拼接在一起
<%
    out.println("你的 IP 地址 " + request.getRemoteAddr());
%>
</body>
</html>

JSP是基于Servlet實現(xiàn)浓体,JSP將表現(xiàn)和邏輯分離泡挺,這樣頁面開發(fā)人員更好的注重頁面表現(xiàn)力更好服務(wù)客戶。
JSP 先轉(zhuǎn)換為 Servlet的源代碼.java文件(Tomcat中使用Jasper轉(zhuǎn)換)汹碱,然后再編譯成.class文件粘衬,最后就可以在JVM中運行了。

JDK

  • JRE:它是Java Runtime Environment縮寫咳促,指Java運行時環(huán)境滤淳, 包含 JVM + Java核心類庫

  • JDK:它是Java Development Kit履因,即 Java 語言的軟件開發(fā)工具包屋群。

  • JDK也就是常說的J2SE开伏,在1999年,正式發(fā)布了Java第二代平臺冲茸,發(fā)布了三個版本:
    J2SE:標準版屯阀,適用于桌面平臺
    J2EE:企業(yè)版缅帘,適用于企業(yè)級應(yīng)用服務(wù)器開發(fā)
    J2ME:微型版,適用于移動难衰、無線钦无、機頂盒等設(shè)備環(huán)境

    2005年,Java的版本又更名為JavaSE盖袭、JavaEE失暂、JavaME。
    Servlet鳄虱、Jsp都包含在JavaEE規(guī)范中弟塞。
    JDK7、JDK8拙已、JDK11是LTS(Long Term Suppot)

    版本 項目名稱 發(fā)行日期
    JDK 1.1.4 Sparkler(寶石) 1997-09-12
    JDK 1.1.5 Pumpkin(南瓜) 1997-12-13
    JDK 1.1.6 Abigail(阿比蓋爾–女子名) 1998-04-24
    JDK 1.1.7 Brutus(布魯圖–古羅馬政治家和將軍) 1998-09-28
    JDK 1.1.8 Chelsea(切爾西–城市名) 1999-04-08
    J2SE 1.2 Playground(運動場) 1998-12-04
    J2SE 1.2.1 none(無) 1999-03-30
    J2SE 1.2.2 Cricket(蟋蟀) 1999-07-08
    J2SE 1.3 Kestrel(美洲紅隼) 2000-05-08
    J2SE 1.3.1 Ladybird(瓢蟲) 2001-05-17
    J2SE 1.4.0 Merlin(灰背隼) 2002-02-13
    J2SE 1.4.1 grasshopper(蚱蜢) 2002-09-16
    J2SE 1.4.2 Mantis(螳螂) 2003-06-26
    Java SE 5.0 (1.5.0) Tiger(老虎) 2004-09-30
    Java SE 6.0 (1.6.0) Mustang(野馬) 2006-04
    Java SE 7.0 (1.7.0) Dolphin(海豚) 2011-07-28
    Java SE 8.0 (1.8.0) Spider(蜘蛛) 2014-03-18
    Java SE 9 2017-09-21
    Java SE 10 2018-03-14 [3]

    JDK協(xié)議是JRL(JavaResearch License)協(xié)議

OpenJDK

OpenJDK是Sun公司采用GPL v2協(xié)議發(fā)布的JDK開源版本决记,于2009年正式發(fā)布。

https://openjdk.java.net/projects/jdk6/
OpenJDK 7是基于JDK7的beta版開發(fā)倍踪,但為了也將Java SE 6開源系宫,從OpenJDK7的b20構(gòu)建反向分支開
發(fā),從中剝離了不符合Java SE 6規(guī)范的代碼建车,發(fā)布OpenJDK 6笙瑟。所以O(shè)penJDK6和JDK6沒什么關(guān)系。
OpenJDK使用GPL v2可以用于商業(yè)用途癞志。

安裝JDK

在Centos中,可以使用yum安裝openjdk框产。

# yum install java-1.8.0-openjdk
# java -version
openjdk version "1.8.0_212"
OpenJDK Runtime Environment (build 1.8.0_212-b04)
OpenJDK 64-Bit Server VM (build 25.212-b04, mixed mode)

本次使用Oracle官網(wǎng)的JDK 8的rpm安裝

# yum install jdk-8u191-linux-x64.rpm
# java
# java -version
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

安裝目錄為/user/java下

Java全局配置
# ll /usr/java
lrwxrwxrwx 1 root root 16 1月 13 01:22 default -> /usr/java/latest
drwxr-xr-x 8 root root 258 1月 13 01:22 jdk1.8.0_191-amd64
lrwxrwxrwx 1 root root 28 1月 13 01:22 latest -> /usr/java/jdk1.8.0_191-amd64

# vi /etc/profile.d/jdk.sh
export JAVA_HOME=/usr/java/default
export PATH=$JAVA_HOME/bin:$PATH
# . /etc/profile.d/jdk.sh

Tomcat

歷史
  • 起始于SUN的一個Servlet的參考實現(xiàn)項目Java Web Server凄杯,作者是James Duncan Davidson,后將項 目貢獻給了ASF秉宿。和ASF現(xiàn)有的項目合并戒突,并開源成為頂級項目,官網(wǎng)http://tomcat.apache.org/描睦。
  • Tomcat僅僅實現(xiàn)了Java EE規(guī)范中與Servlet膊存、JSP相關(guān)的類庫,是JavaEE不完整實現(xiàn)忱叭。
  • 著名圖書出版商O'Reilly約稿該項目成員隔崎,Davidson希望使用一個公貓作為封面,但是公貓已經(jīng)被另一本書使用韵丑,書出版后封面是一只雪豹爵卒。
  • 1999年發(fā)布初始版本是Tomcat 3.0,實現(xiàn)了Servlet 2.2和JSP1.1規(guī)范撵彻。
  • Tomcat 4.x發(fā)布時钓株,內(nèi)建了Catalina(Servlet容器)和Jasper(JSP engine)等实牡。
  • 商用的有IBM WebSphere、Oracle WebLogic(原屬于BEA公司)轴合、Oracle Oc4j创坞、Glassfish、JBoss 等受葛。
  • 開源實現(xiàn)有Tomcat题涨、Jetty、Resin奔坟。
安裝

可以使用CentOS7 yum源自帶的安裝携栋。yum源中是Tomcat 7.0版本。安裝完通過瀏覽器可以觀察一下首頁咳秉。

# yum install tomcat tomcat-admin-webapps tomcat-webapps
# systemctl start tomcat.service
# ss -tanl
LISTEN 0 100 :::8009
LISTEN 0 100 :::8080

采用Apache官網(wǎng)下載婉支,下載8.x.x

# tar xf apache-tomcat-8.5.42.tar.gz -C /usr/local
# cd /usr/local
# ln -sv apache-tomcat-8.5.42/ tomcat
"tomcat" -> "apache-tomcat-8.5.42/"
# cd tomcat
# cd bin
# ./catalina.sh --help
# ./catalina.sh version
# ./catalina.sh start
# ss -tanlp
# ./catalina.sh stop
# ./startup.sh
# ./shutdown.sh

useradd -r java 建立系統(tǒng)賬號
上例中,啟動身份是root澜建,如果使用普通用戶啟動可以使用

# useradd -r java
# chown -R java.java ./*
# su - java -c '/usr/local/tomcat/bin/catalina.sh start'
# ps -aux | grep tomcat

目錄結(jié)構(gòu)

目錄 說明
bin 服務(wù)啟動向挖、停止等相關(guān)
conf 配置文件
lib 庫目錄
logs 日志目錄
webapps 應(yīng)用程序,應(yīng)用部署目錄
work jsp編譯后的結(jié)果文件

配置文件

文件名 說明
server.xml 主配置文件
web.xml 每個webapp只有“部署”后才能被訪問炕舵,它的部署方式通常由web.xml進 行定義何之,其存放位置為WEB-INF/目錄中;此文件為所有的webapps提供 默認部署相關(guān)的配置
context.xml 每個webapp都可以專用的配置文件咽筋,它通常由專用的配置文件 context.xml來定義溶推,其存放位置為WEB-INF/目錄中;此文件為所有的 webapps提供默認配置
tomcat-users.xml 用戶認證的賬號和密碼文件
catalina.policy 當使用-security選項啟動tomcat時奸攻,用于為tomcat設(shè)置安全策略
catalina.properties Java屬性的定義文件蒜危,用于設(shè)定類加載器路徑,以及一些與JVM調(diào)優(yōu)相關(guān) 參數(shù)
logging.properties 日志系統(tǒng)相關(guān)的配置睹耐。log4j

組件分類

頂級組件

Server辐赞,代表整個Tomcat容器

服務(wù)類組件

Service,組織Engine和Connector硝训,里面只能包含一個Engine

連接器組件

Connector响委,有HTTP、HTTPS窖梁、A JP協(xié)議的連接器

容器類

Engine赘风、Host、Context都是容器類組件纵刘,可以嵌入其它組件贝次,內(nèi)部配置如何運行應(yīng)用程序。

內(nèi)嵌類

可以內(nèi)嵌到其他組件內(nèi)彰导,valve蛔翅、logger敲茄、realm、loader山析、manager等堰燎。以logger舉例,在不同容器組件內(nèi)定義笋轨。

集群類組件

listener秆剪、cluster

小筆記:tomcat安裝

#二進制安裝
tar xf apache-tomcat-8.5.42.tar.gz -C /usr/local
cd /usr/local
ln -sv apache-tomcat-8.5.42/ tomcat
"tomcat" -> "apache-tomcat-8.5.42/"
useradd -r java
chown -R java.java ./*
yum install jdk-8u191-linux-x64.rpm
vi /etc/profile.d/jdk.sh
export JAVA_HOME=/usr/java/default
export PATH=$JAVA_HOME/bin:$PATH
. /etc/profile.d/jdk.sh

#啟動
su - java -c '/usr/local/tomcat/bin/catalina.sh start'
ps -aux | grep tomcat
#./startup.sh

Tomcat內(nèi)部組成

由上述組件就構(gòu)成了Tomcat,如下圖

image.png
名稱 說明
Server Tomcat運行的進程實例
Connector 負責客戶端的HTTP爵政、HTTPS仅讽、AJP等協(xié)議的連接。一個Connector只屬于某一個 Engine
Service 用來組織Engine和Connector的關(guān)系
Engine 響應(yīng)并處理用戶請求钾挟。一個引擎上可以綁定多個Connector
Host 虛擬主機
Context 應(yīng)用的上下文洁灵,配置路徑映射path => directory

AJP(Apache Jserv protocol)是一種基于TCP的二進制通訊協(xié)議。

核心組件

  • Tomcat啟動一個Server進程掺出』涨В可以啟動多個Server,但一般只啟動一個

  • 創(chuàng)建一個Service提供服務(wù)汤锨∷椋可以創(chuàng)建多個Service,但一般也只創(chuàng)建一個
    每個Service中闲礼,是Engine和其連接器Connector的關(guān)聯(lián)配置

  • 可以為這個Server提供多個連接器Connector牍汹,這些Connector使用了不同的協(xié)議,綁定了不同的端口柬泽。其作用就是處理來自客戶端的不同的連接請求或響應(yīng)

  • Service內(nèi)部還定義了Engine柑贞,引擎才是真正的處理請求的入口,其內(nèi)部定義多個虛擬主機Host
    Engine對請求頭做了分析聂抢,將請求發(fā)送給相應(yīng)的虛擬主機
    如果沒有匹配,數(shù)據(jù)就發(fā)往Engine上的defaultHost缺省虛擬主機
    Engine上的缺省虛擬主機可以修改

  • Host定義虛擬主機棠众,虛擬主機有name名稱琳疏,通過名稱匹配

  • Context定義應(yīng)用程序單獨的路徑映射和配置

    <?xml version="1.0" encoding="UTF-8"?>
    <Server port="8005" shutdown="SHUTDOWN">
      <Service name="Catalina">
          <Connector port="8080" protocol="HTTP/1.1"
              connectionTimeout="20000"
              redirectPort="8443" />
          <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
            
          <Engine name="Catalina" defaultHost="localhost">
              <Host name="localhost" appBase="webapps"
                  unpackWARs="true" autoDeploy="true">
              </Host>
          </Engine>
      </Service>
    </Server>
    

    舉例:
    假設(shè)來自客戶的請求為:http://localhost:8080/test/index.jsp

    • 瀏覽器端的請求被發(fā)送到服務(wù)端端口8080,Tomcat進程監(jiān)聽在此端口上闸拿。通過偵聽的HTTP/1.1 Connector獲得此請求空盼。
    • Connector把該請求交給它所在的Service的Engine來處理,并等待Engine的響應(yīng)
    • Engine獲得請求localhost:8080/test/index.jsp新荤,匹配它所有虛擬主機Host
    • Engine匹配到名為localhost的Host揽趾。即使匹配不到也把請求交給該Host處理,因為該Host被定義為該Engine的默認主機
    • localhost Host獲得請求/test/index.jsp苛骨,匹配它所擁有的所有Context
    • Host匹配到路徑為/test的Context
    • path=/test的Context獲得請求/index.jsp篱瞎,在它的mapping table中尋找對應(yīng)的servlet
    • Context匹配到URL PATTERN為 *.jsp 的servlet苟呐,對應(yīng)于JspServlet類構(gòu)造HttpServletRequest對象和HttpServletResponse對象,作為參數(shù)調(diào)用JspServlet的doGet或doPost方法俐筋。
    • Context把執(zhí)行完了之后的HttpServletResponse對象返回給Host
    • Host把HttpServletResponse對象返回給Engine
    • Engine把HttpServletResponse對象返回給Connector
    • Connector把HttpServletResponse對象返回給瀏覽器端

應(yīng)用部署

根目錄

Tomcat中默認網(wǎng)站根目錄是CATALINA_BASE/webapps/
在Tomcat中部署主站應(yīng)用程序和其他應(yīng)用程序牵素,和之前WEB服務(wù)程序不同。

nginx

假設(shè)在nginx中部署2個網(wǎng)站應(yīng)用eshop澄者、bbs笆呆,假設(shè)網(wǎng)站根目錄是/var/www/html,那么部署可以是這樣的粱挡。
eshop解壓縮所有文件放到/var/www/html/目錄下赠幕。
bbs的文件放在/var/www/html/bbs下。

Tomcat

Tomcat中默認網(wǎng)站根目錄是CATALINA_BASE/webapps/
在Tomcat的webapps目錄中询筏,有個非常特殊的目錄ROOT榕堰,它就是網(wǎng)站默認根目錄。
將eshop解壓后的文件放到這個ROOT中屈留。
bbs解壓后文件都放在CATALINA_BASE/webapps/bbs目錄下局冰。
每一個虛擬主機的目錄都可以使用appBase配置自己的站點目錄,里面都可以使用ROOT目錄作為主站目錄灌危。

JSP WebApp目錄結(jié)構(gòu)

主頁配置:一般指定為index.jsp或index.html
WEB-INF/:當前WebApp的私有資源路徑康二,通常存儲當前應(yīng)用使用的web.xml和context.xml配置文件
META-INF/:類似于WEB-INF
classes/:類文件,當前webapp需要的類
lib/:當前應(yīng)用依賴的jar包

主頁實驗

默認情況下勇蝙,/usr/local/tomcat/webapps/ROOT/下添加一個index.html文件沫勿,觀察訪問到了什么?
將/usr/local/tomcat/conf/web.xml中的下面 <welcome-file-list>標簽 內(nèi)容(默認頁)味混,復(fù)制到/usr/local/tomcat/webapps/ROOT/WEB-INF/web.xml中产雹,如下

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
                    http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1"
    metadata-complete="true">
    
    <display-name>Welcome to Tomcat</display-name>
    <description>
        Welcome to Tomcat
    </description>
        <welcome-file-list>
            <welcome-file>index.jsp</welcome-file>
            <welcome-file>index.htm</welcome-file>
            <welcome-file>index.html</welcome-file>
    </welcome-file-list>
</web-app>

配置修改后,觀察首頁變化

webapp歸檔格式
  • .war:WebApp打包

  • .jar:EJB類打包文件

  • .rar:資源適配器類打包文件

  • .ear:企業(yè)級WebApp打包

    傳統(tǒng)應(yīng)用開發(fā)測試后翁锡,通常打包為war格式蔓挖,這種文件部署到了Tomcat的webapps下,還可以自動展開馆衔。

    <Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
    
部署Deploy
  • 部署:將webapp的源文件放置到目標目錄瘟判,通過web.xml和context.xml文件中配置的路徑就可以訪問該webapp,通過類加載器加載其特有的類和依賴的類到JVM上角溃。
    • 自動部署Auto Deploy:Tomcat發(fā)現(xiàn)多了這個應(yīng)用就把它加載并啟動起來
    • 手動部署
      冷部署:將webapp放到指定目錄拷获,才去啟動Tomcat
      熱部署:Tomcat服務(wù)不停止,需要依賴manager减细、ant腳本匆瓜、tcd(tomcat client deployer)等工具
  • 反部署undeploy:停止webapp的運行,并從JVM上清除已經(jīng)加載的類,從Tomcat應(yīng)用目錄中移 除部署的文件
  • 啟動start:是webapp能夠訪問
  • 停止stop:webapp不能訪問驮吱,不能提供服務(wù)茧妒,但是JVM并不清除它
實驗

1、添加一個文件糠馆,test.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>jsp例子</title>
</head>
<body>
后面的內(nèi)容是服務(wù)器端動態(tài)生成字符串嘶伟,最后拼接在一起
<%
    out.println("hello jsp");
%>
<br>
    <%=request.getRequestURL()%>
</body>
</html>

先把test.jsp放到ROOT下去,試試看又碌,訪問 http://YourIP:8080/test.jsp 九昧。
立即可以看到,這是通過路徑映射找到相應(yīng)的test.jsp后毕匀,轉(zhuǎn)換成test_jsp.java铸鹰,在編譯成test_jsp.class。
/usr/local/tomcat/work/Catalina/localhost/ROOT/org/apache/jsp下有轉(zhuǎn)換后的文件皂岔。

2蹋笼、添加一個應(yīng)用

模擬部署一個應(yīng)用

# cd
常見開發(fā)項目目錄組成
# mkdir projects/myapp/{WEB-INF,classes,lib} -pv
mkdir: 已創(chuàng)建目錄 "projects"
mkdir: 已創(chuàng)建目錄 "projects/myapp"
mkdir: 已創(chuàng)建目錄 "projects/myapp/WEB-INF"
mkdir: 已創(chuàng)建目錄 "projects/myapp/classes"
mkdir: 已創(chuàng)建目錄 "projects/myapp/lib"
常見應(yīng)用首頁,內(nèi)容就用上面的test.jsp
# vi projects/myapp/index.jsp
手動復(fù)制項目目錄到webapps目錄下去
# cp -r projects/myapp/ /usr/local/tomcat/webapps/
使用http://YourIP:8080/myapp/訪問試試看
配置詳解

server.xml

<?xml version="1.0" encoding="UTF-8"?>
<Server port="8005" shutdown="SHUTDOWN">
    <Service name="Catalina">
        <Connector port="8080" protocol="HTTP/1.1"
            connectionTimeout="20000"
            redirectPort="8443" />
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
        <Engine name="Catalina" defaultHost="localhost">
            <Host name="localhost" appBase="webapps"
                unpackWARs="true" autoDeploy="true">
            </Host> 
        </Engine>
    </Service>
</Server>

8005是Tomcat的管理端口躁垛,默認監(jiān)聽在127.0.0.1上剖毯。SHUTDOWN這個字符串接收到后就會關(guān)閉此Server。

# telnet 127.0.0.1 8005
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
SHUTDOWN

這個管理功能建議禁用教馆,改shutdown為一串猜不出的字符串逊谋。

<Server port="8005" shutdown="44ba3c71d57f494992641b258b965f28">

server.xml

<GlobalNamingResources>
    <!-- Editable user database that can also be used by
    UserDatabaseRealm to authenticate users
    -->
    <Resource name="UserDatabase" auth="Container"
        type="org.apache.catalina.UserDatabase"
        description="User database that can be updated and saved"
        factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
        pathname="conf/tomcat-users.xml" />
</GlobalNamingResources

用戶認證,配置文件是conf/tomcat-users.xml

打開tomcat-users.xml土铺,我們需要一個角色manager-gui胶滋。

<tomcat-users xmlns="http://tomcat.apache.org/xml"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
        version="1.0">
    <role rolename="manager-gui"/>
    <user username="wayne" password="wayne" roles="manager-gui"/>
</tomcat-users>

Tomcat啟動加載后,這些內(nèi)容是常駐內(nèi)存的悲敷。如果配置了新的用戶究恤,需要重啟Tomcat。

訪問manager的時候告訴403后德,提示中告訴去manager的context.xml中修改

文件路徑/usr/local/tomcat/webapps/manager/META-INF/context.xml

<?xml version="1.0" encoding="UTF-8"?>
<Context antiResourceLocking="false" privileged="true" >
    <Valve className="org.apache.catalina.valves.RemoteAddrValve"
        allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1" />
    <Manager sessionAttributeValueClassNameFilter="java\.lang\.(?:Boolean|Integer|Long|Number|String)|org\.apache\.catalina\.filters\.CsrfPreventionFilter\$LruCache(?:\$1)?|java\.util\.(?:Linked)?HashMap"/>
</Context>

看正則表達式就知道是本地訪問了部宿,由于當前訪問地址是192.168.x.x,可以修改正則為

allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|192\.168\.\d+\.\d+"

再次測試瓢湃,成功理张。

小筆記:添加admin-gui角色

vim /usr/local/tomcat/conf/tomcat-users.xml
<tomcat-users xmlns="http://tomcat.apache.org/xml"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
        version="1.0">
    <role rolename="manager-gui"/>
    <role rolename="admin-gui"/>
    <user username="wayne" password="wayne" roles="manager-gui,admin-gui"/>
</tomcat-users>

vim /usr/local/tomcat/webapps/manager/META-INF/context.xml
vim /usr/local/tomcat/webapps/host-manager/META-INF/context.xml  
<Context >
    allow="127\.\d+\.\d+\.\d+|::1|0:0:0:0:0:0:0:1|192\.168\.\d+\.\d+"
</Context>

#重啟服務(wù)
/usr/local/tomcat/bin/shutdown.sh && /usr/local/tomcat/bin/start.sh

一般情況下,一個Server實例配置一個Service箱季,name屬性相當于該Service的ID

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

連接器配置。
redirectPort棍掐,如果訪問HTTPS協(xié)議藏雏,自動轉(zhuǎn)向這個連接器。但大多數(shù)時候,Tomcat并不會開啟
HTTPS掘殴,因為Tomcat往往部署在內(nèi)部赚瘦,HTTPS性能較差。

<Engine name="Catalina" defaultHost="localhost">

引擎配置

defaultHost指向內(nèi)部定義某虛擬主機奏寨。缺省虛擬主機可以改動起意,默認localhost。

<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">

虛擬主機配置病瞳。
name必須是主機名揽咕,用主機名來匹配。
appBase套菜,當前主機的網(wǎng)頁根目錄亲善,是相對于CATALINA_HOME,也可以使用絕對路徑
unpackWARs是否自動解壓war格式
autoDeploy 熱部署逗柴,自動加載并運行應(yīng)用

虛擬主機配置實驗

嘗試再配置一個虛擬主機蛹头,并將myapp部署到/data/webapps目錄下

vim /usr/local/tomcat/conf/server.xml
<Host name="node1.magedu.com" appBase="/data/webapps/" unpackWARs="true" autoDeploy="false" />

常見虛擬主機根目錄

# mkdir /data/webapps -pv
mkdir: 已創(chuàng)建目錄 "/data"
mkdir: 已創(chuàng)建目錄 "/data/webapps"
# cp -r ~/projects/myapp/ /data/webapps/ROOT
# pwd
/usr/local/tomcat
# bin/shutdown.sh
# bin/startup.sh

剛才在虛擬主機中主機名定義node1.magedu.com,所以需要主機在本機手動配置一個域名解析戏溺。
如果是windows渣蜗,修改在C:\Windows\System32\drivers\etc下的hosts文件,需要管理員權(quán)限旷祸。
使用http://node1.magedu.com:8080/訪問試試看耕拷。
也可以在tomcat的host-manager中觀察。

Context配置

Context作用:

  • 路徑映射

  • 應(yīng)用獨立配置肋僧,例如單獨配置應(yīng)用日志斑胜、單獨配置應(yīng)用訪問控制

    <Context path="/test" docBase="/data/test" reloadable="false" />
    

path指的是訪問的路徑
docBase,可以是絕對路徑嫌吠,也可以是相對路徑(相對于Host的appBase)
reloadable止潘,true表示如果WEB-INF/classes或META-INF/lib目錄下.class文件有改動,就會將WEB應(yīng)用
重新加載辫诅,性能消耗很大凭戴。生成環(huán)境中,會使用false來禁用炕矮。
將~/projects/myapp/下面的項目文件復(fù)制到/data/下

# cp -r ~/projects/myapp /data/myappv1
# cd /data
# ln -sv myappv1 test

可以修改一下index.jsp好區(qū)別一下么夫。
Tomcat的配置文件server.xml中修改如下

<Host name="node1.magedu.com" appBase="/data/webapps"
    unpackWARs="true" autoDeploy="true" >
    <Context path="/test" docBase="/data/test" reloadable="false" />
</Host>

使用http://node1.magedu.com:8080/test/
注意:這里特別使用了軟鏈接,原因就是以后版本升級肤视,需要將軟鏈接指向myappv2档痪,重啟Tomcat。
如果新版上線后邢滑,出現(xiàn)問題腐螟,重新修改軟鏈接到上一個版本的目錄,并重啟,就可以實現(xiàn)回滾乐纸。

常見部署方式

image.png
Nginx和Tomcat實踐
從epel源安裝nginx
# wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
# yum install nginx -y
# cd /etc/nginx
# vim nginx.conf
# nginx -t
全部反向代理測試
# 全部反向代理測試
location / {
    # proxy_pass http://127.0.0.1:8080; # 不管什么請求衬廷,都會訪問后面的localhost虛擬主機
    proxy_pass http://node1.magedu.com:8080; # 修改服務(wù)器的/etc/hosts
}

http://192.168.142.151/或者http://node1.magedu.com/全部代理給了自定義的虛擬主機注意:node1.magedu.com需要配置解析,可以通過nginx -t測試汽绢。

動靜分離代理
location / {
    root /data/webapps/ROOT;
    index index.html;
}
# ~* 不區(qū)分大小寫
location ~* \.jsp$ {
    proxy_pass http://node1.magedu.com:8080; # /etc/hosts
}

在/data/webapps/ROOT目錄下增加一個index.html吗跋。
http://192.168.142.151/和http://192.168.142.151/index.jsp測試一下看看。
但是實際上Tomcat不太適合做動靜分離宁昭,用它來管理程序的圖片不好做動靜分離部署跌宛。

應(yīng)用管理
# 全部反向代理
location / {
    proxy_pass http://127.0.0.1:8080; # 不管什么請求,都會訪問后面的localhost虛擬主機
}

點擊Tomcat首頁的右上角的“Manager App”按鈕久窟,彈出登錄對話框秩冈。

管理界面

Applications 應(yīng)用程序管理,可以啟動斥扛、停止入问、重加載、反部署稀颁、清理過期session

Deploy 可以熱部署芬失,也可以部署war文件。

[圖片上傳失敗...(image-6eed46-1592277670825)]

Host Manager虛擬主機管理
<tomcat-users xmlns="http://tomcat.apache.org/xml"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
    version="1.0">
    <role rolename="manager-gui"/>
    <role rolename="admin-gui" />
    <user username="wayne" password="wayne" roles="manager-gui,admin-gui"/>
</tomcat-users>

重啟Tomcat匾灶,點擊“Host Manager”按鈕
可以新增虛擬主機棱烂。

httpd和Tomcat實踐
# yum install httpd -y
# httpd -M
# httpd -M | grep proxy
proxy_module (shared)
proxy_ajp_module (shared)
proxy_balancer_module (shared)
proxy_http_module (shared)
httpd配置

proxy_http_module模塊代理配置

<VirtualHost *:80>
    ServerName node1.magedu.com
    ProxyRequests Off
    ProxyVia On
    ProxyPreserveHost On
    ProxyPass / http://127.0.0.1:8080/
    ProxyPassReverse / http://127.0.0.1:8080/
</VirtualHost>

ProxyRequests:Off關(guān)閉正向代理。
ProxyPass:反向代理指令
ProxyPassReverse:保留代理的response頭不重寫(個別除外)
ProxyPreserveHost:On開啟阶女。讓代理保留原請求的Host首部
ProxyVia:On開啟颊糜。代理的請求響應(yīng)時提供一個response的via首部

# vim /etc/httpd/conf.d/http-tomcat.conf
# httpd -t
# systemctl start httpd
# /usr/local/tomcat/bin/startup.sh

http://192.168.142.151/
http://node1.magedu.com/
http://node1.magedu.com/index.jsp

以上3個URL看到了不同的頁面,說明 ProxyPreserveHost On 起了作用秃踩。
設(shè)置 ProxyPreserveHost Off 再看效果衬鱼,說明什么?

proxy_ajp_module模塊代理配置

<VirtualHost *:80>
    ServerName node1.magedu.com
    ProxyRequests Off
    ProxyVia On
    ProxyPreserveHost On
    ProxyPass / ajp://127.0.0.1:8009/
</VirtualHost>

查看Server Status可以看到確實使用的是ajp連接了

相對來講憔杨,A JP協(xié)議基于二進制比使用HTTP協(xié)議的連接器效率高些犹菱。

負載均衡

動態(tài)服務(wù)器的問題尿赚,往往就是并發(fā)能力太弱漫蛔,往往需要多臺動態(tài)服務(wù)器一起提供服務(wù)低淡。如何把并發(fā)的壓力分攤,這就需要調(diào)度寻狂,采用一定的調(diào)度策略岁经,將請求分發(fā)給不同的服務(wù)器,這就是Load Balance負載均衡蛇券。
當單機的Tomcat缀壤,演化出多機多級部署的時候朽们,一個問題便凸顯出來,這就是Session诉位。而這個問題的由來,都是由于HTTP協(xié)議在設(shè)計之初沒有想到未來的發(fā)展菜枷。

HTTP的無狀態(tài)苍糠,有連接和短連接
  • 無狀態(tài):指的是服務(wù)器端無法知道2次請求之間的聯(lián)系,即使是前后2次請求來自同一個瀏覽器啤誊,也沒有任何數(shù)據(jù)能夠判斷出是同一個瀏覽器的請求岳瞭。后來可以通過cookie、session機制來判斷蚊锹。
    • 瀏覽器端第一次HTTP請求服務(wù)器端時瞳筏,在服務(wù)器端使用session這種技術(shù),就可以在服務(wù)器端產(chǎn)生一個隨機值即SessionID發(fā)給瀏覽器端牡昆,瀏覽器端收到后會保持這個SessionID在Cookie當中姚炕,這個Cookie值一般不能持久存儲,瀏覽器關(guān)閉就消失丢烘。瀏覽器在每一次提交HTTP請求的時候會把這個SessionID傳給服務(wù)器端柱宦,服務(wù)器端就可以通過比對知道是誰了
    • Session通常會保存在服務(wù)器端內(nèi)存中,如果沒有持久化播瞳,則易丟失
    • Session會定時過期掸刊。過期后瀏覽器如果再訪問,服務(wù)端發(fā)現(xiàn)沒有此ID赢乓,將給瀏覽器端重新發(fā) 新的SessionID
    • 更換瀏覽器也將重新獲得新的SessionID
  • 有連接:是因為HTTP1.x基于TCP協(xié)議忧侧,是面向連接的,需要3次握手牌芋、4次斷開蚓炬。
  • 短連接:Http 1.1之前,都是一個請求一個連接姜贡,而Tcp的連接創(chuàng)建銷毀成本高试吁,對服務(wù)器有很大
    的影響。所以楼咳,自Http 1.1開始熄捍,支持keep-alive,默認也開啟母怜,一個連接打開后余耽,會保持一段時
    間(可設(shè)置),瀏覽器再訪問該服務(wù)器就使用這個Tcp連接苹熏,減輕了服務(wù)器壓力碟贾,提高了效率币喧。
會話保持方式

1、session sticky會話黏性

  • Session綁定
    nginx:source ip
    HAProxy:cookie
  • 優(yōu)點:簡單易配置
  • 缺點:如果目標服務(wù)器故障后袱耽,如果沒有做sessoin持久化杀餐,就會丟失session

2、session復(fù)制集群

Tomcat自己的提供的多播集群朱巨,通過多播將任何一臺的session同步到其它節(jié)點史翘。
缺點
Tomcat的同步節(jié)點不宜過多,互相即時通信同步session需要太多帶寬
每一臺都擁有全部session冀续,內(nèi)存占用太多

3琼讽、session server

session 共享服務(wù)器,使用memcached洪唐、redis做共享的Session服務(wù)器钻蹬。

規(guī)劃
IP 主機名 服務(wù)
192.168.142.151 t0 調(diào)度器 Nginx、HTTPD
192.168.142.152 t1 tomcat1 JDK8凭需、Tomcat8
192.168.142.153 t2 tomcat2 JDK8问欠、Tomcat8
每臺主機的域名解析
192.168.142.151 t0.magedu.com t0
192.168.142.152 t1.magedu.com t1
192.168.142.153 t2.magedu.com t2
環(huán)境變量配置
# vim /etc/profile.d/tomcat.sh
export CATALINA_HOME=/usr/local/tomcat
export PATH=$CATALINA_HOME/bin:$PATH
項目路徑配置
# mkdir -pv /data/webapps/ROOT
編寫測試jsp文件,內(nèi)容在下面
# vim /data/webapps/ROOT/index.jsp
# scp -r server.xml 192.168.142.153:/usr/local/tomcat
啟動Tomcat服務(wù)
# startup.sh
測試用jsp

t1和t2節(jié)點的/data/webapps/ROOT/index.jsp

<%@ page import="java.util.*" %>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>lbjsptest</title>
</head>
<body>
<div>On <%=request.getServerName() %></div>
<div><%=request.getLocalAddr() + ":" + request.getLocalPort() %></div>
<div>SessionID = <span style="color:blue"><%=session.getId() %></span></div>
<%=new Date()%>
</body>
</html>

t1虛擬主機配置

<Engine name="Catalina" defaultHost="t1.magedu.com">
    <Host name="t1.magedu.com" appBase="/data/webapps" autoDeploy="true" />
</Engine>

t2虛擬主機配置

<Engine name="Catalina" defaultHost="t2.magedu.com">
    <Host name="t2.magedu.com" appBase="/data/webapps" autoDeploy="true" />
</Engine>
Nginx調(diào)度
# yum install nginx
# cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak
# vim /etc/nginx/nginx.conf
# nginx -t
# systemctl start nginx.service

nginx配置如下

upstream tomcats {
    #ip_hash; # 先禁用看看輪詢粒蜈,之后開啟開黏性
    server t1.magedu.com:8080;
    server t2.magedu.com:8080;
}
server {
    location ~* \.(jsp|do)$ {
        proxy_pass http://tomcats;
    }
}

#小筆記:
vim /etc/nginx/nginx.conf
http {
    ...
    upstream tomcats {
        ip_hash;
        server t1.magedu.com:8080;
        server t2.magedu.com:8080;
    }
    server {
        location / {
            proxy_pass http://tomcats;
        }
    }
    ...
}

測試http://t0.magedu.com/index.jsp溅潜,可以看到輪詢調(diào)度效果。
在upstream中使用ip_hash指令薪伏,使用客戶端IP地址Hash滚澜。這個hash值使用IP v4地址的前24位或全部的IP v6地址。
配置完reload nginx服務(wù)嫁怀。測試一下看看效果设捐。關(guān)閉Session對應(yīng)的Tomcat服務(wù),再重啟啟動它塘淑,看看Session的變化萝招。

Httpd調(diào)度

使用 httpd -M 可以看到proxy_balancer_module,用它來實現(xiàn)負載均衡存捺。

方式 依賴模塊
http負載均衡 mod_proxy mod_proxy_http mod_proxy_balancer
ajp負載均衡 mod_proxy mod_proxy_ajp mod_proxy_balancer
關(guān)閉httpd默認主機
# cd /etc/httpd/conf
# vim httpd.conf
注釋掉 #DocumentRoot "/var/www/html"
# cd ../conf.d
# vim vhosts.conf
# httpd -t
# systemctl start httpd
負載均衡配置說明
配置代理到balancer
ProxyPass [path] !|url [key=value [key=value ...]]
Balancer成員
BalancerMember [balancerurl] url [key=value [key=value ...]]
設(shè)置Balancer或參數(shù)
ProxySet url key=value [key=value ...]

ProxyPass和BalancerMember指令參數(shù)

參數(shù) 缺省值 說明
min 0 連接池最小容量
max 1 - n 連接池最大容量
retry 60 apache請求發(fā)送到后端服務(wù)器錯誤后等待的時間秒數(shù)槐沼。0表示立即重試

Balancer參數(shù)

參數(shù) 缺省值 說明
loadfactor 定義負載均衡后端服務(wù)器權(quán)重,取值范圍1 - 100
lbmethod byrequests 負載均衡調(diào)度方法捌治。 byrequests 基于權(quán)重的統(tǒng)計請求個數(shù)進行調(diào)度岗钩; bytrafficz 執(zhí)行基于權(quán)重的流量計數(shù)調(diào)度; bybusyness 通過考量每個后端服務(wù)器當前負載進行調(diào)度
maxattempts 1 放棄請求前實現(xiàn)故障轉(zhuǎn)移的次數(shù)肖油,默認為1兼吓,其最大值不應(yīng)該大 于總的節(jié)點數(shù)
nofailover Off 如果后端服務(wù)器沒有Session副本,可以設(shè)置為On不允許故障 轉(zhuǎn)移森枪。 Off故障可以轉(zhuǎn)移
stickysession 調(diào)度器的sticky session名字视搏,根據(jù)web后臺編程語言不同审孽,可 以設(shè)置為JSESSIONID或PHPSESSIONID

ProxySet指令也可以使用上面的參數(shù)

conf.d/vhosts.conf內(nèi)容如下

<VirtualHost *:80>
    ProxyRequests Off
    ProxyVia On
    ProxyPreserveHost On
    ProxyPass / balancer://lbtomcats/
    ProxyPassReverse / balancer://lbtomcats/
</VirtualHost>
    <Proxy balancer://lbtomcats>
    BalancerMember http://t1.magedu.com:8080 loadfactor=1
    BalancerMember http://t2.magedu.com:8080 loadfactor=2
</Proxy

loadfactor設(shè)置為1:2,便于觀察浑娜。觀察調(diào)度的結(jié)果是輪詢的

小筆記

#tomcat-server1
vim /usr/local/src/tomcat/conf/server.xml
<Engine name="Catalina" defaultHost="t1.magedu.com" jvmRoute="Tomcat1"> 
    ...
</Engine>

#tomcat-server2
vim /usr/local/src/tomcat/conf/server.xml
<Engine name="Catalina" defaultHost="t2.magedu.com" jvmRoute="Tomcat2"> 
    ...
</Engine>
使用session黏性

修改conf.d/vhosts.conf

Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED
<VirtualHost *:80>
    ProxyRequests Off
    ProxyVia On
    ProxyPreserveHost On
    ProxyPass / balancer://lbtomcats/
    ProxyPassReverse / balancer://lbtomcats/
</VirtualHost>
<Proxy balancer://lbtomcats>
    BalancerMember http://t1.magedu.com:8080 loadfactor=1 route=Tomcat1
    BalancerMember http://t2.magedu.com:8080 loadfactor=2 route=Tomcat2
    ProxySet stickysession=ROUTEID
</Proxy>

發(fā)現(xiàn)Session不變了佑力,一直找的同一個Tomcat服務(wù)器。

ajp調(diào)度

修改conf.d/vhosts.conf

<VirtualHost *:80>
    ProxyRequests Off
    ProxyVia On
    ProxyPreserveHost On
    ProxyPass / balancer://lbtomcats/
    ProxyPassReverse / balancer://lbtomcats/
</VirtualHost>
<Proxy balancer://lbtomcats>
    BalancerMember ajp://t1.magedu.com:8009 loadfactor=1 route=Tomcat1
    BalancerMember ajp://t2.magedu.com:8009 loadfactor=2 route=Tomcat2
    #ProxySet stickysession=ROUTEID
</Proxy>
  • ProxySet stickysession=ROUTEID 先禁用看看切換效果筋遭,開啟后看看黏住效果搓萧。
    開啟后,發(fā)現(xiàn)Session不變了宛畦,一直找的同一個Tomcat服務(wù)器。

  • 雖然揍移,上面的做法實現(xiàn)客戶端在一段時間內(nèi)找同一臺Tomcat次和,從而避免切換后導(dǎo)致的Session丟失。但是如果Tomcat節(jié)點掛掉那伐,那么Session依舊丟失踏施。

  • 假設(shè)有A、B兩個節(jié)點罕邀,都把Session做了持久化畅形。如果Tomcat A服務(wù)下線期間用戶切換到了Tomcat B上,就獲得了Tomcat B的Session诉探,就算持久化Session的Tomcat A上線了日熬,也沒用了。

Tomcat Session集群

參考:https://tomcat.apache.org/tomcat-8.5-doc/cluster-howto.html

        <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                 channelSendOptions="8">

          <Manager className="org.apache.catalina.ha.session.DeltaManager"
                   expireSessionsOnShutdown="false"
                   notifyListenersOnReplication="true"/>

          <Channel className="org.apache.catalina.tribes.group.GroupChannel">
            <Membership className="org.apache.catalina.tribes.membership.McastService"
                        address="228.0.0.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="192.168.142.152"
                      port="4000"
                      autoBind="100"
                      selectorTimeout="5000"
                      maxThreads="6"/>

            <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
              <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
            </Sender>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
          </Channel>

          <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                 filter=""/>
          <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

          <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                    tempDir="/tmp/war-temp/"
                    deployDir="/tmp/war-deploy/"
                    watchDir="/tmp/war-listen/"
                    watchEnabled="false"/>

          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
        </Cluster>

配置說明

  • Cluster 集群配置

  • Manager 會話管理器配置

  • Channel 信道配置

    Membership 成員判定肾胯。使用什么多播地址竖席、端口多少、間隔時長ms敬肚、超時時長ms毕荐。同一個多播地址和端口認為同屬一個組。使用時修改這個多播地址艳馒,以防沖突
    Receiver 接收器憎亚,多線程接收多個其他節(jié)點的心跳、會話信息弄慰。默認會從4000到4100依次嘗試可用端口第美。

    • address="auto",auto可能綁定到127.0.0.1上陆爽,所以一定要改為可以用的IP上去Sender 多線程發(fā)送器斋日,內(nèi)部使用了tcp連接池。

    Interceptor 攔截器

  • Valve
    ReplicationValve 檢測哪些請求需要檢測Session墓陈,Session數(shù)據(jù)是否有了變化恶守,需要啟動復(fù)
    制過程

  • ClusterListener
    ClusterSessionListener 集群session偵聽器

    使用 <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
    添加到 <Engine> 所有虛擬主機都可以啟用Session復(fù)制
    添加到 <Host> 第献,該虛擬主機可以啟用Session復(fù)制
    最后,在應(yīng)用程序內(nèi)部啟用了才可以使用

    前提:
    時間同步兔港,確保NTP或Chrony服務(wù)正常運行庸毫。 # systemctl status chronyd
    防火墻規(guī)則。 # systemctl stop firewalld

    IP 主機名 服務(wù)
    192.168.142.151 t0 調(diào)度器 Nginx衫樊、HTTPD
    192.168.142.152 t1 tomcat1 JDK8飒赃、Tomcat8
    192.168.142.153 t2 tomcat2 JDK8、Tomcat8

    本次把多播復(fù)制的配置放到缺省虛擬主機里面科侈, 即Host之下载佳。
    特別注意修改Receiver的address屬性為一個本機可對外的IP地址

    t1的server.xml中,如下

    <Host name="t1.magedu.com" appBase="/data/webapps" autoDeploy="true" >
      其他略去
      <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
       address="192.168.142.152"
       port="4000"
       autoBind="100"
       selectorTimeout="5000"
       maxThreads="6"/>
    

    t2的server.xml中臀栈,如下

    <Host name="t2.magedu.com" appBase="/data/webapps" autoDeploy="true" >
      其他略去
      <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
          address="192.168.142.153"
          port="4000"
          autoBind="100"
          selectorTimeout="5000"
          maxThreads="6"/>
    

    Tomcat重啟后蔫慧,ss命令能看到tomcat監(jiān)聽在4000端口上
    嘗試使用剛才配置過得負載均衡(移除Session黏性),測試發(fā)現(xiàn)Session還是變來變?nèi)ァ?/p>

    準備web.xml
    在應(yīng)用中增加WEB-INF权薯,從全局復(fù)制一個web.xml過來

    # cp /usr/local/tomcat/conf/web.xml /data/webapps/ROOT/WEB-INF/
    

    為web.xml的 <web-app> 標簽增加子標簽 <distributable/> 來開啟該應(yīng)用程序的分布式姑躲。
    重啟全部Tomcat,通過負載均衡調(diào)度到不同節(jié)點盟蚣,返回的SessionID不變了黍析。

小筆記;復(fù)制集群

1屎开、apache
#tomcat1
vim conf/server.xml
<host>
        <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                 channelSendOptions="8">

          <Manager className="org.apache.catalina.ha.session.DeltaManager"
                   expireSessionsOnShutdown="false"
                   notifyListenersOnReplication="true"/>

          <Channel className="org.apache.catalina.tribes.group.GroupChannel">
            <Membership className="org.apache.catalina.tribes.membership.McastService"
                        address="228.0.0.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="192.168.142.152"
                      port="4000"
                      autoBind="100"
                      selectorTimeout="5000"
                      maxThreads="6"/>

            <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
              <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
            </Sender>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
          </Channel>

          <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                 filter=""/>
          <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

          <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                    tempDir="/tmp/war-temp/"
                    deployDir="/tmp/war-deploy/"
                    watchDir="/tmp/war-listen/"
                    watchEnabled="false"/>
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
        </Cluster>    
</host>

#tomcat2
vim conf/server.xml     
<Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="192.168.142.153"
#其他配置跟tomacat1一樣
</Receiver>
bin/shutdown.sh
bin/startup.sh          

2阐枣、nginx
#tomcat1
vim conf/server.xml
<host>
        <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"
                 channelSendOptions="8">

          <Manager className="org.apache.catalina.ha.session.DeltaManager"
                   expireSessionsOnShutdown="false"
                   notifyListenersOnReplication="true"/>

          <Channel className="org.apache.catalina.tribes.group.GroupChannel">
            <Membership className="org.apache.catalina.tribes.membership.McastService"
                        address="228.0.0.4"
                        port="45564"
                        frequency="500"
                        dropTime="3000"/>
            <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                      address="192.168.142.152"     #修改這為本機IP地址
                      port="4000"
                      autoBind="100"
                      selectorTimeout="5000"
                      maxThreads="6"/>

            <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
              <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
            </Sender>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
            <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
          </Channel>

          <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"
                 filter=""/>
          <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>

          <Deployer className="org.apache.catalina.ha.deploy.FarmWarDeployer"
                    tempDir="/tmp/war-temp/"
                    deployDir="/tmp/war-deploy/"
                    watchDir="/tmp/war-listen/"
                    watchEnabled="false"/>
          <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
        </Cluster>    
</host>
cp /usr/local/tomcat/conf/web.xml /data/webapps/ROOT/WEB-INF/
vim conf/web.xml        #添加標簽" <distributable/> "
<web-app>
    <distributable/>        #開啟集群
</web-app>

NoSQL

NoSQL是對非SQL、非傳統(tǒng)關(guān)系型數(shù)據(jù)庫的統(tǒng)稱奄抽。
NoSQL一詞誕生于1998年侮繁,2009年這個詞匯被再次提出指非關(guān)系型、分布式如孝、不提供ACID的數(shù)據(jù)庫設(shè)計模式宪哩。
隨著互聯(lián)網(wǎng)時代的到來,數(shù)據(jù)爆發(fā)式增長第晰,數(shù)據(jù)庫技術(shù)發(fā)展日新月異锁孟,要適應(yīng)新的業(yè)務(wù)需求。
隨著移動互聯(lián)網(wǎng)茁瘦、物聯(lián)網(wǎng)的到來品抽,大數(shù)據(jù)的技術(shù)中NoSQL也同樣重要。

https://db-engines.com/en/ranking

分類

  • Key-value Store
    redis甜熔、memcached
  • Document Store
    mongodb圆恤、CouchDB
  • Column Store列存數(shù)據(jù)庫,Column-Oriented DB
    HBase腔稀、Cassandra
  • Graph DB
    Neo4j
  • Time Series 時序數(shù)據(jù)庫
    InfluxDB

Memcached

Memcached只支持能序列化的數(shù)據(jù)類型盆昙,不支持持久化羽历,基于Key-Value的內(nèi)存緩存系統(tǒng)。

內(nèi)存分配機制

應(yīng)用程序運行需要使用內(nèi)存存儲數(shù)據(jù)淡喜,但對于一個緩存系統(tǒng)來說秕磷,申請內(nèi)存、釋放內(nèi)存將十分頻繁炼团,非
常容易導(dǎo)致大量內(nèi)存碎片澎嚣,最后導(dǎo)致無連續(xù)可用內(nèi)存可用。
Memcached采用了Slab Allocator機制來分配瘟芝、管理內(nèi)存 易桃。

  • Page:分配給Slab的內(nèi)存空間,默認為1MB锌俱,分配后就得到一個Slab晤郑。Slab分配之后內(nèi)存按照固定字節(jié)大小等分成chunk。
  • Chunk:用于緩存記錄kv值的內(nèi)存空間嚼鹉。Memcached會根據(jù)數(shù)據(jù)大小選擇存到哪一個chunk中,假設(shè)chunk有128bytes驱富、64bytes锚赤,數(shù)據(jù)只有100bytes存儲在128bytes中,存在些浪費褐鸥。
    Chunk最大就是Page的大小线脚,即一個Page中就一個Chunk
  • Slab Class:Slab按照大小分組,就組成不同的Slab Class
  • 如果有100bytes要存叫榕,那么Memcached會選擇上圖中Slab Class 2存儲浑侥,因為它是120bytes的Chunk。
  • Slab之間的差異可以使用Growth Factor控制晰绎,默認1.25寓落。
懶過期Lazy Expiration

memcached不會監(jiān)視數(shù)據(jù)是否過期,而是在取數(shù)據(jù)時才看是否過期荞下,過期的把數(shù)據(jù)有效期限標識為0伶选,并不清除該數(shù)據(jù)。以后可以覆蓋該位置存儲其它數(shù)據(jù)尖昏。

LRU

當內(nèi)存不足時仰税,memcached會使用LRU(Least Recently Used)機制來查找可用空間,分配給新紀錄使用抽诉。

集群

Memcached集群陨簇,稱為基于客戶端的分布式集群。
Memcached集群內(nèi)部并不互相通信迹淌,一切都需要客戶端連接到Memcached服務(wù)器后自行組織這些節(jié)點河绽,并決定數(shù)據(jù)存儲的節(jié)點己单。

安裝
# yum install memcached
# rpm -ql memcached
/etc/sysconfig/memcached
/usr/bin/memcached
/usr/bin/memcached-tool
/usr/lib/systemd/system/memcached.service
/usr/share/doc/memcached-1.4.15
/usr/share/doc/memcached-1.4.15/AUTHORS
/usr/share/doc/memcached-1.4.15/CONTRIBUTORS
/usr/share/doc/memcached-1.4.15/COPYING
/usr/share/doc/memcached-1.4.15/ChangeLog
/usr/share/doc/memcached-1.4.15/NEWS
/usr/share/doc/memcached-1.4.15/README.md
/usr/share/doc/memcached-1.4.15/protocol.txt
/usr/share/doc/memcached-1.4.15/readme.txt
/usr/share/doc/memcached-1.4.15/threads.txt
/usr/share/man/man1/memcached-tool.1.gz
/usr/share/man/man1/memcached.1.gz
# cat /usr/lib/systemd/system/memcached.service
[Service]
Type=simple
EnvironmentFile=-/etc/sysconfig/memcached
ExecStart=/usr/bin/memcached -u $USER -p $PORT -m $CACHESIZE -c $MAXCONN $OPTIONS
# cat /etc/sysconfig/memcached
PORT="11211"
USER="memcached"
MAXCONN="1024"
CACHESIZE="64"
OPTIONS=""
前臺顯示看看效果
# memcached -u memcached -p 11211 -f 1.25 -vv
# systemctl start memcached
  • 修改memcached運行參數(shù),可以使用下面的選項修改/etc/sysconfig/memcached文件

    -u username memcached運行的用戶身份葵姥,必須普通用戶
    -p 綁定的端口荷鼠,默認11211
    -m num 最大內(nèi)存,單位MB榔幸,默認64MB
    -c num 最大連接數(shù)允乐,缺省1024
    -d 守護進程方式運行
    -f 增長因子Growth Factor,默認1.25
    -v 詳細信息削咆,-vv能看到詳細信息
    -M 內(nèi)存耗盡牍疏,不許LRU
    -U 設(shè)置UDP監(jiān)聽端口,0表示禁用UDP

# yum list all | grep memcached
memcached.x86_64 1.4.15-10.el7_3.1 @base
libmemcached.i686 1.0.16-5.el7 base
libmemcached.x86_64 1.0.16-5.el7 base
libmemcached-devel.i686 1.0.16-5.el7 base
libmemcached-devel.x86_64 1.0.16-5.el7 base
memcached-devel.i686 1.4.15-10.el7_3.1 base
memcached-devel.x86_64 1.4.15-10.el7_3.1 base
opensips-memcached.x86_64 1.10.5-4.el7 epel
php-ZendFramework-Cache-Backend-Libmemcached.noarch
php-pecl-memcached.x86_64 2.2.0-1.el7 epel
python-memcached.noarch 1.48-4.el7 base
uwsgi-router-memcached.x86_64 2.0.17.1-2.el7 epel

與memcached通信的不同語言的連接器拨齐。
libmemcached提供了C庫和命令行工具鳞陨。

協(xié)議
查看/usr/share/doc/memcached-1.4.15/protocol.txt

# yum install telnet
# telnet locahost 11211
stats
add mykey 1 60 4
test
STORED
get mykey
VALUE mykey 1 4
test
END
set mykey 1 60 5
test1
STORED
get mykey
VALUE mykey 1 5
test1
END

add key flags exptime bytes , 增加key瞻惋,過期時間為秒厦滤,bytes為存儲數(shù)據(jù)的字節(jié)數(shù)

session共享服務(wù)器

msm

msm(memcached session manager)提供將Tomcat的session保持到memcached或redis的程序,可以實現(xiàn)高可用歼狼。
目前項目托管在Github掏导,https://github.com/magro/memcached-session-manager
支持Tomcat的6.x、7.x羽峰、8.x趟咆、9.x。

  • Tomcat的Session管理類梅屉,Tomcat版本不同
    memcached-session-manager-2.3.2.jar
    memcached-session-manager-tc8-2.3.2.jar
  • Session數(shù)據(jù)的序列化值纱、反序列化類
    官方推薦kyro
    在webapp中WEB-INF/lib/下
  • 驅(qū)動類
    memcached(spymemcached.jar)
    Redis(jedis.jar)

安裝

https://github.com/magro/memcached-session-manager/wiki/SetupAndConfiguration
將spymemcached.jar、memcached-session-manage坯汤、kyro相關(guān)的jar文件都放到Tomcat的lib目錄中去虐唠,這個目錄是 $CATALINA_HOME/lib/ ,對應(yīng)本次安裝就是/usr/local/tomcat/lib惰聂。

asm-5.2.jar
kryo-3.0.3.jar
kryo-serializers-0.45.jar
memcached-session-manager-2.3.2.jar
memcached-session-manager-tc8-2.3.2.jar
minlog-1.3.1.jar
msm-kryo-serializer-2.3.2.jar
objenesis-2.6.jar
reflectasm-1.11.9.jar
spymemcached-2.12.3.jar
sticky模式
原理

當請求結(jié)束時Tomcat的session會送給memcached備份凿滤。即Tomcat session為主session,memcached session為備session庶近,使用memcached相當于備份了一份Session翁脆。
查詢Session時Tomcat會優(yōu)先使用自己內(nèi)存的Session,Tomcat通過jvmRoute發(fā)現(xiàn)不是自己的Session鼻种,便從memcached中找到該Session反番,更新本機Session,請求完成后更新memcached。

部署
<t1> <t2>
. \ / .
.  X .
. / \ .
<m1> <m2>

t1和m1部署在一臺主機上罢缸,t2和m2部署在同一臺篙贸。

配置

放到 $CATALINA_HOME/conf/context.xml 中
特別注意,t1配置中為failoverNodes="n1"枫疆, t2配置為failoverNodes="n2"

以下是sticky的配置
<Context>
...
    <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
        memcachedNodes="n1:192.168.142.152:11211,n2:192.168.142.153:11211"
        failoverNodes="n1"
        requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
        transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
    />      
</Context>
memcachedNodes="n1:host1.yourdomain.com:11211,n2:host2.yourdomain.com:11211"

memcached的節(jié)點們爵川;n1、n2只是別名息楔,可以重新命名寝贡。
failoverNodes故障轉(zhuǎn)移節(jié)點,n1是備用節(jié)點值依,n2是主存儲節(jié)點圃泡。另一臺Tomcat將n1改為n2,其主節(jié)點是n1愿险,備用節(jié)點是n2颇蜡。

實驗

如果配置成功,可以在logs/catalina.out中看到下面的內(nèi)容

信息 [t1.magedu.com-startStop-1]
de.javakaffee.web.msm.MemcachedSessionService.startInternal --------
- finished initialization:
- sticky: true
- operation timeout: 1000
- node ids: [n2]
- failover node ids: [n1]
- storage key prefix: null
- locking mode: null (expiration: 5s)

配置成功后辆亏,網(wǎng)頁訪問以下风秤,頁面中看到了Session。然后運行下面的Python程序扮叨,就可以看到是否存儲到了memcached中了缤弦。

import memcache # pip install python-memcached
mc = memcache.Client(['192.168.142.152:11211', '192.168.142.153:11211'], debug=True)

stats = mc.get_stats()[0]
print(stats)
for k,v in stats[1].items():
    print(k, v)

print('-' * 30)
# 查看全部key
print(mc.get_stats('items')) # stats items 返回 items:5:number 1
print('-' * 30)

for x in mc.get_stats('cachedump 5 0'):# stats cachedump 5 0 # 5和上面的items返回的值有關(guān);0表示全部
    print(x)

t1甫匹、t2甸鸟、n1惦费、n2依次啟動成功兵迅,分別使用http://t1.magedu.com:8080/http://t2.magedu.com:8080/ 觀察。
看起負載均衡調(diào)度器薪贫,通過http://t0.magedu.com來訪問看看效果

On tomcats
192.168.142.153:8080
SessionID = 2A19B1EB6D9649C9FED3E7277FDFD470-n2.Tomcat1
Wed Jun 26 16:32:11 CST 2019
On tomcats
192.168.142.152:8080
SessionID = 2A19B1EB6D9649C9FED3E7277FDFD470-n1.Tomcat2
Wed Jun 26 16:32:36 CST 2019

可以看到瀏覽器端被調(diào)度到不同Tomcat上恍箭,但是都獲得了同樣的SessionID。
停止t2瞧省、n2看看效果扯夭,恢復(fù)看看效果。

小筆記:測試msm

systemctl stop redis    #停一臺查看
systemctl memcached     #都停用查看
#查看使用python程序
non-sticky模式
原理

從msm 1.4.0之后開始支持non-sticky模式鞍匾。
Tomcat session為中轉(zhuǎn)Session交洗,如果n1為主session,n2為備session橡淑。產(chǎn)生的新的Session會發(fā)送給主构拳、備memcached,并清除本地Session。
n1下線置森,n2轉(zhuǎn)正斗埂。n1再次上線,n2依然是主Session存儲節(jié)點凫海。

memcached配置

放到 $CATALINA_HOME/conf/context.xml 中

<Context>
...
    <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
        memcachedNodes="n1:192.168.142.152:11211,n2:192.168.142.153:11211"
        sticky="false"
        sessionBackupAsync="false"
        lockingMode="uriPattern:/path1|/path2"
        requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
        transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
    />
</Context>
redis配置

下載jedis.jar呛凶,放到 $CATALINA_HOME/lib/ ,對應(yīng)本次安裝就是/usr/local/tomcat/lib行贪。

# yum install redis
# vim /etc/redis.conf
bind 0.0.0.0
# systemctl start redis

放到 $CATALINA_HOME/conf/context.xml 中

<Context>
...
    <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
        memcachedNodes="redis://192.168.142.152:6379"
        sticky="false"
        sessionBackupAsync="false"
        lockingMode="uriPattern:/path1|/path2"
        requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$"
        transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
    />
</Context>

python程序

import redis
client = redis.Redis(host='192.168.142.140', port=6379 db=1)

print(client.keys())
print(client.get, get('abc'))
client.set('abc',0b1100010) 
print(client.get, get('abc'))

client.set(0b11,'啊'.encode())   
print(client.get('3'))

總結(jié)

  • 通過多組實驗漾稀,使用不同技術(shù)實現(xiàn)了session持久機制
  1. session綁定,基于IP或session cookie的瓮顽。其部署簡單县好,尤其基于session黏性的方式,粒度小暖混,
    對負載均衡影響小缕贡。但一旦后端服務(wù)器有故障,其上的session丟失拣播。
  2. session復(fù)制集群晾咪,基于tomcat實現(xiàn)多個服務(wù)器內(nèi)共享同步所有session。此方法可以保證任意一
    臺后端服務(wù)器故障贮配,其余各服務(wù)器上還都存有全部session谍倦,對業(yè)務(wù)無影響。但是它基于多播實現(xiàn)
    心跳泪勒,TCP單播實現(xiàn)復(fù)制昼蛀,當設(shè)備節(jié)點過多,這種復(fù)制機制不是很好的解決方案圆存。且并發(fā)連接多的
    時候叼旋,單機上的所有session占據(jù)的內(nèi)存空間非常巨大,甚至耗盡內(nèi)存沦辙。
  3. session服務(wù)器夫植,將所有的session存儲到一個共享的內(nèi)存空間中兽间,使用多個冗余節(jié)點保存
    session订雾,這樣做到session存儲服務(wù)器的高可用捌肴,且占據(jù)業(yè)務(wù)服務(wù)器內(nèi)存較小履磨。是一種比較好的解
    決session持久的解決方案帐姻。
  • 以上的方法都有其適用性靠抑。生產(chǎn)環(huán)境中篮奄,應(yīng)根據(jù)實際需要合理選擇商叹。

    不過以上這些方法都是在內(nèi)存中實現(xiàn)了session的保持兔综,可以使用數(shù)據(jù)庫或者文件系統(tǒng)饿凛,把session數(shù)據(jù)存儲起來隅俘,持久化。這樣服務(wù)器重啟后笤喳,也可以重新恢復(fù)session數(shù)據(jù)为居。不過session數(shù)據(jù)是有時效性的,是否需要這樣做杀狡,視情況而定蒙畴。

JVM

Java目前是最流行的編程語言。
目前主要應(yīng)用在企業(yè)級WEB開發(fā)和大數(shù)據(jù)領(lǐng)域呜象。

JVM組成

使用Java語言編寫.java Source Code文件膳凝,通過javac編譯成.class Byte Code文件。

class loader類加載器:將所需的類加載到內(nèi)存恭陡,必要時將類實例化成實例蹬音。

圖中中間部分是進程的內(nèi)存邏輯結(jié)構(gòu),稱為Jvm運行時區(qū)域休玩,由下面幾部分構(gòu)成:
方法區(qū):所有線程共享的內(nèi)存空間著淆,存放已加載的類信息、常量和靜態(tài)變量拴疤。
heap堆:所有線程共享的內(nèi)存空間永部,存放創(chuàng)建的所有對象。堆是靠GC垃圾回收器管理的呐矾。
Java棧:每個線程會分配一個棧苔埋,存放線程用的本地變量、方法參數(shù)和返回值等蜒犯。

PC寄存器:PC, 即Program Counter组橄,每一個線程用于記錄當前線程正在執(zhí)行的字節(jié)碼指令地址。因為線程需要切換罚随,當一個線程被切換回來需要執(zhí)行的時候玉工,知道執(zhí)行到哪里了。

本地方法棧:為本地方法執(zhí)行構(gòu)建的內(nèi)存空間毫炉,存放本地方法執(zhí)行時的局部變量瓮栗、操作數(shù)等削罩。

所謂本地方法瞄勾,簡單的說是非Java實現(xiàn)的方法,例如操作系統(tǒng)的C編寫的庫提供的本地方法弥激,Java調(diào)用這些本地方法接口執(zhí)行进陡。但是要注意,本地方法應(yīng)該避免直接編程使用微服,因為Java可能跨平臺使用趾疚,如果用了Windows API,換到了Linux平臺部署就有了問題。

虛擬機

目前Oracle官方使用的是HotSpot糙麦, 它最早由一家名為"Longview Technologies"公司設(shè)計辛孵,使用了很 多優(yōu)秀的設(shè)計理念和出色的性能,1997年該公司被SUN公司收購赡磅。后來隨著JDK一起發(fā)布了HotSpot VM魄缚。目前HotSpot是最主要的VM。

安卓程序需要運行在JVM上焚廊,而安卓平臺使用了Google自研的Java虛擬機——Dalvid冶匹,適合于內(nèi)存、處 理器能力有限系統(tǒng)咆瘟。

GC垃圾收集器

堆內(nèi)存里面經(jīng)常創(chuàng)建嚼隘、銷毀對象,內(nèi)存也是被使用袒餐、被釋放飞蛹。如果不妥善處理,一個使用頻繁的進程灸眼, 將可能有內(nèi)存容量桩皿,但是無法分配出可用內(nèi)存空間,因為沒有連續(xù)成片的內(nèi)存了幢炸,內(nèi)存全是碎片化的數(shù) 據(jù)泄隔。

回收基本算法
  1. 引用計數(shù)
    每一個堆內(nèi)對象上都與一個私有引用計數(shù)器,記錄著被引用的次數(shù)宛徊,引用計數(shù)清零佛嬉,該對象所占用
    堆內(nèi)存就可以被回收。循環(huán)引用的對象都無法引用計數(shù)歸零闸天,就無法清除暖呕。
  2. 標記-清除 Mark-Sweep
    分垃圾標記階段和內(nèi)存釋放階段。標記階段苞氮,找到所有可訪問對象打個標記湾揽。清理階段,遍歷整個
    堆笼吟,對未標記對象清理
  3. 復(fù)制 Copying
    先將可用內(nèi)存分為大小相同兩塊區(qū)域A和B库物,每次只用其中一塊,比如A贷帮。當A用完后戚揭,則將A中存活的對象復(fù)制到B。復(fù)制到B的時候連續(xù)的使用內(nèi)存撵枢,最后將A一次性清除干凈民晒。缺點是比較浪費內(nèi)存精居,能使用原來一半的內(nèi)存,因為內(nèi)存對半劃分了潜必,復(fù)制過程畢竟也是有代價靴姿。好處是沒有碎片,復(fù)制過程中保證對象使用連續(xù)空間磁滚。
  4. 標記-壓縮 Mark-Compact
    分垃圾標記階段和內(nèi)存整理階段空猜。標記階段,找到所有可訪問對象打個標記恨旱。內(nèi)存清理階段時辈毯,整理時將對象向內(nèi)存一端移動,整理后存活對象連續(xù)的集中在內(nèi)存一端搜贤。
  5. 分代收集算法
    既然上述垃圾回收算法都有優(yōu)缺點谆沃,能不能對不同數(shù)據(jù)進行區(qū)分管理,不同分區(qū)對數(shù)據(jù)實施不同回收策略仪芒,分而治之唁影。
    1.7及以前,堆內(nèi)存分為新生代掂名、老年代据沈、持久代。
    1.8開始饺蔑,持久代沒有了锌介,取而代之MetaSpace。
STW

對于大多數(shù)垃圾回收算法而言猾警,GC線程工作時孔祸,需要停止所有工作的線程,稱為Stop The World发皿。GC完成時崔慧,恢復(fù)其他工作線程運行。這也是JVM運行中最頭疼的問題穴墅。

分代堆內(nèi)存
image.png

Heap堆內(nèi)存分為

  • 新生代:剛剛創(chuàng)建的對象
    伊甸園區(qū)
    存活區(qū)Servivor Space:有2個存活區(qū)惶室,一個是from區(qū),一個是to區(qū)玄货。它們大小相等皇钞、地位相同、可互換誉结。
  • to指的是本次復(fù)制數(shù)據(jù)的目標區(qū)
  • 老年代:長時間存活的對象
  • 持久代:JVM的類和方法
新生代回收

起始時鹅士,所有新建對象都出生在eden券躁,當eden滿了惩坑,啟動GC掉盅。這個稱為Young GC,Minor GC以舒。

先標記eden存活對象趾痘,然后將存活對象復(fù)制到s0(假設(shè)本次是s0,也可以是s1蔓钟,它們可以調(diào)換)永票,eden剩余所有空間都“清空”。GC完成滥沫。

繼續(xù)新建對象侣集,當eden滿了,啟動GC兰绣。

先標記eden和s0中存活對象世分,然后將存活對象復(fù)制到s1。將eden和s0“清空”缀辩。

繼續(xù)新建對象臭埋,當eden滿了,啟動GC臀玄。

先標記eden和s1中存活對象瓢阴,然后將存活對象復(fù)制到s0。將eden和s1“清空”健无。

以后就重復(fù)上面的步驟荣恐。

大多數(shù)對象都不會存活很久,而且創(chuàng)建活動非常多累贤,新生代就需要頻繁垃圾回收募胃。

但是,如果一個對象一直存活畦浓,它最后就在from痹束、to來回復(fù)制,如果from區(qū)中對象復(fù)制次數(shù)達到閾值讶请,就直接復(fù)制到老年代祷嘶。

老年代回收

進入老年代的數(shù)據(jù)較少,所以老年代區(qū)被占滿的速度較慢夺溢,所以垃圾回收也不頻繁论巍。老年代GC稱為OldGC,Major GC风响。

由于老年代對象一般來說存活次數(shù)較長嘉汰,所有較常采用標記-壓縮算法。

Full GC:對所有“代”的內(nèi)存進行垃圾回收Minor GC比較頻繁状勤,Major GC較少鞋怀。但一般Major GC時双泪,由于老年代對象也可以引用新生代對象,所以先進行一次Minor GC密似,然后在Major GC會提高效率焙矛。可以認為回收老年代的時候完成了一次Full
GC残腌。

GC觸發(fā)條件
Minor GC觸發(fā)條件:當eden區(qū)滿了觸發(fā)
Full GC觸發(fā)條件:

  • 老年代滿了
  • 新生代搬向老年代村斟,老年代空間不夠
  • 持久代滿了
  • System.gc()手動調(diào)用。不推薦

調(diào)整策略

減少STW時長抛猫,串行變并行
減少GC次數(shù)蟆盹,要分配合適的內(nèi)存大小
對JVM調(diào)整策略應(yīng)用極廣

  • 在WEB領(lǐng)域中Tomcat等
  • 在大數(shù)據(jù)領(lǐng)域Hadoop生態(tài)各組件
  • 在消息中間件領(lǐng)域的Kafka等
  • 在搜索引擎領(lǐng)域的ElasticSearch、Solr等

在不同領(lǐng)域?qū)VM需要不同的調(diào)整策略

垃圾收集器類型

按回收線程數(shù):

  • 串行垃圾回收器:一個GC線程完成回收工作
  • 并行垃圾回收器:多個GC線程同時一起完成回收工作闺金,充分利用CPU資源

指的是GC線程是否串并行

按工作模式不同:

  • 并發(fā)垃圾回收器:讓GC線程垃圾回收某些階段可以和工作線程一起進行日缨。
  • 獨占垃圾回收器:只有GC在工作,STW一直進行到回收完畢掖看,工作線程才能繼續(xù)執(zhí)行匣距。

指的是GC線程和工作線程是否一起運行

一般情況下,我們大概可以使用以下原則:
客戶端或較小程序哎壳,內(nèi)存使用量不大毅待,可以使用串行回收;
對于服務(wù)端大型計算归榕,可以使用并行回收尸红;
大型WEB應(yīng)用,用戶端不愿意等刹泄,盡量少的STW外里,可以使用并發(fā)回收;

內(nèi)存調(diào)整
參數(shù) 說明 舉例
-Xms 設(shè)置應(yīng)用程序初始使用的堆內(nèi)存大刑厥(新生代+老 年代) -Xms2g
-Xmx 設(shè)置應(yīng)用程序能獲得的最大堆內(nèi)存 早期JVM不建議超過32G盅蝗,內(nèi)存管理效率下降 -Xms4g
-XX:NewSize 設(shè)置初始新生代大小
-XX:MaxNewSize 設(shè)置最大新生代內(nèi)存空間
-XX:NewRatio 以比例方式設(shè)置新生代和老年代 -XX:NewRatio=2 new/old=1/2
- XX:SurvivorRatio 以比例方式設(shè)置eden和survivor - XX:SurvivorRatio=6 eden/survivor=6/1 survivor/new=1/8
-Xss 設(shè)置線程的棧大小
$ java -Xms512m -Xmx1g HelloWorld
// 測試用java程序
// javac HelloWorld.java
// java -Xms512m -Xmx1g HelloWorld
public class HelloWorld extends Thread {
    public static void main(String[] args) {
    try {
        while (true) {
            Thread.sleep(2000);
            System.out.println("hello magedu");
        }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Tomcat設(shè)置

默認不指定,-Xmx大約使用了1/4的內(nèi)存姆蘸,當前本機內(nèi)存指定約為1G墩莫。
在bin/catalina.sh中增加

JAVA_OPTS="-server -Xmx512m -Xms128m -XX:NewSize=48m -XX:MaxNewSize=200m"

-server:VM運行在server模式,為在服務(wù)器端最大化程序運行速度而優(yōu)化
-client:VM運行在Client模式逞敷,為客戶端環(huán)境減少啟動時間而優(yōu)化

重啟Tomcat狂秦,觀察

# ps aux | grep java
root 8980 2.7 12.3 3050788 123216 pts/0 Sl 21:53 0:05
/usr/java/default/bin/java -
Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties -
Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Xmx512m -Xms128m -
XX:NewSize=48m -XX:MaxNewSize=200m -Djdk.tls.ephemeralDHKeySize=2048 -
Djava.protocol.handler.pkgs=org.apache.catalina.webresources -
Dorg.apache.catalina.security.SecurityListener.UMASK=0027 -Dignore.endorsed.dirs= -
classpath /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar -
Dcatalina.base=/usr/local/tomcat -Dcatalina.home=/usr/local/tomcat -
Djava.io.tmpdir=/usr/local/tomcat/temp org.apache.catalina.startup.Bootstrap start
垃圾回收器

新生代

  • 新生代串行收集器:單線程、獨占式串行推捐,回收算法標記-復(fù)制
  • 新生代并行收集器:將單線程的串行收集器變成了多線程并行裂问、獨占式
  • 新生代并行回收收集器:多線程并行、獨占式,使用復(fù)制算法堪簿,關(guān)注調(diào)整吞吐量

老年代

  • 老年代串行收集器:單線程痊乾、獨占式串行,回收算法使用標記-壓縮
  • 老年代并行回收收集器:多線程戴甩、獨占式并行符喝,回收算法使用標記-壓縮闪彼,關(guān)注調(diào)整吞吐量

CMS收集器

  • Concurrent Mark Sweep并發(fā)標記清除算法
  • 在某些階段盡量使用和工作線程一起運行甜孤,減少停頓時長。是互聯(lián)網(wǎng)站點服務(wù)端BS系統(tǒng)上較 佳的回收算法
  • 分為4個階段:初始標記畏腕、并發(fā)標記缴川、重新標記、并發(fā)清除描馅,在初始標記把夸、重新標記時需要 STW。

G1收集器

  • Garbage First是最新垃圾回收器铭污,從JDK1.6實驗性提供恋日,JDK1.7發(fā)布,其設(shè)計目標是在多處理器嘹狞、大內(nèi)存服務(wù)器端提供優(yōu)于CMS收集器的吞吐量和停頓控制的回收器岂膳。建議JDK8再考慮它。
  • 基于標記-壓縮算法磅网。+UseG1GC
  • 分為4個階段:初始標記谈截、并發(fā)標記、最終標記涧偷、篩選回收簸喂。并發(fā)標記并發(fā)執(zhí)行,其它階段STW只有GC線程并行執(zhí)行燎潮。

垃圾收集器設(shè)置

可以單獨指定新生代喻鳄、老年代的垃圾收集器

  • -XX:+UseSerialGC
    運行在Client模式下,新生代确封、老年代都啟用 串行收集器

  • -XX:+UseParNewGC
    新生代使用 并行收集器 诽表,老年代使用 串行收集器

  • -XX:+UseParallelGC
    新生代使用 并行回收收集器 ,老年代使用 串行收集器

  • -XX:+UseParallelOldGC
    新生代隅肥、老年代都是用 并行回收收集器
    -XX:ParallelGCThreads=8竿奏,在關(guān)注吞吐量的場景使用它增加并行線程數(shù)

  • XX:+UseConcMarkSweepGC

    • 新生代使用 并行收集器 ,老年代使用 CMS收集器
    • 響應(yīng)時間要短腥放,停頓短使用這個垃圾收集器
    • -XX:CMSInitiatingOccupancyFraction=N泛啸,N為0-100整數(shù)表示達到老年代的大小的百分比多少觸發(fā)回收
      默認68
    • 由于CMS算法有碎片產(chǎn)生,還可以將-XX:+UseCMSCompactAtFullCollection開啟秃症,在CMS收集后候址,進行一次內(nèi)存碎片整理吕粹。-XX:CMSFullGCsBeforeCompaction=N設(shè)定多少次CMS后,進行一次整理
    -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection
    -XX:CMSFullGCsBeforeCompaction=5
    

    將參數(shù)加入到bin/catalina.sh中岗仑,重啟觀察Tomcat status匹耕。老年代已經(jīng)使用CMS

  • 開啟垃圾回收統(tǒng)計信息
    -XX:+PrintGC 輸出GC信息
    -XX:+PrintGCDetails 輸出GC詳細信息
    -XX:+PrintGCTimeStamps 與前兩個組合使用,在信息上加上一個時間戳
    -XX:+PrintHeapAtGC 生成更多信息供分析荠雕,日志會更大
    以上調(diào)試完成后稳其,請移除這些參數(shù),否則有非常多的日志輸出

工具

$JAVA_HOME/bin下

命令 說明
jps 查看所有jvm進程
jinfo 查看進程的運行環(huán)境參數(shù)炸卑,主要是jvm命令行參數(shù)
jstat 對jvm應(yīng)用程序的資源和性能進行實時監(jiān)控
jstack 查看所有線程的運行狀態(tài)
jmap 查看jvm占用物理內(nèi)存的狀態(tài)
jhat +UseParNew
jconsole
jvisualvm
jps:Java virutal machine Process Status tool既鞠,
    jps [-q] [-mlvV] [<hostid>]
        -q:靜默模式;
        -v:顯示傳遞給jvm的命令行參數(shù)盖文;
        -m:輸出傳入main方法的參數(shù)嘱蛋;
        -l:輸出main類或jar完全限定名稱;
        -v:顯示通過flag文件傳遞給jvm的參數(shù)五续;
        [<hostid>]:主機id洒敏,默認為localhost;
        
詳細列出當前Java進程信息
# jps -l -v
jinfo:輸出給定的java進程的所有配置信息疙驾;
    jinfo [option] <pid>
        -flags:打印 VM flags
        -sysprops:to print Java system properties
        -flag <name>:to print the value of the named VM flag
        
先獲得一個java進程ID凶伙,然后jinfo
# jps
# jinfo 6822
# jinfo -flags 6822
jstat:輸出指定的java進程的統(tǒng)計信息
    jstat -help|-options
    jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]]
    [<interval> [<count>]]
        interval:時間間隔,單位是毫秒荆萤;
        count:顯示的次數(shù)镊靴;

返回可用統(tǒng)計項列表
# jstat -options
    -class:class loader
    -compiler:JIT
    -gc:gc
    -gccapacity:統(tǒng)計堆中各代的容量
    -gccause:
    -gcmetacapacity
    -gcnew:新生代
    -gcnewcapacity
    -gcold:老年代
    -gcoldcapacity
    -gcutil
    -printcompilation

# jstat -gc 6822
YGC:新生代的垃圾回收次數(shù);
YGCT:新生代垃圾回收消耗的時長链韭;
FGC:Full GC的次數(shù)偏竟;
FGCT:Full GC消耗的時長;
GCT:GC消耗的總時長敞峭;
3次踊谋,一秒一次
# jstat -gcnew 6822 1000 3

程序員常用堆棧情況查看工具

jstack:查看指定的java進程的線程棧的相關(guān)信息;
    jstack [-l] <pid>
    jstack -F [-m] [-l] <pid>
        -l:long listings旋讹,會顯示額外的鎖信息殖蚕,因此,發(fā)生死鎖時常用此選項沉迹;
        -m:混合模式睦疫,既輸出java堆棧信息,也輸出C/C++堆棧信息鞭呕;
        -F:當使用“jstack -l PID"無響應(yīng)蛤育,可以使用-F強制輸出信息;
        
先獲得一個java進程ID,然后jinfo
# jps
# jstack -l 6822

jmap:Memory Map, 用于查看堆內(nèi)存的使用狀態(tài)瓦糕;
查看進程堆內(nèi)存情況
# jmap -heap 6822

jhat:Java Heap Analysis Tool
    jmap [option] <pid>
    
    查看堆空間的詳細信息:
    jmap -heap <pid>
    
    查看堆內(nèi)存中的對象的數(shù)目:
    jmap -histo[:live] <pid>
        live:只統(tǒng)計活動對象底洗;
        
    保存堆內(nèi)存數(shù)據(jù)至文件中,而后使用jvisualvm或jhat進行查看:
    jmap -dump:<dump-options> <pid>
        dump-options:
        live        dump only live objects; if not specified, all objects in the heap are dumped.
    format=b binary format
    file=<file> dump heap to <file>     
Tomcat常用配置
1咕娄、內(nèi)存空間優(yōu)化
JAVA_OPTS="-server -Xms32g -Xmx32g -XX:NewSize= -XX:MaxNewSize= "
    -server:服務(wù)器模式
    -Xms:堆內(nèi)存初始化大泻ヒ尽;
    -Xmx:堆內(nèi)存空間上限圣勒;
    -XX:NewSize=:新生代空間初始化大蟹驯洹;
    -XX:MaxNewSize=:新生代空間最大值灾而;
2胡控、線程池調(diào)整
<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

常用屬性:

  • maxThreads:最大線程數(shù)扳剿,默認200旁趟;
  • minSpareThreads:最小空閑線程數(shù);
  • maxSpareThreads:最大空閑線程數(shù)庇绽;
  • acceptCount:當啟動線程滿了之后锡搜,等待隊列的最大長度,默認100瞧掺;
  • URIEncoding:URI地址編碼格式耕餐,中文建議使用UTF-8;
  • enableLookups:是否啟用客戶端主機名的DNS反向解析辟狈,缺省禁用肠缔,建議禁用,就使用客戶端IP就行哼转;
  • compression:是否啟用傳輸壓縮機制明未,建議“on",CPU和流量的平衡;
    • compressionMinSize:啟用壓縮傳輸?shù)臄?shù)據(jù)流最小值壹蔓,單位是字節(jié)趟妥;
    • compressableMimeType:定義啟用壓縮功能的MIME類型;
      text/html, text/xml, text/css, text/javascript
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載佣蓉,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者披摄。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市勇凭,隨后出現(xiàn)的幾起案子疚膊,更是在濱河造成了極大的恐慌,老刑警劉巖虾标,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寓盗,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機贞让,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進店門周崭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人喳张,你說我怎么就攤上這事续镇。” “怎么了销部?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵摸航,是天一觀的道長。 經(jīng)常有香客問我舅桩,道長酱虎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任擂涛,我火速辦了婚禮读串,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘撒妈。我一直安慰自己恢暖,他們只是感情好,可當我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布狰右。 她就那樣靜靜地躺著杰捂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪棋蚌。 梳的紋絲不亂的頭發(fā)上嫁佳,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機與錄音谷暮,去河邊找鬼蒿往。 笑死,一個胖子當著我的面吹牛坷备,可吹牛的內(nèi)容都是我干的熄浓。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼省撑,長吁一口氣:“原來是場噩夢啊……” “哼赌蔑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起竟秫,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤娃惯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后肥败,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體趾浅,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡次舌,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年厢钧,在試婚紗的時候發(fā)現(xiàn)自己被綠了构罗。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片机隙。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖证膨,靈堂內(nèi)的尸體忽然破棺而出如输,到底是詐尸還是另有隱情,我是刑警寧澤央勒,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布不见,位于F島的核電站,受9級特大地震影響崔步,放射性物質(zhì)發(fā)生泄漏稳吮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一井濒、第九天 我趴在偏房一處隱蔽的房頂上張望灶似。 院中可真熱鬧,春花似錦眼虱、人聲如沸喻奥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至润梯,卻和暖如春过牙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背纺铭。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工寇钉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人舶赔。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓扫倡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親竟纳。 傳聞我的和親對象是個殘疾皇子撵溃,可洞房花燭夜當晚...
    茶點故事閱讀 43,465評論 2 348