編程語言,是用來定義計算機程序的形式語言。他是一種被標準化的交流技巧,用來向計算機發出指令。一種計算機語言讓程序員可以準肯定義計算機所須要使用的數據,並準確的定義在不一樣狀況下所應採起的行動,這裏簡單將編程語言分爲一下三類:
硬件級:微碼編程
系統級:C,C++
應用級:Java, PHP, Python, Ruby, Perl, C#, ...css
程序由指令加數據組成,編程主要有兩種形式:
過程式編程:面向過程的編程,以指令爲中心,數據服務與代碼。shell爲表明。
對象式編程:面向對象的編程,以數據爲中心,指令服務與數據;Java就是面向對象的編程語言。html
Java程序設計語言
Java class文件格式
Java API類庫
Java VM 運行時區域:方法區、堆、Java棧、PC寄存器、本地方法棧;前端
用java語言編譯源代碼,把他編譯成java class文件,而後在Java VM中運行class文件;但編寫程序時,經過調用類庫(Java API)中的方法來訪問系統資源,也知足Java API的調用。Java VM和Java API一塊兒組成了一個平臺,全部的Java程序都在其上編譯和運行,所以,他們有時也被稱做是Java運行時環境。
Java VM的主要任務是裝載class文件,而且執行其中的字節碼。Java VM包含一個類裝載器(class loader),他能夠從程序和API裝載class文件;而Java API的類只在程序執行中須要時纔會被裝載。java
Java字節碼有執行引擎來執行。而不一樣的Java VM 中,其執行引擎的實現可能不一樣,最簡單的執行引擎不是一次性解釋字節碼,而另外一種成爲「即時編譯器(just-in-time complier)」的執行引擎執行速度更快。但要消耗更多的內存資源,即時編譯模式下,第一次被執行的字節碼會被編譯成成本地機器代碼並緩存下以實現複用。第三中至此那個引擎是所謂的自適應優化器,此種方法中,虛擬機開始的時候解釋字節碼, 屆時會監視運行中程序的活動,而且記錄下使用最頻繁的代碼。程序運行時,虛擬機只把那些活動最頻繁的代碼編譯成本地代碼,而不頻繁的代碼則仍然保留爲字節碼由虛擬機解釋執行。自適應優化器可使得Java VM在80%-90%的時間裏執行被優化過的本地代碼,而只須要編譯10%-20%對性能有影響的代碼。最後一種虛擬機由硬件芯片構成,它用本地方法執行Java字節碼,其執行引擎內嵌於芯片中。mysql
Java代碼的每個源代碼一般以.java結尾,java程序運行時首先經過編譯器將.java的代碼編譯成.class結尾的類文件。要注意的是這些類文件須要遵照必定的規則,java源碼編譯完成之後生成類文件,這些類文件須要在JVM上運行,然而市面上各類公司、組織所開發了JVM又都有所不一樣。要實現這些類文件能夠在各類JVM的環境下順利運行,就須要不管是JAVA的類文件仍是JVM的開發都遵照必定的規則,在必定的協議框架之下實現各類類文件在不一樣的JVM中運行。
另外一方面,類文件在運行過程當中必然會涉及到各類通用代碼,爲了簡化編寫代碼的複雜程度Java集成了類文件在運行過程當中所依賴的各類通用代碼的類文件庫,也就是java API的類文件庫。JVM在邏輯上由兩部分組成:class loader類文件加載器,以及執行引擎。Java程序可以得以在JVM中運行不只要依賴類文件加載器去加載java源代碼的類文件,同時也要加載源代碼類文件運行依賴的其餘類庫文件。
class loader類加載器不只須要將java代碼的類文件加載到執行引擎中,同時還要負責java代碼運行所依賴的類庫文件,加載完這二者之後,java代碼纔會在java虛擬機中的執行引擎上運行。而執行引擎是由C語言開發的,因此最終java代碼的運行仍是會轉化成標準C庫的運行以及系統調用上的某寫功能的運行。linux
Java代碼的運行:
*.java(source code) --> javac --> *.class(bytecode)
loader,加載程序的類文件,及程序的類文件依賴到的其它的類文件然後運行; 整個運行表現爲一個jvm進程;nginx
SE包含了Java二進制程序(如JVM和Java字節碼編譯器)和Java的核心代碼庫,而Jave EE標準則包含了一組適用於建立企業級Web應用程序的API。Jave EE創建在Java SE的基礎上,並依賴於Java SE才能正常工做。固然,任何級別的應用程序均能從Java EE中獲益,但Jave EE卻更適合解決大型軟件系統設計中的問題。
Java SE APIs:
JNDI(Java Naming and Directory Interface):用於與LDAP服務交互的API;
JAXP(Java API for XML Processing):用於分析及轉換XML(基於XSLT實現);git
Mobile Edition用途有限,不作介紹程序員
任何的Java 2 EE的開發都是基於 Java 2 SE的。
JAVA EE包含多個獨立的API,Servlet和JSP就是其中的兩個。github
而JAVA EE中著名的API中還包含以下的幾個:
EJB(Enterprise JavaBeans):JAVA相關的諸多高級功能的實現,如RMI(Remote Method Invocation), 對象/關係映射,跨越多個數據源的分佈式事務等;
JMS(Java Message Service):高性能異步消息服務,實現JAVA EE應用程序與非JAVA程序的「透明」通訊;
JMX(Java Management Extensions):在程序運行時對其進行交互式監控和管理的機制;
JTA(Java Transaction API):容許應用程序在自身的一個或多個組件中平滑地處理錯誤的機制;
JavaMail:經過工業標準的POP/SMTP/IMAP協議發送和接收郵件的機制;
Servlet:服務端小程序文件,這種類使得Java代碼能夠藉助與CGI技術在服務器端運行,並基於HTTP協議或者其餘協議跟客戶端進行交互響應客戶端請求。servlet程序運行時加載了servlet類自己的各類應用程序,而且基於CGI技術或其餘技術實現與客戶端進行交流。這種運行環境就叫作servlet Container(servlet 容器)。
servlet 容器事實上所實現的功能就是當客戶端發送一個URL請求時服務器端Java編寫的應用程序代碼在servlet容器中運行,並將運行結果以一個HTML格式組織的頁面返回給客戶端。可是應用程序本來是隻能識別程序代碼的。既然返回給客戶端的頁面是以HTML格式組織的,就必然要求該應用程序有打印頁面標題的功能。servlet在這一功能的實現中僅僅提供了一個規範,早期程序員基於servlet開發網絡頁面時是須要將頁面標題等文本信息硬編碼到頁面程序中的。這種頁面開發方式極其繁瑣。而Java 2 EE中另外一技術JSP則解決了這一問題。
JSP: Java Server Page,換種說法JSP能夠理解爲servlet的前端,JSP實現了HTML格式的文本能夠直接嵌入在Java代碼中。JSP代碼內嵌在Java代碼中之後會被程序代碼自動識別並翻譯成servlet語言在servlet容器中運行使用程序進行輸出實現與客戶端的交互。
<html> <title>TITLE</title> <body> <h1>...</h1> <% ... java code... %> </body> </html> #.jsp -->jasper--> .java --> javac --> .class --> jvm #注意:基於jasper將靜態輸出的數據轉爲java代碼進行輸出,結果爲servlet規範的代碼
所以要部署任何以JSP所研發的應用程序,實現JSP最終以Java代碼的形式運行,就必需要有Java代碼運行的容積:
JSP實現HTML格式的文本嵌入在servlet代碼中被自動識別,servlet使得Java代碼能夠基於CGI技術經過HTTP協議與客戶端進行交互。servlet類做爲類文件被加載到Java虛擬機的執行引擎中實現Java代碼的運行。這一系列的組合實現被成爲Web Container。
商業實現:
WebSphere, WebLogic, Oc4j, Glassfish, Geronimo, JOnAS, JBoss, ...
開源實現:
Tomcat, Jetty, Resin, ...
Tomcat: Java 2 EE技術體系的不完整實現;
catalina:servlet container
coyote:http connection
jasper:JSP Engine
Tomcat 的開發語言:JAVA
Tomcat Instance:運行中的tomcat進程(Java進程)
圖中的各類組件只是Tomcat衆多組件中的幾個比較重要的組件,在Tomcat中每一個組件都由「類」來實現,而有些類的實現還不止一種。
頂級類組件:server
服務類組件:service
容器類組件:能夠部署Webapp的組件,例如:engine,host,context
鏈接器組件:connector
被嵌套類組件:valve,logger,realm
<Server> <Service> <connector/> <connector/> ... <Engine> <Host> <Context/> <Context/> ... </Host> <Host> ... </Host> </Engine> </Service> </Server>
一個server對應一個tomcat實例,一個操做系統能夠運行多個tomcat實例,前提是這多個tomcat監聽的端口不一樣,各自的配置文件不衝突,對應的文檔再也不同一目錄下。
tomcat的單實例內存不能大於32G,當tomcat的單實例大於32G時會出現內存管理問題。所以當生產當中一臺服務器的運行內存有128G或者更大時,而該服務器又專門用來提供tomcat服務。這個時候就能夠考慮在一臺主機上運行多個tomcat實例。
instance,即表現出的一個java進程;監聽在8005端口,只接收「SHUTDOWN」。各server監聽的端口不能相同,所以,在同一物理主機啓動多個實例時,須要修改其監聽端口爲不一樣的端口;
建議中止該服務,port=-1,或者將命令改成其餘字符
如上圖中所示Tomcat最核心的組件就是Engine,全部的程序最終都要以Java代碼的形式在Engine中運行,可是Engine沒有經過B/S協議與客戶端進行交互的能力。Engine要依賴於鏈接器Connetctor接入解析用戶的請求,並將運行結果構建成響應報文來響應客戶端。可是Engine與鏈接器Connetctor之間也沒法進行交互,因此就須要一個輔助的組件讓鏈接器與Engine之間也創建起關聯關係,這個輔助組件就叫作service。
一般來說一個鏈接器只服務於一個Engine,可是一個Engine能夠有多個鏈接器。舉例來說:用戶經過http協議發來的請求由http鏈接器負責解析和處理,並經過service所關聯的Engine創建起與Engine的交互關係將用戶的請求發送給Engine。一樣的若是用戶的請求經過https協議發送給服務器,那麼就須要用https的鏈接器經過service關聯的同一個Engine創建與Engine的交互而後發送用戶請求。
可想而知service的功能歸納來說就是將若干個鏈接器與一個engine創建關聯關係,因此要求一個service中只能有一個Engine。
屬性:
className:org.apache.catalina.core.StandardService #用於實現service的類名 name:此服務的名稱,默認爲Catalina;
Tomcat的核心組件,用於運行JSP或者servlet代碼;即servlet引擎,其內部能夠一個或多個host組件來定義站點; 一般須要經過defaultHost來定義默認的虛擬主機;
屬性:
name= defaultHost="localhost" #默認引擎 jvmRoute= #後端服務的惟一標識
接入並解析用戶請求,將請求映射爲Engine中運行的代碼;以後將運行結果構建成響應報文;
常見的有三類http/https/ajp;
進入tomcat的請求可分爲兩類:
(1) standalone : 請求來自於客戶端瀏覽器;
(2) 由其它的web server反代:來自前端的反代服務器;
nginx --> http connector --> tomcat
httpd(proxy_http_module) --> http connector --> tomcat
AJP(Apache JServ Protocol):
AJP是面向數據包的基於TCP/IP的協議,它在Apache和Tomcat的實例之間提供了一個專用的通訊信道。目前經常使用AJP協議的版本是1.3,它主要有如下特徵:
1) 在快速網絡有着較好的性能表現,支持數據壓縮傳輸;
2) 支持SSL,加密及客戶端證書;
3) 支持Tomcat實例集羣;
4) 支持在apache和tomcat之間的鏈接的重用;
httpd(proxy_ajp_module) --> ajp connector --> tomcat
httpd(mod_jk) --> ajp connector --> tomcat ajp協議做爲二進制協議工做效率要比http文本協議高
屬性:
port="8080" protocol="AJP/1.3" #http協議不用標ajp協議須要標註 connectionTimeout="20000"等待客戶端發送請求的超時時間 address:監聽的IP地址;默認爲本機全部可用地址; maxThreads:最大併發鏈接數,默認爲200; enableLookups:是否啓用DNS查詢功能;#通常關閉 acceptCount:等待隊列的最大長度; redirectPort:若是某鏈接器支持的協議是HTTP,當接收客戶端發來的HTTPS請求時,則轉發至此屬性定義的端口; secure: sslProtocol:pls1.1,1.2,tls
下面是一個定義了多個屬性的SSL鏈接器:
<Connector port="8443" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" acceptCount="100" debug="0" scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" />
能夠將Tomcat中的Engine來類比成一個Web server,相對於Engine來講Host就相似於httpd中的虛擬主機;
示例:
<Host name="localhost" appBase="webapps" #相似documentroot 定義主站點 unpackWARs="true" #自動展開功能 autoDeploy="true"> #自動部署 </Host>
經常使用屬性說明:
(1) appBase:此Host的webapps的默認存放目錄,指存放非歸檔的web應用程序的目錄或歸檔的WAR文件目錄路徑;可使用基於$CATALINA_BASE變量所定義的路徑的相對路徑;
(2) autoDeploy:在Tomcat處於運行狀態時,將某webapp放置於appBase所定義的目錄中時,是否自動將其部署至tomcat;
示例:
<Host name="a.com" appBase="/appdata/webapps" unpackWARs="true" autoDeploy="true"> </Host> \# mkdir -pv /appdata/webapps \# mkdir -pv /appdata/webapps/ROOT/{lib,classes,WEB-INF}
提供一個測試頁便可;
主機別名定義:
若是一個主機有兩個或兩個以上的主機名,額外的名稱都可以以別名的形式進行定義,以下:
<Host name="www.a.com" appBase="webapps" unpackWARs="true"> <Alias>a.com</Alias> </Host>
相似於httpd中的alias,通常若是一個網頁資源是一個獨立的運行程序的話那麼就須要將他歸爲Context中單獨配置管理。
經常使用的屬性定義有:
1) docBase:相應的Web應用程序的存放位置;也可使用相對路徑,起始路徑爲此Context所屬Host中appBase定義的路徑;切記,docBase的路徑名不能與相應的Host中appBase中定義的路徑名有包含關係,好比,若是appBase爲deploy,而docBase毫不能爲deploy-bbs類的名字;
2) path:相對於Web服務器根路徑而言的URI;若是爲空「」,則表示爲此webapp的根路徑;若是context定義在一個單獨的xml文件中,此屬性不須要定義;
3) reloadable:是否容許從新加載此context相關的Web應用程序的類;默認爲false;
示例:
<Context path="/PATH" docBase="/PATH/TO/SOMEDIR" reloadable=""/>
<Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" prefix="localhost_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> Valve存在多種類型: 定義訪問日誌:org.apache.catalina.valves.AccessLogValve 定義訪問控制:org.apache.catalina.valves.RemoteAddrValve <Valve className="org.apache.catalina.valves.RemoteAddrValve" deny="172\.16\.100\.67"/>
standalone:
經過內置的web server(http connector)來接收客戶端的請求;
proxy:有專門的web server服務客戶端的http請求;
in-process:部署於同一主機;
network:部署於不一樣主機;
Tomcat的安裝有三種方式,光盤中base源中有rpm包。或者下載編譯過以後的安裝包。也能夠下載源碼編譯安裝,因爲java的體質因此tomcat編譯以前就是Java源代碼,編譯以後就是類文件。tomcat的編譯須要專門的java編譯器來進行編譯,這點與通常的軟件用GCC編譯不一樣。
Java程序的運行須要Java運行環境須要先下載JVM
OpenJDK:
java-VERSION-openjdk:
The OpenJDK runtime environment.
java-VERSION-openjdk-headless:
The OpenJDK runtime environment without audio and video support.
java-VERSION-openjdk-devel:
The OpenJDK development tools.
CentOS 7:
VERSION:1.6.0, 1.7.0, 1.8.0
注意:多版本並存時,可以使用 alternatives命令設定默認使用的版本;
Oracle JDK:
安裝相應版本的rpm包;
jdk-VERSION-OS-ARCH.rpm
例如:jdk-1.8.0_25-linux-x64.rpm
注意:安裝完成後,要配置JAVA_HOME環境變量,指向java的安裝路徑;
OpenJDK:
JAVA_HOME=/usr
Oracle JDK:
JAVA_HOME=/usr/java/jdk_VERSION
yum安裝:
[root@CentOS6 ~]#yum list *java* #看JDK包名 [root@CentOS6 ~]#yum install java-1.8.0-openjdk.x86_64
查看JDK版本:
[root@CentOS6 ~]#java -version openjdk version "1.8.0_121" OpenJDK Runtime Environment (build 1.8.0_121-b13) OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)
yum安裝:
[root@CentOS6 ~]#yum install tomcat
二進制包安裝:
Tomcat binary release: # tar xf apache-tomcat-VERSION.tar.gz -C /usr/local/ # cd /usr/local # ln -sv apache-tomcat-VERSION tomca
配置環境變量:
/etc/profile.d/tomcat.sh export CATALINA_BASE=/usr/local/tomcat export PATH=$CATALINA_BASE/bin:$PATH
除非要使tomcat監聽在小於1024的端口上時須要以管理員的身份去運行tomcat,不然出於安全考慮都應該使用普通用戶的身份去運行tomcat,使用普通用戶運行tomcat是要注意文件權限問題。
bin:腳本,及啓動時用到的類;
conf:配置文件目錄;
lib:庫文件,Java類庫,jar;
logs:日誌文件目錄;
temp:臨時文件目錄;
webapps:webapp的默認目錄;
work:工做目錄,存放編譯後的字節碼文件;
server.xml:主配置文件;
web.xml:每一個webapp只有「部署」後才能被訪問,它的部署方式一般由web.xml進行定義,其存放位置爲WEB-INF/目錄中;此文件爲全部的webapps提供默認部署相關的配置;
context.xml:每一個webapp均可以使用的配置文件,它一般由專用的配置文件context.xml來定義,其存放位置爲WEB-INF/目錄中;此文件爲全部的webapps提供默認配置;
tomcat-users.xml:用戶認證的帳號和密碼文件;角色(role),用戶(User);此文件在tomcat啓動時被裝入內存;
catalina.policy:當使用-security選項啓動tomcat時,用於爲tomcat設置安全策略;
catalina.properties:Java屬性的定義文件,用於設定類加載器路徑,以及一些與JVM調優相關參數;
logging.properties:日誌系統相關的配置;
/: webapps的根目錄
index.jsp:主頁;
WEB-INF/:當前webapp的私有資源路徑;一般用於存儲當前webapp的web.xml和context.xml配置文件;
META-INF/:相似於WEB-INF/;
classes/:類文件,當前webapp所提供的類;
lib/:類文件,當前webapp所提供的類,被打包爲jar格式;
.war:webapp應用程序;
.jar:EJB的類打包文件(類庫);
.rar:資源適配器類打包文件;
.ear:企業級應用程序;
部署(deploy)webapp的相關操做:
deploy:將webapp的源文件放置於目標目錄(網頁程序文件存放目錄),配置tomcat服務器可以基於web.xml和context.xml文件中定義的路徑來訪問此webapp;將其特有的類和依賴的類經過class loader裝載至JVM;
部署有兩種方式:
自動部署:auto deploy
手動部署:
冷部署:把webapp複製到指定的位置,然後才啓動tomcat;
熱部署:在不中止tomcat的前提下進行部署;
部署工具:manager、ant腳本、tcd(tomcat client deployer)等;
undeploy:拆除(反部署),中止webapp,並從tomcat實例上卸載webapp;
start:啓動處於中止狀態的webapp;
stop:中止webapp,再也不向用戶提供服務;其類依然在jvm上;
redeploy:從新部署;
步驟:
[root@CentOS6 webapps]#mkdir -pv myapp/{lib,classes,WEB-INF,META-INF} mkdir: created directory `myapp' mkdir: created directory `myapp/lib' mkdir: created directory `myapp/classes' mkdir: created directory `myapp/WEB-INF' mkdir: created directory `myapp/META-INF' [root@CentOS6 ~]#tree /var/lib/tomcat/webapps/myapp/ /var/lib/tomcat/webapps/myapp/ ├── classes ├── lib ├── META-INF └── WEB-INF
[root@CentOS6 myapp]#vim index.jsp <%@ page language="java" %> <%@ page import="java.util.*" %> <html> <head> <title>Test Page</title> </head> <body> <% out.println("hello world"); %> </body> </html>
此時用瀏覽器去訪問咱們的部署的程序頁面速度仍是比較快的,由於咱們的頁面比較簡單,編譯過程很簡單。
當咱們使用瀏覽器對以前部署的程序發送訪問請求時,咱們部署的JSP格式的文件首先要被轉換成成.java結尾的servlet文件,以後再編譯成.class結尾的類文件。其編譯結果就在/usr/share/tomcat/work/Catalina/loca
lhost目錄中:
[root@CentOS6 ~]#tree /usr/share/tomcat/work/ /usr/share/tomcat/work/ └── Catalina #引擎Engine └── localhost #虛擬主機名 ├── _ │ └── org │ └── apache │ └── jsp │ ├── index_jsp.class │ └── index_jsp.java ├── docs ├── examples ├── myapp #應用程序 │ └── org │ └── apache │ └── jsp #類名:rog.apache.jsp │ ├── index_jsp.class │ └── index_jsp.java #servlet文件 └── sample #實現JSP格式的程序文件能夠在Java虛擬機中運行所調用的類有多種版本,爲了區分各個類在全局惟一性因此規定全部的常被公共調用的類通常以公司或者組織的域名命名,這裏的類名rog.apache.jsp也遵循次規定。
Java程序在第一次被訪問之後會被編譯成類文件,同時將編譯結果裝載在Java虛擬機上,因此以後的訪問就不用在反覆執行編譯運行過程而是直接從Java虛擬機中加載。
另外Java虛擬機會自動監控程序的源文件是否發生了改變,若是源文件發生改變他會自動的將源文件轉化成servlet文件再編譯成class類文件從新運行源文件。以供用戶訪問。
若是要使用Tomcat自帶的管理應用,就必須下載安裝tomcat的admin工具包。
訪問Tomcat的原始默認頁面,點擊Manger App 會彈出認證窗口。點擊取消會響應一個401錯誤頁面。根據錯誤頁面提示進行相應配置。
給出了四個用戶,分別對應不一樣的權限:
manager-gui - allows access to the HTML GUI and the status pages
同時具備web頁面管理權限以及web頁面服務器狀態訪問權限
manager-script - allows access to the text interface and the status pages
只容許訪問文本接口和web界面的服務器狀態界面
manager-jmx - allows access to the JMX proxy and the status pages
容許訪問服務遠程接口
manager-status - allows access to the status pages only
只能訪問服務狀態信息頁面
點擊Host Manager也會返回401頁面,設定與以前相同
兩個角色:
admin-gui - allows access to the HTML GUI
admin-script - allows access to the text interface
在/etc/tomcat/tomcat-users.xml文件中設定用戶:
[root@CentOS6 ~]#vim /etc/tomcat/tomcat-users.xml <role rolename="manager-gui,admin-gui"/> <user username="tomcat" password="0315" roles="manager-gui"/> #設定用戶角色既屬於manager-gui又屬於admin-gui #設定用戶名tomcat口令0315
Linux Nginx MySQL Tomcat
Client (http) --> nginx (reverse proxy)(http) --> tomcat (http connector)
location / { proxy_pass http://tc1.a.com:8080; } location ~* \.(jsp|do)$ { proxy_pass http://tc1.a.com:8080; }
Linux Apache(httpd) MySQL Tomcat
httpd的代理模塊:
proxy_module
proxy_http_module:適配http協議客戶端;
proxy_ajp_module:適配ajp協議客戶端;
proxy_http_module代理配置示例:
<VirtualHost \*:80> ServerName tc1.a.com ProxyRequests Off ProxyVia On #顯示前端代理服務器信息 ProxyPreserveHost On <Proxy *> Require all granted </Proxy> ProxyPass / http://tc1.a.com:8080/ ProxyPassReverse / http://tc1.a.com:8080/ <Location /> Require all granted </Location> </VirtualHost>
proxy_ajp_module代理配置示例:
<VirtualHost \*:80> ServerName tc1.a.com ProxyRequests Off ProxyVia On ProxyPreserveHost On <Proxy *> Require all granted </Proxy> ProxyPass / ajp://tc1.a.com:8009/ ProxyPassReverse / ajp://tc1.a.com:8009/ <Location /> Require all granted </Location> </VirtualHost>
任何應用程序的集羣會話保持都是在所不免的,Tomcat要想實現集羣話,必然要面對的前提就是會話保持。
Tomcat Cluster(session) session綁定的三種方法:
分佈式場景:
(1) session sticky
會話綁定:對IP、URL或者cookie進行hash
映射法
一致性hash算法
會話綁定的方式缺陷在於當Tomcat集羣中任何一臺Tomcat服務器出現故障那麼保存在這臺服務器的session就會丟失,也就是說基於這種方式實現的會話綁定集羣的全部節點都是一個單點。
(2) session cluster 會話集羣適用範圍有限
將多個tomcat服務器部署爲一個集羣,各個tomcat服務器之間經過單播或者多播的方式將會話信息傳遞給其餘服務器。
會話集羣的方式缺陷在於在傳遞會話信息時會佔用大量的網絡帶寬,另外每臺服務器都保存全部會話信息形成信息冗餘,浪費資源
tomcat delta manager
(3) session server 會話服務器
使用高性能的kv服務器
memcached:將數據緩存在內存中,單點問題容易形成數據丟失
redis:數據存儲在磁盤當中
1、首先在兩臺虛擬主機上部署tomcat服務:
[root@Centos6 ~]#java -version openjdk version "1.8.0_121" OpenJDK Runtime Environment (build 1.8.0_121-b13) OpenJDK 64-Bit Server VM (build 25.121-b13, mixed mode)
[root@Centos6 ~]#tar xf apache-tomcat-8.0.23.tar.gz -C /usr/local/ #解壓包 [root@Centos6 ~]#ln -sv /usr/local/tomcat /usr/local/apache-tomcat-8.0.23/ #建立軟連接 #在原有的engine字段中配置虛擬主機: [root@Centos6 ~]#vim /usr/local/tomcat/conf/server.xml <Host name="web1.huxiaoqi.com" appBase="/app/webapps" autoDeploy="ture"> <Context path="" docBase="ROOT" /> <Valve className="org.apache.catalina.valves.AccessLogValve" directory="/app/logs" prefix="web1_access_log" suffix=".txt" pattern="%h %l %u %t "%r" %s %b" /> </Host> #這裏並將默認訪問host改成新部署的web1.huxiaoqi.com
#配置目錄結構以下: [root@Centos6 ]#tree /app ├── logs └── webapps └── ROOT ├── classes ├── index.jsp ├── lib ├── META-INF └── WEB-INF [root@Centos6 ~]#vim /app/webapps/ROOT/index.jsp <%@ page language="java" %> <%@ page import="java.util.*" %> <html> <head> <title>web1 Page</title> </head> <body> <% out.println("hello world,this is web1"); %> </body> </html> #tomcat程序測試頁面如上
2、 配置前端nginx服務器:
修改nginx配置文件:
upstream tcsrvs { ip_hash; #基於IP會話綁定 server web1.huxiaoqi.com:8080; server web2.huxiaoqi.com:8080; } server { listen 80 default_server; listen [::]:80 default_server; server_name _; root /usr/share/nginx/html; location / { } location ~*\.(jsp|do)$ { #動態請求既以.jsp結尾或.do結尾的發日後端tomcat服務器 proxy_pass http://tcsrvs; } }
3、訪問測試:
能夠先將前端nginx調度器的配置文件中的基於IP會話綁定註釋掉,而後測試訪問。若是訪問效果是兩臺tomcat服務器輪詢調度就說名配置成功。
而後再加上IP會話綁定設定,第一次訪問前端的nginx服務器http服務的動態資源nginx基於IP的會話綁定後就會把該客戶端之後的全部動態請求都固定的調度到某臺tomcat服務器。
基於apache向後端負載均衡tomcat有三種三種方案可選擇,主要之前兩種爲主,基於http協議以及ajp協議,第三種早期的tomcat負載均衡集羣有用到,目前以很少用。
(1)apache:
mod_proxy
mod_proxy_httpd
要實現負載均衡功能還要再用到mod_proxy_balancer模塊
tomcat:
http connector
(2)apache:
mod_proxy
mod_proxy_ajp
一樣要實現負載均衡功能還要再用到mod_proxy_balancer模塊
tomcat:
ajp connector
(3)apache:mod_jk自身既能反帶又能作負載均衡
mod_jk
tomcat:
ajp connector
[root@CentOS6 ~]#vim /etc/httpd/conf.d/virtualhost.conf <proxy balancer://tcsrvs> BalancerMember http://192.168.45.11:8080 loadfactor=10 route=TomcatA BalancerMember http://192.168.45.12:8080 loadfactor=10 route=TomcatB ProxySet lbmethod=byrequests </Proxy> <VirtualHost *:80> ServerName web1.huxiaoqi.com ProxyVia On #顯示代理主機IP信息 ProxyRequests Off #關閉正向代理 ProxyPreserveHost On <Proxy *> Require all granted </Proxy> ProxyPass / balancer://tcsrvs/ ProxyPassReverse / balancer://tcsrvs/ <Location /> Require all granted </Location> </VirtualHost> #注意:若是使用的apache是2.4之前的版本則無須受權訪問,既將全部的Require all granted字段刪掉
關於如上apache指令的說明:
ProxyPreserveHost {On|Off}:若是啓用此功能,代理會將用戶請求報文中的Host:行發送給後端的服務器,而再也不使用ProxyPass指定的服務器地址。若是想在反向代理中支持虛擬主機,則須要開啓此項,不然就無需打開此功能。
ProxyVia {On|Off|Full|Block}:用於控制在http首部是否使用Via:,主要用於在多級代理中控制代理請求的流向。默認爲Off,即不啓用此功能;On表示每一個請求和響應報文均添加Via:;Full表示每一個Via:行都會添加當前apache服務器的版本號信息;Block表示每一個代理請求報文中的Via:都會被移除。
ProxyRequests {On|Off}:是否開啓apache正向代理的功能;啓用此項時爲了代理http協議必須啓用mod_proxy_http模塊。同時,若是爲apache設置了ProxyPass,則必須將ProxyRequests設置爲Off。
ProxyPass [path] !|url [key=value key=value ...]]:將後端服務器某URL與當前服務器的某虛擬路徑關聯起來做爲提供服務的路徑,path爲當前服務器上的某虛擬路徑,url爲後端服務器上某URL路徑。使用此指令時必須將ProxyRequests的值設置爲Off。須要注意的是,若是path以「/」結尾,則對應的url也必須以「/」結尾,反之亦然。
另外,mod_proxy模塊在httpd 2.1的版本以後支持與後端服務器的鏈接池功能,鏈接在按需建立在能夠保存至鏈接池中以備進一步使用。鏈接池大小或其它設定能夠經過在ProxyPass中使用key=value的方式定義。經常使用的key以下所示:
◇ min:鏈接池的最小容量,此值與實際鏈接個數無關,僅表示鏈接池最小要初始化的空間大小。
◇ max:鏈接池的最大容量,每一個MPM都有本身獨立的容量;都值與MPM自己有關,如Prefork的老是爲1,而其它的則取決於ThreadsPerChild指令的值。
◇ loadfactor:用於負載均衡集羣配置中,定義對應後端服務器的權重,取值範圍爲1-100。
◇ retry:當apache將請求發送至後端服務器獲得錯誤響應時等待多長時間之後再重試。單位是秒鐘。
若是Proxy指定是以balancer://開頭,即用於負載均衡集羣時,其還能夠接受一些特殊的參數,以下所示:
◇lbmethod:apache實現負載均衡的調度方法,默認是byrequests,即基於權重將統計請求個數進行調度,bytraffic則執行基於權重的流量計數調度,bybusyness經過考量每一個後端服務器的當前負載進行調度。
◇ maxattempts:放棄請求以前實現故障轉移的次數,默認爲1,其最大值不該該大於總的節點數。
◇ nofailover:取值爲On或Off,設置爲On時表示後端服務器故障時,用戶的session將損壞;所以,在後端服務器不支持session複製時可將其設置爲On。
◇ stickysession:調度器的sticky session的名字,根據web程序語言的不一樣,其值爲JSESSIONID或PHPSESSIONID。
[root@Centos6 ~]#vim /usr/local/tomcat/conf/server.xml <Engine name="Catalina" defaultHost="web1.huxiaoqi.com" jvmRoute="TomcatA"> #在engine組件中添加jvmroute字段,把兩個tomcat節點加以區分
[root@Centos6 ~]#vim /app/webapps/ROOT/index.jsp <%@ page language="java" %> <html> <head><title>TomcatA</title></head> <body> <h1><font color="red">TomcatA.huxiaoqi.com</font></h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("huxiaoqi.com","huxiaoqi.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html> #注意兩臺tomcat節點要有區分
Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED <proxy balancer://tcsrvs> BalancerMember http://192.168.45.11:8080 route=TomcatA loadfactor=10 BalancerMember http://192.168.45.12:8080 route=TomcatB loadfactor=10 ProxySet lbmethod=byrequests ProxySet stickysession=ROUTEID </Proxy> #在虛擬主機配置文件中定義報文頭部添加ROUTEID信息。 #在proxy字段中定義根據報文頭部的routeID實現會話綁定
實現基於ajp協議apache前端調度tomcat方法與http協議的方法大致相同:
[root@CentOS6 ~]#vim /etc/httpd/conf.d/virtualhost.conf <proxy balancer://tcsrvs> BalancerMember ajp://192.168.45.11:8009 loadfactor=10 route=TomcatA BalancerMember ajp://192.168.45.12:8009 loadfactor=10 route=TomcatB ProxySet lbmethod=byrequests </Proxy> #只需將協議修改成ajp,監聽端口修改成8009便可
ajp協議實現會話綁定:
[root@CentOS6 ~]#vim /etc/httpd/conf.d/virtualhost.conf #Header add Set-Cookie "ROUTEID=.%{BALANCER_WORKER_ROUTE}e; path=/" env=BALANCER_ROUTE_CHANGED <proxy balancer://tcsrvs> BalancerMember ajp://192.168.45.11:8009 loadfactor=10 route=TomcatA BalancerMember ajp://192.168.45.12:8009 loadfactor=10 route=TomcatB # ProxySet lbmethod=byrequests ProxySet stickysession=ROUTEID </Proxy> #只需定義ProxySet stickysession=ROUTEID便可
apache balancer模塊中有關於集羣管理接口,用過設置能夠實現基於web界面直接對集羣進行管理
啓用管理接口:
<Location /balancer-manager> SetHandler balancer-manager ProxyPass ! Require all granted </Location>
此種方式依然淘汰不作討論
tomcat內部存在組件session manager用來自動實現本地tomcat集羣的會話管理,主要實現方式共有四種:
StandarManager :標準會話管理器
PersistenManager:持久會話管理器
DeltaManager:
BackupManager:
配置方法:
<Manager className="org.apache.catalina.session.StandardManager"maxInactiveInterval="7200"/> #在HOST組件中配置就對該主機全部應用程序有效若是隻針對某個應用程序生效則應設置在context組件中。 #maxActiveSessions:最多容許的活動會話數量,默認爲-1,表示不限制; #maxInactiveInterval:非活動的會話超時時長,默認爲60s; #pathname:會話文件的保存目錄;
StandarManager,標準會話管理器也能夠理解爲簡單的會話管理器,該種方法僅僅實現tomcat具備會話保持的功能,可是不管是對與會話信息的連續保存,以及會話保持的高可用性都沒法有效解決。
標準會話管理器的工做原理是週期性的將訪問該臺主機的用戶會話信息保存在本地文件系統中,默認保存於$CATALINA_HOME/work/Catalina/
將會話數據保存至持久存儲中,而且能在服務器意外停止後從新啓動時從新加載這些會話信息。持久會話管理器支持將會話保存至文件存儲(FileStore)或JDBC存儲(JDBCStore)中。
該種方法雖然能夠實現將用戶會話信息永久存儲在一個遠程的公用存儲之上,可是這些會話信息是不能共享的。也就是說持久會話管理器中,後端的每臺Tomcat服務器都將訪問該臺服務器的會話信息存儲在本地或者遠程的集中存儲區間。可是每臺服務器須要去讀取這些會話信息時只能去讀取本身存儲的會話信息,而不能讀取其餘主機存儲的會話信息。這樣一來若是後端tomcat集羣中某臺服務器出現故障,仍是沒法實現高可用性效果。
該種方法邏輯簡單,對於小型的tomcat服務集羣具備可用性。
保存至文件中的示例:
<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true"> <Store className="org.apache.catalina.session.FileStore" directory="/data/tomcat-sessions"/> </Manager>
每一個用戶的會話會被保存至directory指定的目錄中的文件中,文件名爲
保存至JDBCStore中的示例:
<Manager className="org.apache.catalina.session.PersistentManager" saveOnRestart="true"> <Store className="org.apache.catalina.session.JDBCStore" driverName="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/mydb?user=jb;password=pw"/> </Manager>
該方法在以上兩種會話管理理念的基礎之上有所改進,在備份會話管理中後端的Tomcat集羣服務器沒臺服務器都有一臺備用機器,同時同步會話信息以提升會話管理高可用性。可是該方法邏輯較爲複雜極難實現標準化管理。
將後端的多個Tomcat服務器節點經過特殊的信道以多播的方式傳遞信號創建成一個Tomcat服務集羣,該方法也是Tomcat session cluster最經常使用的實現方法。
在這種會話管理的方法中,後端的全部Tomcat服務器收集到訪問本機的客戶端會話信息之後都會經過一個專門的信道經過多播的方式發送給集羣中其餘主機,集羣中的其餘主機也會共同監聽同一個多播端口,收集全部主機發來的會話信息並保存。
該方法雖然實現了會話管理的高可用性,可是當集羣過於龐大時會話信息的傳播將帶來巨大的網絡壓力,同時每臺主機都保存全部會話信息也浪費了資源。
該方法配置方法可參考https://tomcat.apache.org/tomcat-8.0-doc/cluster-howto.html#For_the_impatient
注意不一樣版本的Tomcat配置寫法不一樣
前提:
兩個tomcat節點:172.16.100.7(tomcatA.a.com),172.16.100.8(tomcatB.a.com)
兩個memcached節點:172.16.100.9, 172.16.100.10
一個負載均衡節點:172.16.100.6
Clients-->172.16.100.6-->(tomcatA, tomcatB)
memcached-session-manager項目地址,http://code.google.com/p/memcached-session-manager/, https://github.com/magro/memcached-session-manager
下載以下jar文件至各tomcat節點的tomcat安裝目錄下的lib目錄中,其中的${version}要換成你所須要的版本號,tc${6,7,8}要換成與tomcat版本相同的版本號。
memcached-session-manager-${version}.jar
memcached-session-manager-tc${6,7,8}-${version}.jar
spymemcached-${version}.jar
msm-javolution-serializer-${version}.jar
javolution-${version}.jar
分別在兩個tomcat上的某host上定義一個用於測試的context容器,並在其中建立一個會話管理器,以下所示:
<Context path="/test" docBase="/usr/local/tomcat/webapps/test" reloadable="true"> <Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" memcachedNodes="n1:172.16.100.9:11211,n2:172.16.100.10:11211" failoverNodes="n1" requestUriIgnorePattern=".*\.(ico|png|gif|jpg|css|js)$" transcoderFactoryClass="de.javakaffee.web.msm.serializer.javolution.JavolutionTranscoderFactory" /> </Context>
分別爲兩個context提供測試頁面:
tomcatA:
# mkdir -pv /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
# vim /usr/local/tomcat/webapps/test/index.jsp
添加以下內容:
<%@ page language="java" %> <html> <head><title>TomcatA</title></head> <body> <h1><font color="red">TomcatA.magedu.com</font></h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("magedu.com","magedu.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
tomcatB:
# mkdir -pv /usr/local/tomcat/webapps/test/WEB-INF/{classes,lib}
# vim /usr/local/tomcat/webapps/test/index.jsp
添加以下內容:
<%@ page language="java" %> <html> <head><title>TomcatB</title></head> <body> <h1><font color="blue">TomcatB.magedu.com</font></h1> <table align="centre" border="1"> <tr> <td>Session ID</td> <% session.setAttribute("magedu.com","magedu.com"); %> <td><%= session.getId() %></td> </tr> <tr> <td>Created on</td> <td><%= session.getCreationTime() %></td> </tr> </table> </body> </html>
在172.16.100.6上配置反向代理的負載均衡內容,相似以下所示:
<Proxy balancer://tomcat> BalancerMember http://172.16.100.7:8080 loadfactor=1 BalancerMember http://172.16.100.8:8080 loadfactor=1 ProxySet lbmethod=byrequests </Proxy> ProxyVia Off ProxyRequests Off ProxyPass / balancer://tomcat/ ProxyPassReverse / balancer://tomcat/ <Proxy *> Order Allow,Deny Allow From all </Proxy> <Location /> Order Allow,Deny Allow From all </Location>
測試結果,在瀏覽器中訪問http://172.16.100.6/test,結果以下所示,其session ID在負載均衡環境中保持不變。
TomcatA.a.com
Session ID 4DD0340CE6294FF2BBE802CD4CD039EC-n2
Created on 1399890838103
TomcatB.magedu.com
Session ID 4DD0340CE6294FF2BBE802CD4CD039EC-n2 Created on 1399890838103