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,如下圖
名稱 | 說明 |
---|---|
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)回滾乐纸。
常見部署方式
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 firewalldIP 主機名 服務(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持久機制
- session綁定,基于IP或session cookie的瓮顽。其部署簡單县好,尤其基于session黏性的方式,粒度小暖混,
對負載均衡影響小缕贡。但一旦后端服務(wù)器有故障,其上的session丟失拣播。 - 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)存沦辙。 - 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ù)泄隔。
回收基本算法
- 引用計數(shù)
每一個堆內(nèi)對象上都與一個私有引用計數(shù)器,記錄著被引用的次數(shù)宛徊,引用計數(shù)清零佛嬉,該對象所占用
堆內(nèi)存就可以被回收。循環(huán)引用的對象都無法引用計數(shù)歸零闸天,就無法清除暖呕。 - 標記-清除 Mark-Sweep
分垃圾標記階段和內(nèi)存釋放階段。標記階段苞氮,找到所有可訪問對象打個標記湾揽。清理階段,遍歷整個
堆笼吟,對未標記對象清理 - 復(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ù)空間磁滚。 - 標記-壓縮 Mark-Compact
分垃圾標記階段和內(nèi)存整理階段空猜。標記階段,找到所有可訪問對象打個標記恨旱。內(nèi)存清理階段時辈毯,整理時將對象向內(nèi)存一端移動,整理后存活對象連續(xù)的集中在內(nèi)存一端搜贤。 - 分代收集算法
既然上述垃圾回收算法都有優(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)存
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