第一週
1.在虛擬機器安裝tomcat並且部署一個服務,並且實現會話共享。
如何能在叢集中的多個節點之間保持資料的一致性,會話(Session)資訊是這些資料中最重要的一塊。要實現這一點, 大體上有兩種方式:
一種是把所有Session資料放到一臺伺服器上或者資料庫中,叢集中的所有節點透過訪問這臺Session伺服器來獲取資料;
另一種就是在叢集中的所有節點間進行Session資料的同步複製,任何一個節點均儲存了所有的Session資料。
note:其他方案 Tomcat叢集session同步方案有以下幾種方式:
1)使用tomcat自帶的cluster方式,多個tomcat間自動實時複製session資訊,配置起來很簡單。但這個方案的效率比較低,在大併發下表現並不好。
2)利用nginx的基於訪問ip的hash路由策略,保證訪問的ip始終被路由到同一個tomcat上,這個配置更簡單。每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個後端伺服器,可以解決session(並不是共享session解決)的問題! 並且如果應用是某一個區域網大量使用者同時登入,這樣負載均衡就沒什麼作用了。
3)利用nginx外掛實現tomcat叢集和session同步,nginx-upstream-jvm-route是一個Nginx的擴充套件模組,用來實現基於Cookie的Session Sticky的功能。但是遺憾的是,這個模組的補丁在nginx1.4版本之後就沒有再更新了,所以nginx1.4之後版本跟該模組就不相容了!!
4)利用memcached實現(MSM工具)。memcached儲存session,並把多個tomcat的session集中管理,前端在利用nginx負載均衡和動靜態資源分離,在兼顧系統水平擴充套件的同時又能保證較高的效能。即透過MSM工具把Tomcat的Session序列化後儲存到Memcached裡面,從而實現Session共享.
5)利用redis實現。使用redis不僅僅可以將快取的session持久化,還因為它支援的單個物件比較大,而且資料型別豐富,不只是快取 session,還可以做其他用途,可以一舉幾得。Redis這種方式目前還不支援Tomcat8環境(現在網上外掛不支援tomcat8,非要支援tomcat8,則需修改外掛jar包的原始碼)!
6)利用filter方法實現。這種方法比較推薦,因為它的伺服器使用範圍比較多,不僅限於tomcat ,而且實現的原理比較簡單容易控制。
7)利用terracotta伺服器共享session。這種方式配置比較複雜。
session是伺服器端快取,cookie是客戶端快取。
MSM是一個高可用的Tomcat Session共享解決方案,除了可以從本機記憶體快速讀取Session資訊(僅針對黏性Session)外,還可使用Memcached存取Session,以實現高可用。
傳統tomcat叢集,會話複製隨著結點數增多,擴充套件性成為瓶頸。MSM使用memcached完成統一管理tomcat會話,避免tomcat結點間過多會話複製。
MSM利用Value(Tomcat 閥)對Request進行跟蹤。Request請求到來時,從memcached載入session,Request請求結束時,將tomcat session更新至memcached,以達到session共享之目的, 支援sticky和non-sticky模式:
sticky : 會話粘連模式(黏性session)。客戶端在一臺tomcat例項上完成登入後,以後的請求均會根據IP直接繫結到該tomcat例項。
no-sticky:會話非粘連模式(非粘性session)。客戶端的請求是隨機分發,多臺tomcat例項均會收到請求
MSM工作原理:
a)Sticky Session(黏性) 模式下的工作原理:
Tomcat本地Session為主Session,Memcached 中的Session為備Session。Request請求到來時, 從memcached載入備 Session到 tomcat (僅當tomcat jvmroute發生變化時, 否則直接取Tomcat Session);Request請求結束時,將Tomcat Session更新至memcached,以達到主備同步之目的。 安裝在Tomcat上的MSM使用本機記憶體儲存Session,當一個請求執行完畢之後,如果對應的Session在本地不存在(即某使用者的第一次請求),則將該Session複製一份至Memcached;當該Session的下一個請求到達時,會使用Tomcat的本地Session,請求處理結束之後,Session的變化會同步更新到 Memcached,保證資料一致。當叢集中的一個Tomcat掛掉,下一次請求會被路由到其他Tomcat上。負責處理此此請求的Tomcat並不清楚Session資訊,於是從Memcached查詢該Session,更新該Session並將其儲存至本機。此次請求結束,Session被修改,送回Memcached備份。
b)Non-sticky Session (非黏性)模式下的工作原理(記住:多臺tomcat叢集或多個tomcat例項時需要選擇Non-Sticky模式,即sticky="false"):
Tomcat本地Session為中轉Session,Memcached1為主Session,Memcached2為備Session。Request請求到來時,從Memcached2載入備Session到tomcat,(當容器中還是沒有Session 則從Memcached1載入主Session到tomcat,這種情況是隻有一個memcached節點,或者有Memcached1 出錯時),Request請求結束時,將Tomcat Session更新至主Memcached1和備memcached2,並且清除Tomcat Session 。以達到主備同步之目的。 多臺tomcat叢集時 需要選擇Non-Sticky模式,即sticky="false"
======================================================================================================
Tomcat8+Memcached+Nginx實現session會話共享的操作記錄:
1)基礎環境
ip 主機名 應用 埠
192.168.10.200 Nginx-node nginx1.12.2 80
192.168.10.201 Tomcat-node1 java8.131、tomcat8.0.53 8080
192.168.10.202 Tomcat-node2 java8.131、tomcat8.0.53 8080
192.168.10.203 Mem-node1 memcached-1.4.34 11211
192.168.10.205 Mem-node2 memcached-1.4.34 11211
jsp檔案
編寫一個測試頁面(直接修改index.jsp檔案):
[root@Tomcat-node1 ~]# cat /usr/local/tomcat8/webapps/ROOT/index.jsp
192.168.10.201
port:8080
this is Tomcat-node1 !
<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false"%>
SessionID:<%=session.getId()%>
SessionIP:<%=request.getServerName()%>
SessionPort:<%=request.getServerPort()%>
<% out.println("This is Tomcat server 201 !"); %>
另一臺tomcat的測試頁面為:
[root@Tomcat-node2 ~]# cat /usr/local/tomcat8/webapps/ROOT/index.jsp
192.168.10.202
port:8080
this is Tomcat-node2!
<%@ page contentType="text/html;charset=UTF-8" isELIgnored="false"%>
SessionID:<%=session.getId()%>
SessionIP:<%=request.getServerName()%>
SessionPort:<%=request.getServerPort()%>
<% out.println("This is Tomcat server 202 !"); %>
nginx反向代理到後端伺服器配置檔案
[root@Nginx-node vhosts]# vim lb_tomcat.conf
upstream tomcat-lb {
server 192.168.10.201:8080;
server 192.168.10.202:8080;
}
server {
listen 80;
server_name www.kevin.com;
location / {
proxy_pass http://tomcat-lb;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ .*\.(gif|jpg|png|htm|html|css|ico|flv|swf)(.*) {
proxy_pass http://tomcat-lb;
proxy_redirect off;
proxy_set_header Host $host;
proxy_cache cache_one;
proxy_cache_valid 200 302 1h;
proxy_cache_valid 301 1d;
proxy_cache_valid any 10m;
expires 30d;
proxy_cache_key $host$uri$is_args$args;
}
}
如上,在配置memcached-session-manager會話共享之前,訪問http://www.kevin.com的請求會輪詢負載到tomcat-node1和tomcat-node2兩個節點上,並且session id會隨著頁面的重新整理而改變,即此時還沒有實現session會話共享!!
安裝Memcached
啟動memcached,埠11211可以根據自己需要修改不同埠
[root@mem-node1 ~]# /usr/local/memcached/bin/memcached -d -m 512 -u root -p 11211 -c 1024 -P /var/lib/memcached.11211pid
配置Tomcat,透過MSM實現共享session
[root@Tomcat-node1 MSM_Software]# ll
total 1212
-rw-rw-r--. 1 root root 53259 Aug 27 09:53 asm-5.2.jar
-rw-rw-r--. 1 root root 323740 Aug 27 09:51 kryo-4.0.0.jar
-rw-rw-r--. 1 root root 85217 Aug 27 09:51 kryo-serializers-0.38.jar
-rw-rw-r--. 1 root root 152401 Aug 27 09:49 memcached-session-manager-1.9.7.jar
-rw-rw-r--. 1 root root 10788 Aug 27 09:49 memcached-session-manager-tc8-1.9.7.jar
-rw-rw-r--. 1 root root 5711 Aug 27 09:52 minlog-1.3.0.jar
-rw-rw-r--. 1 root root 37160 Aug 27 09:51 msm-kryo-serializer-1.9.7.jar
-rw-rw-r--. 1 root root 51287 Aug 27 09:53 objenesis-2.4.jar
-rw-rw-r--. 1 root root 20883 Aug 27 09:52 reflectasm-1.11.3.jar
-rw-rw-r--. 1 root root 472838 Aug 27 09:50 spymemcached-2.12.2.jar
需要把上面這些MSM依賴的jar包下載後全部上傳到兩臺機器的tomcat安裝路徑的lib/ 目錄下
[root@Tomcat-node1 MSM_Software]# cp -rf /usr/local/src/MSM_Software/* /usr/local/tomcat8/lib/
在No-Stick模式和Stick模式下context.xml檔案配置也有所不同(一般用的是No-Stick模式)
只需要修改conf/context.xml檔案:
[root@Tomcat-node1 ~]# cd /usr/local/tomcat8/conf/
[root@Tomcat-node1 conf]# cp context.xml context.xml.bak
a)No-Stick模式
記住:多個tomcat例項時 需要選擇Non-Sticky模式,即sticky="false"
[root@Tomcat-node1 conf]# vim context.xml
在和 之間新增下面內容.就在底部之前新增就行
.......
第一臺tomcat節點的context.xml配置好之後,再將該檔案複製到另一臺tomcat節點的相同路徑下
b) Stick模式。
故障轉移配置節點(failoverNodes),不能使用在Non-Sticky模式,多個使用空格或逗號分開,配置某個節點為備份節點。
當其他節點都不可用時才會儲存到備份節點,適用於sticky模式(即一臺tomcat,多臺memcached)。
[root@Tomcat-node1 conf]# vim context.xml
......
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager"
memcachedNodes="n1:192.168.10.203:11211,n2:192.168.10.205:11211" #多個memcached之間用空格或逗號隔開都可以的
sticky="true"
failoverNodes="n2"
requestUriIgnorePattern=".*.(png|gif|jpg|css|js|swf|flv)$"
transcoderFactoryClass="de.javakaffee.web.msm.serializer.kryo.KryoTranscoderFactory"
copyCollectionsForSerialization="true"
/>
第一臺tomcat節點的context.xml配置好之後,再將該檔案複製到另一臺tomcat節點的相同路徑下,並將failoverNodes後面的引數改為n1
配置好之後,一定要記得重啟兩臺機器的tomcat服務!
[root@Tomcat-node1 ~]# /usr/local/tomcat8/bin/shutdown.sh #或者直接使用kill殺死
[root@Tomcat-node1 ~]# lsof -i:8080
[root@Tomcat-node1 ~]# /usr/local/tomcat8/bin/startup.sh
======================================================================================
Manager 各引數說明:
memcachedNodes 必選項,memcached的節點資訊,多個memcached節點,中間需要使用空格
failoverNodes="n2" 表示當前session保持到n1的memcached節點上
failoverNodes 可選項,不能使用在non-sticky sessions模式。故障轉移配置節點,多個使用空格或逗號分開,配置某個節點為備份節點,
當其他節點都不可用時才會儲存到備份節點,官方建議配置為和tomcat同伺服器的節點。
理由如下:
假如有兩臺伺服器m1,m2,其中m1部署tomcat和memcached節點n1,m2部署memcached節點n2。
如果配置tomcat的failoverNodes值為n2或者不配置,則當伺服器m1掛掉後n1和tomcat中儲存的session會丟失,而n2中未儲存或者只儲存了部分session,
這就造成 部分使用者狀態丟失。
如果配置tomcat的failoverNodes值為n1,則當m1掛掉後因為n2中儲存了所有的session,所以重啟tomcat的時候使用者狀態不會丟失。
為什麼n2中儲存了所有的session? 因為failoverNodes配置的值是n1,只有當n2節點不可用時才會把session儲存到n1,所以這個時候n1中是沒有儲存任何session的。
lockingMode 可選值,預設none,只對non-sticky有效。
requestUriIgnorePattern 可選值,制定忽略那些請求的session操作,一般制定靜態資源如css,js一類的。
sessionBackupAsync 可選值,預設true,是否非同步的方式儲存到memcached。
sessionBackupTimeout 可選項,預設100毫秒,非同步儲存session的超時時間。
2.總結JVM記憶體結構和垃圾回收演算法。
JVM 組成
JVM 組成部分
類載入子系統: 使用Java語言編寫.java Source Code檔案,透過javac編譯成.class Byte Code文
件。class loader類載入器將所需所有類載入到記憶體,必要時將類例項化成例項
執行時資料區: 最消耗記憶體的空間,需要最佳化
執行引擎: 包括JIT (JustInTimeCompiler)即時編譯器, GC垃圾回收器
本地方法介面: 將本地方法棧透過JNI(Java Native Interface)呼叫Native Method Libraries, 比
如:C,C++庫等,擴充套件Java功能,融合不同的程式語言為Java所用
JVM 記憶體常用相關引數
垃圾回收基本演算法
1 標記-清除 Mark-Sweep
分垃圾標記階段和記憶體釋放兩個階段。
標記階段,找到所有可訪問物件打個標記。清理階段,遍歷整個堆
對未標記物件(即不再使用的物件)逐一進行清理。
特點:
優點:演算法簡單
缺點:標記-清除最大的問題會造成記憶體碎片,但是不浪費空間,效率較高(如果物件較多時,逐一刪除效率也
會受到影響)
2 標記-壓縮 (壓實)Mark-Compact
分垃圾標記階段和記憶體整理兩個階段。
標記階段,找到所有可訪問物件打個標記。
記憶體清理階段時,整理時將物件向記憶體一端移動,整理後存活物件連續的集中在記憶體一端。
特點:
標記-壓縮演算法好處是整理後記憶體空間連續分配,有大段的連續記憶體可分配,沒有記憶體碎片。
缺點是記憶體整理過程有消耗,效率相對低下
3 複製 Copying
先將可用記憶體分為大小相同兩塊區域A和B,每次只用其中一塊,比如A。當A用完後,則將A中存活的對
象複製到B。複製到B的時候連續的使用記憶體,最後將A一次性清除乾淨。
特點
好處是沒有碎片,複製過程中保證物件使用連續空間,且一次性清除所有垃圾,所以即使物件很多,收回效
率也很高
缺點是比較浪費記憶體,只能使用原來一半記憶體,因為記憶體對半劃分了,複製過程畢竟也是有代價
4 多種演算法總結
沒有最好的演算法,在不同場景選擇最合適的演算法
效率: 複製演算法>標記清除演算法> 標記壓縮演算法
記憶體整齊度: 複製演算法=標記壓縮演算法> 標記清除演算法
記憶體利用率: 標記壓縮演算法=標記清除演算法>複製演算法
5 STW
對於大多數垃圾回收演算法而言,GC執行緒工作時,停止所有工作的執行緒,稱為Stop The World。GC 完成
時,恢復其他工作執行緒執行。這也是JVM執行中最頭疼的問題
3.總結安裝Nexus步驟實現私有倉庫
Nexus 是一個強大的 Maven 和其它倉庫的管理器,它極大地簡化了自己內部倉庫的維護和外部倉庫的訪問。
Nexus私服有很多好處,例如:
- 加速構建;
- 節省頻寬;
- 節省中央Maven倉庫的頻寬;
- 穩定(應付一旦中央伺服器出問題的情況);
- 控制和審計;
- 能夠部署第三方構件;
- 可以建立本地內部倉庫;
- 可以建立公共倉庫。
https://help.sonatype.com/repomanager3/download/download-archives---repository-manager-3
部署
安裝Nexus 建議記憶體至少4G以上
安裝JDK
[root@ubuntu1804 ~]#apt install openjdk-8-jdk -y
[root@ubuntu1804 ~]#java -version
openjdk version "1.8.0_282"
OpenJDK Runtime Environment (build 1.8.0_282-8u282-b08-0ubuntu1~18.04-b08)
OpenJDK 64-Bit Server VM (build 25.282-b08, mixed mode)
上網下載
[root@ubuntu1804 ~]#wget https://download.sonatype.com/nexus/3/nexus-3.29.2-02-
unix.tar.gz
[root@ubuntu1804 ~]#ll -h nexus-3.29.2-02-unix.tar.gz
-rw-r--r-- 1 root root 155M Feb 18 17:09 nexus-3.29.2-02-unix.tar.gz
[root@ubuntu1804 ~]#tar xf nexus-3.29.2-02-unix.tar.gz -C /usr/local/
[root@ubuntu1804 ~]#ln -s /usr/local/nexus-3.29.2-02/ /usr/local/nexus
[root@ubuntu1804 ~]#ln -s /usr/local/nexus/bin/nexus /usr/bin/
[root@ubuntu1804 ~]#file /usr/local/nexus/bin/nexus
bin/nexus: POSIX shell script, ASCII text executable, with very long lines
指定執行身份
[root@ubuntu1804 ~]#vim /usr/local/nexus/bin/nexus.rc
run_as_user="root"
檢視配置檔案,可以在此檔案中修改埠等配置
[root@ubuntu1804 ~]#cat /usr/local/nexus/etc/nexus-default.properties
DO NOT EDIT - CUSTOMIZATIONS BELONG IN $data-dir/etc/nexus.properties
Jetty section
application-port=8081
application-host=0.0.0.0
nexus-args=${jetty.etc}/jetty.xml,${jetty.etc}/jetty-
http.xml,${jetty.etc}/jetty-requestlog.xml
nexus-context-path=/
Nexus section
nexus-edition=nexus-pro-edition
nexus-features=
nexus-pro-feature
nexus.hazelcast.discovery.isEnabled=true
檢視JVM配置檔案
[root@ubuntu1804 ~]#cat /usr/local/nexus/bin/nexus.vmoptions
-Xms2703m
-Xmx2703m
......
前臺執行
[root@ubuntu1804 ~]#nexus run
WARNING: ************************************************************
WARNING: Detected execution as "root" user. This is NOT recommended!
WARNING: ************************************************************
.....
2021-02-18 17:20:21,580+0800 INFO [jetty-main-1] *SYSTEM
org.eclipse.jetty.server.Server - Started @37755ms
2021-02-18 17:20:21,581+0800 INFO [jetty-main-1] *SYSTEM
org.sonatype.nexus.bootstrap.jetty.JettyServer -
Started Sonatype Nexus OSS 3.29.2-02
注意: 如果記憶體太小,可能會出現下面提示錯誤
[root@ubuntu1804 ~]#nexus run
WARNING: ************************************************************
WARNING: Detected execution as "root" user. This is NOT recommended!
WARNING: ************************************************************
OpenJDK 64-Bit Server VM warning: INFO: os::commit_memory(0x0000000717000000,
1890582528, 0) failed; error='Cannot allocate memory' (errno=12)
There is insufficient memory for the Java Runtime Environment to continue.
Native memory allocation (mmap) failed to map 1890582528 bytes for committing
reserved memory.
An error report file with more information is saved as:
/usr/local/nexus-3.29.2-02/hs_err_pid7285.log
後臺執行
[root@ubuntu1804 ~]#nexus start
檢視狀態
[root@ubuntu1804 ~]#nexus status
停止服務
[root@ubuntu1804 ~]#nexus stop
檢視埠
[root@ubuntu1804 ~]#ss -ntlp|grep java
LISTEN 0 50 0.0.0.0:8081 0.0.0.0:* users:(("java",pid=20564,fd=798))
LISTEN 0 1 127.0.0.1:38147 0.0.0.0:* users:(("java",pid=20564,fd=125))
建立service檔案
參考連結https://help.sonatype.com/repomanager3/installation/system-requirements
[root@ubuntu1804 ~]#cat /lib/systemd/system/nexus.service
[Unit]
Description=nexus service
After=network.target
[Service]
Type=forking
LimitNOFILE=65536
ExecStart=/usr/local/nexus/bin/nexus start
ExecStop=/usr/local/nexus/bin/nexus stop
User=root
User=nexus
Restart=on-abort
[Install]
WantedBy=multi-user.target
[root@ubuntu1804 ~]#systemctl daemon-reload
[root@ubuntu1804 ~]#systemctl enable --now nexus.service
[root@ubuntu1804 ~]#tail /var/log/syslog
Aug 4 12:20:23 ubuntu1804 systemd[1]: Starting nexus service...
Aug 4 12:20:23 ubuntu1804 nexus[9534]: WARNING:
Aug 4 12:20:23 ubuntu1804 nexus[9534]: WARNING: Detected execution as "root"
user. This is NOT recommended!
Aug 4 12:20:23 ubuntu1804 nexus[9534]: WARNING:
Aug 4 12:20:23 ubuntu1804 nexus[9534]: Starting nexus
Aug 4 12:20:23 ubuntu1804 systemd[1]: Started nexus service.
Aug 4 12:21:58 ubuntu1804 systemd[1]: Reloading.
登入 Web 介面初始化
http://nexus主機:8081/
檢視預設密碼
[root@ubuntu1804 ~]#cat /usr/local/sonatype-work/nexus3/admin.password
f88248fc-2589-44da-af76-4b01855591b8
驗證預設倉庫:預設倉庫有以下 type 型別
Hosted:本地倉庫,通常我們會部署自己的構件到這一型別的倉庫,比如公司的第三方庫
Proxy:代理倉庫,它們被用來代理遠端的公共倉庫,如maven 中央倉庫(官方倉庫)
Group:倉庫組,用來合併多個 hosted/proxy 倉庫,當你的專案希望在多個repository 使用資源時就
不需要多次引用了,只需要引用一個 group 即可
預設倉庫maven-central使用國外倉庫地址,可修改為如下的國內映象地址進行加速
http://maven.aliyun.com/nexus/content/groups/public
在Maven配置檔案指定Nexus伺服器地址,後續進行JAVA原始碼編譯可以從Nexus倉庫下載相關包
[root@ubuntu2204 ~]#vim /etc/maven/settings.xml
......
後續的編譯就會從nexus伺服器下載相關包
[root@ubuntu2204 spring-boot-helloWorld]#mv package
.....
Downloading from nexus-m51: http://10.0.0.200:8081/repository/maven-
central/com/google/inject/guice/4.0/guice-4.0.pom
Downloaded from nexus-m51: http://10.0.0.200:8081/repository/maven-
central/com/google/inject/guice/4.0/guice-4.0.pom (0 B at 0 B/s)
Downloading from nexus-m51: http://10.0.0.200:8081/repository/maven-
central/com/google/inject/guice-parent/4.0/guice-parent-4.0.pom
.....