第一周 架构

meishijia8 / 2024-04-20 / 原文

第一周

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

<%= request.getSession().getId() %>

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

<%= request.getSession().getId() %>

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私服有很多好处,例如:

  1. 加速构建;
  2. 节省带宽;
  3. 节省中央Maven仓库的带宽;
  4. 稳定(应付一旦中央服务器出问题的情况);
  5. 控制和审计;
  6. 能够部署第三方构件;
  7. 可以建立本地内部仓库;
  8. 可以建立公共仓库。

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
......

my-nexus
*
My-Nexus
http://nexus.wang.org:8081/repository/maven-central/

后续的编译就会从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
.....

可以看到下面的显示出下载的相关包